diff --git a/.gitignore b/.gitignore index 6ef247d..92b7c10 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,157 @@ *.o -windows/putty.map -windows/make__quickrun.sh -windows/make__quickrun2.sh -windows/putty.log +*.pyc +.dirstamp +.deps +.DS_Store +/*.pdb +/*.ilk +/*.res +/*.RES +/*.pch +/*.rsp +/*.obj +/*.exe +/*.ncb +/*.plg +/*.dsw +/*.opt +/*.dsp +/*.tds +/*.td2 +/*.map +/Makefile.mgw +/Makefile.vc +/Makefile.lcc +/MSVC +/*.log +/*.GID +/local +/Output +/pageant +/plink +/pscp +/psftp +/putty +/puttytel +/puttygen +/pterm +/puttyapp +/ptermapp +/psusan +/osxlaunch +/uppity +/psocks +/unix/PuTTY.app +/unix/Pterm.app +/fuzzterm +/testcrypt +/testsc +/testzlib +/cgtest +/scctest +/*.DSA +/*.RSA +/*.cnt +/*.hlp +/.bmake +/build.log +/build.out +/uxconfig.h +/empty.h +/config.status +/Makefile.am +/Makefile.in +/Makefile +/compile +/config.status +/configure +/stamp-h1 +/aclocal.m4 +/ar-lib +/autom4te.cache +/depcomp +/install-sh +/local +/missing +/uxconfig.in +/uxconfig.in~ +/uxconfig.h +/licence.h +/*.a +/charset/sbcsdat.c +/contrib/cygtermd/cygtermd.exe +/doc/*.html +/doc/*.txt +/doc/*.cnt +/doc/*.hlp +/doc/*.gid +/doc/*.GID +/doc/*.chm +/doc/*.log +/doc/*.1 +/doc/*.info +/doc/vstr.but +/doc/*.hhp +/doc/*.hhc +/doc/*.hhk +/doc/licence.but +/doc/copy.but +/icons/*.pam +/icons/*.png +/icons/*.ico +/icons/*.icns +/icons/*.xpm +/icons/*.c +/unix/Makefile.gtk +/unix/Makefile.ux +/unix/Makefile.local +/unix/empty.h +/unix/plink +/unix/pterm +/unix/putty +/unix/puttytel +/unix/psftp +/unix/pscp +/unix/puttygen +/unix/stamp-h1 +/unix/*.log +/unix/.deps +/windows/*.pdb +/windows/*.ilk +/windows/*.res +/windows/*.RES +/windows/*.pch +/windows/*.rsp +/windows/*.obj +/windows/*.exe +/windows/*.ncb +/windows/*.plg +/windows/*.dsw +/windows/*.opt +/windows/*.dsp +/windows/*.tds +/windows/*.td2 +/windows/*.map +/windows/*.rcpp +/windows/Makefile.clangcl +/windows/Makefile.mgw +/windows/Makefile.vc +/windows/Makefile.lcc +/windows/MSVC +/windows/DEVCPP +/windows/VS2010 +/windows/VS2012 +/windows/*.log +/windows/*.GID +/windows/local +/windows/Output +/windows/*.DSA +/windows/*.RSA +/windows/*.cnt +/windows/*.hlp +/windows/.bmake +/windows/*.sln +/windows/*.suo +/windows/*.msi +/windows/*.wixobj +/windows/*.wixpdb diff --git a/Buildscr b/Buildscr index 2b789d4..f7556f7 100644 --- a/Buildscr +++ b/Buildscr @@ -35,7 +35,7 @@ module putty ifeq "$(RELEASE)" "" set Ndate $(!builddate) ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -pe 's/(....)(..)(..)/$$1-$$2-$$3/' > date ifneq "$(Ndate)" "" read Date date -set Epoch 17161 # update this at every release +set Epoch 17818 # update this at every release ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days ifneq "$(Ndate)" "" read Days days @@ -144,7 +144,7 @@ delegate - in putty do ./mkauto.sh in putty do ./configure CC=clang CFLAGS="-fsanitize=address -fsanitize=leak" in putty do make -j$(nproc) -in putty do python test/cryptsuite.py +in putty do python3 test/cryptsuite.py enddelegate # Windowsify LICENCE, since it's going in the Windows installers. @@ -152,8 +152,7 @@ in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE # Some gratuitous theming for the MSI installer UI. in putty/icons do make -j$(nproc) -in putty do convert -size 164x312 'gradient:blue-white' -distort SRT -90 -swirl 180 \( -size 329x312 canvas:white \) +append \( icons/putty-48.png -geometry +28+24 \) -composite \( icons/pscp-48.png -geometry +88+96 \) -composite \( icons/puttygen-48.png -geometry +28+168 \) -composite \( icons/pageant-48.png -geometry +88+240 \) -composite windows/msidialog.bmp -in putty do convert -size 493x58 canvas:white \( icons/putty-48.png -geometry +440+5 \) -composite windows/msibanner.bmp +in putty do ./windows/make_install_images.sh mkdir putty/windows/build32 mkdir putty/windows/build64 @@ -200,10 +199,20 @@ in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=x64 -dDllOk= in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=Arm -dDllOk=no -dBuilddir=abuild32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera32.msi -spdb in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=Arm64 -dDllOk=no -dBuilddir=abuild64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera64.msi -spdb -# Bodge the platform fields for the Windows on Arm installers, since -# WiX 3 doesn't understand Arm platform names itself. -in putty/windows do ./msiplatform.py installera32.msi Arm -in putty/windows do ./msiplatform.py installera64.msi Arm64 +# Change the width field for our dialog background image so that it +# doesn't stretch across the whole dialog. (WiX's default one does; we +# replace it with a narrow one so that the text to the right of it +# shows up on system default background colour, meaning that +# high-contrast mode doesn't make the text white on white. But that +# means we also have to modify the width field, and there's nothing in +# WiX's source syntax to make that happen.) +# +# Also bodge the platform fields for the Windows on Arm installers, +# since WiX 3 doesn't understand Arm platform names itself. +in putty/windows do ./msifixup.py installer32.msi --dialog-bmp-width=123 +in putty/windows do ./msifixup.py installer64.msi --dialog-bmp-width=123 +in putty/windows do ./msifixup.py installera32.msi --dialog-bmp-width=123 --platform=Arm +in putty/windows do ./msifixup.py installera64.msi --dialog-bmp-width=123 --platform=Arm64 # Sign the Windows installers. ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi installera32.msi installera64.msi @@ -232,7 +241,7 @@ in putty/windows with clangcl_a64 do Platform=arm64 make -f Makefile.clangcl BUI # # There's no installer to go with these, so they must also embed the # help file. -in putty/windows with clangcl32_2003 do Platform=x86 make -f Makefile.clangcl BUILDDIR=buildold/ RCFL=-DEMBED_CHM $(Makeargs) CCTARGET=i386-pc-windows-msvc13.0.0 SUBSYSVER=,4.0 EXTRA_windows=wincrt0.obj EXTRA_console=crt0.obj EXTRA_libs=libcpmt.lib XFLAGS=/arch:IA32 all -j$(nproc) +in putty/windows with clangcl32_2003 do Platform=x86 make -f Makefile.clangcl BUILDDIR=buildold/ RCFL=-DEMBED_CHM $(Makeargs) CCTARGET=i386-pc-windows-msvc13.0.0 SUBSYSVER=,4.0 EXTRA_windows=wincrt0.obj EXTRA_console=crt0.obj EXTRA_libs=libcpmt.lib XFLAGS="/arch:IA32 -Wno-pragma-pack" all -j$(nproc) # Remove test programs again. in putty/windows do make -f Makefile.clangcl BUILDDIR=build32/ cleantestprogs diff --git a/Buildscr.cv b/Buildscr.cv index deaf8be..fab61dc 100644 --- a/Buildscr.cv +++ b/Buildscr.cv @@ -25,7 +25,7 @@ enddelegate # Windows scanner for download). delegate covscan32wine in putty do tar xzvf cov-int.tar.gz - in putty/windows do cov-build --dir ../cov-int make -f Makefile.mgw CC=winegcc RC=wrc XFLAGS="-DCOVERITY -DNO_SECUREZEROMEMORY -D_FORCE_SOFTWARE_AES" + in putty/windows do cov-build --dir ../cov-int make -f Makefile.mgw CC=winegcc RC=wrc in putty do tar czvf cov-int.tar.gz cov-int return putty/cov-int.tar.gz enddelegate diff --git a/CHECKLST.txt b/CHECKLST.txt index 69355d2..5e9a7a0 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -109,8 +109,18 @@ Making a release candidate build * check they report the right version number * if there's any easily observable behaviour difference between the release branch and master, arrange to observe it - * test the Windows installer - * test the Unix source tarball. + * test that the Windows installer installs successfully + + on x86 and Arm, and test that putty.exe runs in both cases + * test that the Unix source tarball unpacks and builds + + on at least a reasonably current stable Linux distro, and + also try Debian sid + + test-build with all of GTK 1, 2 and 3 + + test-build with -DNOT_X_WINDOWS + * feed the release-candidate source to Coverity and make sure it + didn't turn up any last-minute problems + * make sure we have a clean run of sctest + * do some testing on a system with a completely clean slate (no + prior saved session data) Preparing to make the release ----------------------------- diff --git a/LATEST.VER b/LATEST.VER index 6ab5ccf..07b41f7 100644 --- a/LATEST.VER +++ b/LATEST.VER @@ -1 +1 @@ -0.73 +0.76 diff --git a/LICENCE b/LICENCE index 98c8c85..0e1df3c 100644 --- a/LICENCE +++ b/LICENCE @@ -1,11 +1,12 @@ -PuTTY is copyright 1997-2019 Simon Tatham. +PuTTY is copyright 1997-2021 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav -Kuzmich, Nico Williams, Viktor Dukhovni, and CORE SDI S.A. +Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, +and CORE SDI S.A. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files diff --git a/Makefile.am b/Makefile.am index 3360c6b..faa2af4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,55 +10,62 @@ allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ charset/enum.c charset/fromucs.c charset/internal.h \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ - charset/toucs.c charset/utf8.c charset/xenc.c cmdgen.c \ - cmdline.c conf.c config.c cproxy.c defs.h dialog.c dialog.h \ - ecc.c ecc.h errsock.c fuzzterm.c import.c ldisc.c ldisc.h \ - licence.h logging.c mainchan.c marshal.c marshal.h memory.c \ + charset/toucs.c charset/utf8.c charset/xenc.c clicons.c \ + cmdgen.c cmdline.c conf.c config.c console.c console.h \ + cproxy.c defs.h dialog.c dialog.h ecc.c ecc.h errsock.c \ + fuzzterm.c import.c ldisc.c ldisc.h licence.h logging.c \ + mainchan.c marshal.c marshal.h memory.c millerrabin.c \ minibidi.c misc.c misc.h miscucs.c mpint.c mpint.h mpint_i.h \ - network.h nocmdline.c nocproxy.c nogss.c noshare.c noterm.c \ - notiming.c nullplug.c pageant.c pageant.h pgssapi.c \ - pgssapi.h pinger.c portfwd.c proxy.c proxy.h pscp.c psftp.c \ - psftp.h psftpcommon.c putty.h puttymem.h puttyps.h raw.c \ - rlogin.c scpserver.c sercfg.c sesschan.c sessprep.c \ - settings.c sftp.c sftp.h sftpcommon.c sftpserver.c ssh.c \ - ssh.h ssh1bpp.c ssh1censor.c ssh1connection-client.c \ - ssh1connection-server.c ssh1connection.c ssh1connection.h \ - ssh1login-server.c ssh1login.c ssh2bpp-bare.c ssh2bpp.c \ - ssh2censor.c ssh2connection-client.c ssh2connection-server.c \ + mpunsafe.c mpunsafe.h network.h nocmdline.c nocproxy.c \ + nogss.c norand.c noshare.c noterm.c notiming.c nullplug.c \ + pageant.c pageant.h pgssapi.c pgssapi.h pinger.c pockle.c \ + portfwd.c primecandidate.c proxy.c proxy.h pscp.c psftp.c \ + psftp.h psftpcommon.c psocks.c psocks.h putty.h puttymem.h \ + puttyps.h raw.c rlogin.c scpserver.c sesschan.c sessprep.c \ + settings.c sftp.c sftp.h sftpcommon.c sftpserver.c \ + smallprimes.c ssh.c ssh.h ssh1bpp.c ssh1censor.c \ + ssh1connection-client.c ssh1connection-server.c \ + ssh1connection.c ssh1connection.h ssh1login-server.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ + ssh2connection-client.c ssh2connection-server.c \ ssh2connection.c ssh2connection.h ssh2kex-client.c \ ssh2kex-server.c ssh2transhk.c ssh2transport.c \ ssh2transport.h ssh2userauth-server.c ssh2userauth.c \ - sshaes.c ssharcf.c sshauxcrypt.c sshbcrypt.c sshblowf.c \ - sshblowf.h sshbpp.h sshccp.c sshchan.h sshcommon.c sshcr.h \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshdssg.c \ - sshecc.c sshecdsag.c sshgss.h sshgssc.c sshgssc.h sshhmac.c \ - sshmac.c sshmd5.c sshppl.h sshprime.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshrsag.c sshserver.c sshserver.h \ - sshsh256.c sshsh512.c sshsha.c sshshare.c sshsignals.h \ - sshttymodes.h sshverstring.c sshzlib.c storage.h stripctrl.c \ - telnet.c terminal.c terminal.h testcrypt.c testcrypt.h \ - testsc.c testzlib.c time.c timing.c tree234.c tree234.h \ - unix/gtkapp.c unix/gtkask.c unix/gtkcfg.c unix/gtkcols.c \ - unix/gtkcols.h unix/gtkcomm.c unix/gtkcompat.h unix/gtkdlg.c \ - unix/gtkfont.c unix/gtkfont.h unix/gtkmain.c unix/gtkmisc.c \ - unix/gtkmisc.h unix/gtkwin.c unix/osxlaunch.c unix/procnet.c \ - unix/unix.h unix/ux_x11.c unix/uxagentc.c unix/uxagentsock.c \ - unix/uxcfg.c unix/uxcons.c unix/uxfdsock.c unix/uxgen.c \ + sshaes.c ssharcf.c sshargon2.c sshauxcrypt.c sshbcrypt.c \ + sshblake2.c sshblowf.c sshblowf.h sshbpp.h sshccp.c \ + sshchan.h sshcommon.c sshcr.h sshcrc.c sshcrcda.c sshdes.c \ + sshdh.c sshdss.c sshdssg.c sshecc.c sshecdsag.c sshgss.h \ + sshgssc.c sshgssc.h sshhmac.c sshkeygen.h sshmac.c sshmd5.c \ + sshppl.h sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c \ + sshrsag.c sshserver.c sshserver.h sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshsignals.h sshttymodes.h \ + sshutils.c sshverstring.c sshzlib.c storage.h stripctrl.c \ + supdup.c telnet.c terminal.c terminal.h testcrypt.c \ + testcrypt.h testsc.c testzlib.c time.c timing.c tree234.c \ + tree234.h unix/gtkapp.c unix/gtkask.c unix/gtkcfg.c \ + unix/gtkcols.c unix/gtkcols.h unix/gtkcomm.c \ + unix/gtkcompat.h unix/gtkdlg.c unix/gtkfont.c unix/gtkfont.h \ + unix/gtkmain.c unix/gtkmisc.c unix/gtkmisc.h unix/gtkwin.c \ + unix/osxlaunch.c unix/procnet.c unix/unix.h unix/ux_x11.c \ + unix/uxagentc.c unix/uxagentsock.c unix/uxcfg.c \ + unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c unix/uxgen.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpgnt.c unix/uxplink.c \ - unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxpterm.c \ - unix/uxpty.c unix/uxputty.c unix/uxsel.c unix/uxser.c \ - unix/uxserver.c unix/uxsftp.c unix/uxsftpserver.c \ - unix/uxshare.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ - unix/uxutils.c unix/x11misc.c unix/x11misc.h unix/xkeysym.c \ + unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxpsusan.c \ + unix/uxpterm.c unix/uxpty.c unix/uxputty.c unix/uxsel.c \ + unix/uxser.c unix/uxserver.c unix/uxsftp.c \ + unix/uxsftpserver.c unix/uxshare.c unix/uxsignal.c \ + unix/uxsocks.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ + unix/uxutils.h unix/x11misc.c unix/x11misc.h unix/xkeysym.c \ unix/xpmptcfg.c unix/xpmpterm.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c version.c version.h wcwidth.c \ - wildcard.c windows/pageant.rc windows/plink.rc \ - windows/pscp.rc windows/psftp.rc windows/putty.rc \ - windows/puttygen.rc windows/puttytel.rc windows/rcstuff.h \ - windows/sizetip.c windows/version.rc2 windows/win_res.h \ - windows/win_res.rc2 windows/wincapi.c windows/wincapi.h \ - windows/wincfg.c windows/wincons.c windows/winctrls.c \ + wildcard.c windows/pageant-rc.h windows/pageant.rc \ + windows/plink.rc windows/pscp.rc windows/psftp.rc \ + windows/putty.rc windows/puttygen-rc.h windows/puttygen.rc \ + windows/puttytel.rc windows/rcstuff.h windows/sizetip.c \ + windows/version.rc2 windows/win_res.h windows/win_res.rc2 \ + windows/wincapi.c windows/wincapi.h windows/wincfg.c \ + windows/wincliloop.c windows/wincons.c windows/winctrls.c \ windows/windefs.c windows/windlg.c windows/window.c \ windows/wingss.c windows/winhandl.c windows/winhelp.c \ windows/winhelp.h windows/winhelp.rc2 windows/winhsock.c \ @@ -67,22 +74,25 @@ allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ windows/winnojmp.c windows/winnpc.c windows/winnps.c \ windows/winpgen.c windows/winpgnt.c windows/winpgntc.c \ windows/winplink.c windows/winprint.c windows/winproxy.c \ - windows/winsecur.c windows/winsecur.h windows/winser.c \ - windows/winsftp.c windows/winshare.c windows/winstore.c \ - windows/winstuff.h windows/wintime.c windows/winucs.c \ - windows/winutils.c windows/winx11.c x11fwd.c + windows/winseat.h windows/winsecur.c windows/winsecur.h \ + windows/winselcli.c windows/winselgui.c windows/winser.c \ + windows/winsftp.c windows/winshare.c windows/winsocks.c \ + windows/winstore.c windows/winstuff.h windows/wintime.c \ + windows/winucs.c windows/winutils.c windows/winx11.c \ + x11fwd.c if HAVE_GTK -bin_PROGRAMS = plink pscp psftp puttygen pageant pterm putty puttytel +bin_PROGRAMS = plink pscp psftp psusan puttygen pageant pterm putty puttytel else -bin_PROGRAMS = plink pscp psftp puttygen +bin_PROGRAMS = plink pscp psftp psusan puttygen endif if HAVE_GTK -noinst_PROGRAMS = cgtest fuzzterm osxlaunch testcrypt testsc testzlib uppity \ - ptermapp puttyapp +noinst_PROGRAMS = cgtest fuzzterm osxlaunch psocks testcrypt testsc testzlib \ + uppity ptermapp puttyapp else -noinst_PROGRAMS = cgtest fuzzterm osxlaunch testcrypt testsc testzlib uppity +noinst_PROGRAMS = cgtest fuzzterm osxlaunch psocks testcrypt testsc testzlib \ + uppity endif AM_CPPFLAGS = -I$(srcdir)/./ -I$(srcdir)/charset/ -I$(srcdir)/windows/ \ @@ -97,14 +107,16 @@ libversion_a_SOURCES = version.c libversion_a_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) noinst_LIBRARIES = libversion.a -cgtest_SOURCES = cgtest.c conf.c ecc.c import.c marshal.c memory.c misc.c \ - mpint.c notiming.c sshaes.c sshauxcrypt.c sshbcrypt.c \ - sshblowf.c sshdes.c sshdss.c sshdssg.c sshecc.c sshecdsag.c \ - sshhmac.c sshmd5.c sshprime.c sshprng.c sshpubk.c sshrand.c \ - sshrsa.c sshrsag.c sshsh256.c sshsh512.c sshsha.c \ - stripctrl.c time.c tree234.c unix/uxcons.c unix/uxgen.c \ - unix/uxmisc.c unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c \ - unix/uxstore.c unix/uxutils.c utils.c wcwidth.c +cgtest_SOURCES = cgtest.c conf.c console.c ecc.c import.c marshal.c memory.c \ + millerrabin.c misc.c mpint.c mpunsafe.c notiming.c pockle.c \ + primecandidate.c smallprimes.c sshaes.c sshargon2.c \ + sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ + sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ + tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ + unix/uxutils.c utils.c wcwidth.c cgtest_LDADD = libversion.a fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ @@ -112,44 +124,46 @@ fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ fuzzterm.c logging.c marshal.c memory.c minibidi.c misc.c \ - miscucs.c sercfg.c settings.c stripctrl.c terminal.c time.c \ - timing.c tree234.c unix/uxcfg.c unix/uxmisc.c unix/uxnogtk.c \ + miscucs.c settings.c stripctrl.c terminal.c time.c timing.c \ + tree234.c unix/uxcfg.c unix/uxmisc.c unix/uxnogtk.c \ unix/uxprint.c unix/uxstore.c unix/uxucs.c utils.c wcwidth.c fuzzterm_LDADD = libversion.a osxlaunch_SOURCES = unix/osxlaunch.c if HAVE_GTK -pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c ecc.c \ - errsock.c logging.c marshal.c memory.c misc.c mpint.c \ +pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c console.c \ + ecc.c errsock.c logging.c marshal.c memory.c misc.c mpint.c \ nocproxy.c nogss.c nullplug.c pageant.c proxy.c settings.c \ - sshaes.c sshauxcrypt.c sshdes.c sshdss.c sshecc.c sshhmac.c \ - sshmd5.c sshprng.c sshpubk.c sshrsa.c sshsh256.c sshsh512.c \ - sshsha.c stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ + sshaes.c sshargon2.c sshauxcrypt.c sshblake2.c sshdes.c \ + sshdss.c sshecc.c sshhmac.c sshmd5.c sshprng.c sshpubk.c \ + sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ - unix/uxagentsock.c unix/uxcons.c unix/uxfdsock.c \ - unix/uxmisc.c unix/uxnet.c unix/uxnoise.c unix/uxpeer.c \ - unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ - unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ - wcwidth.c x11fwd.c + unix/uxagentsock.c unix/uxcliloop.c unix/uxcons.c \ + unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ + unix/uxpeer.c unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c \ + unix/uxsel.c unix/uxsignal.c unix/uxstore.c unix/uxutils.c \ + utils.c wcwidth.c x11fwd.c pageant_LDADD = libversion.a $(GTK_LIBS) endif -plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c cmdline.c \ - conf.c cproxy.c ecc.c errsock.c ldisc.c logging.c mainchan.c \ - marshal.c memory.c misc.c mpint.c noterm.c nullplug.c \ - pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ - sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ - ssh1connection-client.c ssh1connection.c ssh1login.c \ - ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ +plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c clicons.c \ + cmdline.c conf.c console.c cproxy.c ecc.c errsock.c ldisc.c \ + logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ + noterm.c nullplug.c pgssapi.c pinger.c portfwd.c proxy.c \ + raw.c rlogin.c sessprep.c settings.c ssh.c ssh1bpp.c \ + ssh1censor.c ssh1connection-client.c ssh1connection.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c telnet.c time.c \ - timing.c tree234.c unix/ux_x11.c unix/uxagentc.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c supdup.c telnet.c time.c timing.c \ + tree234.c unix/ux_x11.c unix/uxagentc.c unix/uxcliloop.c \ unix/uxcons.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c \ unix/uxplink.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ @@ -157,56 +171,93 @@ plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c cmdline.c \ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c plink_LDADD = libversion.a -pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ - conf.c cproxy.c ecc.c errsock.c logging.c mainchan.c \ - marshal.c memory.c misc.c mpint.c nullplug.c pgssapi.c \ - pinger.c portfwd.c proxy.c pscp.c psftpcommon.c settings.c \ - sftp.c sftpcommon.c ssh.c ssh1bpp.c ssh1censor.c \ - ssh1connection-client.c ssh1connection.c ssh1login.c \ - ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ +pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ + cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ + logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ + nullplug.c pgssapi.c pinger.c portfwd.c proxy.c pscp.c \ + psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ + ssh1censor.c ssh1connection-client.c ssh1connection.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c time.c timing.c \ - tree234.c unix/uxagentc.c unix/uxcons.c unix/uxfdsock.c \ - unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ - unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ - unix/uxsel.c unix/uxsftp.c unix/uxshare.c unix/uxstore.c \ - unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c time.c timing.c tree234.c \ + unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ + unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ + unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ + unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ + x11fwd.c pscp_LDADD = libversion.a -psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ - conf.c cproxy.c ecc.c errsock.c logging.c mainchan.c \ - marshal.c memory.c misc.c mpint.c nullplug.c pgssapi.c \ - pinger.c portfwd.c proxy.c psftp.c psftpcommon.c settings.c \ - sftp.c sftpcommon.c ssh.c ssh1bpp.c ssh1censor.c \ - ssh1connection-client.c ssh1connection.c ssh1login.c \ - ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ +psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ + cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ + logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ + nullplug.c pgssapi.c pinger.c portfwd.c proxy.c psftp.c \ + psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ + ssh1censor.c ssh1connection-client.c ssh1connection.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c time.c timing.c \ - tree234.c unix/uxagentc.c unix/uxcons.c unix/uxfdsock.c \ - unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ - unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ - unix/uxsel.c unix/uxsftp.c unix/uxshare.c unix/uxstore.c \ - unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c time.c timing.c tree234.c \ + unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ + unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ + unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ + unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ + x11fwd.c psftp_LDADD = libversion.a +psocks_SOURCES = be_misc.c callback.c conf.c console.c errsock.c logging.c \ + marshal.c memory.c misc.c nocproxy.c norand.c portfwd.c \ + proxy.c psocks.c sshutils.c stripctrl.c time.c timing.c \ + tree234.c unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c \ + unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxpeer.c \ + unix/uxpoll.c unix/uxproxy.c unix/uxsel.c unix/uxsignal.c \ + unix/uxsocks.c utils.c wcwidth.c +psocks_LDADD = libversion.a + +psusan_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ + errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ + mpint.c mpunsafe.c nogss.c nullplug.c pgssapi.c pockle.c \ + portfwd.c primecandidate.c proxy.c scpserver.c sesschan.c \ + settings.c sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ + ssh1censor.c ssh1connection-server.c ssh1connection.c \ + ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ + ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ + ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ + timing.c tree234.c unix/procnet.c unix/ux_x11.c \ + unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ + unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c \ + unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c unix/uxpsusan.c \ + unix/uxpty.c unix/uxsel.c unix/uxsftpserver.c \ + unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ + wcwidth.c wildcard.c x11fwd.c +psusan_LDADD = libversion.a + if HAVE_GTK pterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c cmdline.c conf.c config.c \ dialog.c ldisc.c logging.c marshal.c memory.c minibidi.c \ - misc.c miscucs.c nocproxy.c nogss.c sercfg.c sessprep.c \ - settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ + misc.c miscucs.c nocproxy.c nogss.c sessprep.c settings.c \ + stripctrl.c terminal.c time.c timing.c tree234.c \ unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ @@ -222,7 +273,7 @@ ptermapp_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ ldisc.c logging.c marshal.c memory.c minibidi.c misc.c \ - miscucs.c nocmdline.c nocproxy.c nogss.c sercfg.c sessprep.c \ + miscucs.c nocmdline.c nocproxy.c nogss.c sessprep.c \ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c unix/gtkwin.c \ @@ -241,18 +292,19 @@ putty_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ charset/xenc.c cmdline.c conf.c config.c cproxy.c dialog.c \ ecc.c errsock.c ldisc.c logging.c mainchan.c marshal.c \ memory.c minibidi.c misc.c miscucs.c mpint.c nullplug.c \ - pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c sercfg.c \ + pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection.c ssh1login.c \ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c telnet.c terminal.c \ - time.c timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ + timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ @@ -273,18 +325,19 @@ puttyapp_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ charset/xenc.c conf.c config.c cproxy.c dialog.c ecc.c \ errsock.c ldisc.c logging.c mainchan.c marshal.c memory.c \ minibidi.c misc.c miscucs.c mpint.c nocmdline.c nullplug.c \ - pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c sercfg.c \ + pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection.c ssh1login.c \ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c telnet.c terminal.c \ - time.c timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ + timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ @@ -297,14 +350,16 @@ puttyapp_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ puttyapp_LDADD = libversion.a $(GTK_LIBS) endif -puttygen_SOURCES = cmdgen.c conf.c ecc.c import.c marshal.c memory.c misc.c \ - mpint.c notiming.c sshaes.c sshauxcrypt.c sshbcrypt.c \ - sshblowf.c sshdes.c sshdss.c sshdssg.c sshecc.c sshecdsag.c \ - sshhmac.c sshmd5.c sshprime.c sshprng.c sshpubk.c sshrand.c \ - sshrsa.c sshrsag.c sshsh256.c sshsh512.c sshsha.c \ - stripctrl.c time.c tree234.c unix/uxcons.c unix/uxgen.c \ - unix/uxmisc.c unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c \ - unix/uxstore.c unix/uxutils.c utils.c wcwidth.c +puttygen_SOURCES = cmdgen.c conf.c console.c ecc.c import.c marshal.c \ + memory.c millerrabin.c misc.c mpint.c mpunsafe.c notiming.c \ + pockle.c primecandidate.c smallprimes.c sshaes.c sshargon2.c \ + sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ + sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ + tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ + unix/uxutils.c utils.c wcwidth.c puttygen_LDADD = libversion.a if HAVE_GTK @@ -314,48 +369,54 @@ puttytel_SOURCES = be_misc.c be_nos_s.c callback.c charset/fromucs.c \ charset/toucs.c charset/utf8.c charset/xenc.c cmdline.c \ conf.c config.c dialog.c errsock.c ldisc.c logging.c \ marshal.c memory.c minibidi.c misc.c miscucs.c nocproxy.c \ - nogss.c pinger.c proxy.c raw.c rlogin.c sercfg.c sessprep.c \ - settings.c stripctrl.c telnet.c terminal.c time.c timing.c \ - tree234.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ - unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c \ - unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c unix/uxmisc.c \ - unix/uxnet.c unix/uxpeer.c unix/uxpoll.c unix/uxprint.c \ - unix/uxproxy.c unix/uxputty.c unix/uxsel.c unix/uxser.c \ - unix/uxsignal.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ - unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ + nogss.c norand.c pinger.c proxy.c raw.c rlogin.c sessprep.c \ + settings.c stripctrl.c supdup.c telnet.c terminal.c time.c \ + timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ + unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ + unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c \ + unix/uxmisc.c unix/uxnet.c unix/uxpeer.c unix/uxpoll.c \ + unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ + unix/uxser.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ + unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c wcwidth.c puttytel_LDADD = libversion.a $(GTK_LIBS) endif -testcrypt_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ - sshauxcrypt.c sshblowf.c sshccp.c sshcrc.c sshcrcda.c \ - sshdes.c sshdh.c sshdss.c sshecc.c sshhmac.c sshmd5.c \ - sshprime.c sshprng.c sshrsa.c sshsh256.c sshsh512.c sshsha.c \ - testcrypt.c tree234.c unix/uxutils.c utils.c +testcrypt_SOURCES = ecc.c marshal.c memory.c millerrabin.c mpint.c \ + mpunsafe.c pockle.c primecandidate.c smallprimes.c sshaes.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c \ + sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c sshprime.c \ + sshprng.c sshpubk.c sshrsa.c sshrsag.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c testcrypt.c tree234.c unix/uxutils.c \ + utils.c testsc_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ - sshauxcrypt.c sshblowf.c sshccp.c sshcrc.c sshcrcda.c \ - sshdes.c sshdh.c sshdss.c sshecc.c sshhmac.c sshmac.c \ - sshmd5.c sshrsa.c sshsh256.c sshsh512.c sshsha.c testsc.c \ - tree234.c unix/uxutils.c utils.c wildcard.c + sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c sshccp.c \ + sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ + sshhmac.c sshmac.c sshmd5.c sshpubk.c sshrsa.c sshsh256.c \ + sshsh512.c sshsha.c sshsha3.c testsc.c tree234.c \ + unix/uxutils.c utils.c wildcard.c testzlib_SOURCES = marshal.c memory.c sshzlib.c testzlib.c utils.c uppity_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ - errsock.c logging.c marshal.c memory.c misc.c mpint.c \ - nullplug.c pgssapi.c portfwd.c proxy.c scpserver.c \ - sesschan.c settings.c sftpcommon.c sftpserver.c ssh1bpp.c \ + errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ + mpint.c mpunsafe.c nullplug.c pgssapi.c pockle.c portfwd.c \ + primecandidate.c proxy.c scpserver.c sesschan.c settings.c \ + sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ ssh1censor.c ssh1connection-server.c ssh1connection.c \ - ssh1login-server.c ssh2bpp.c ssh2censor.c \ + ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprime.c sshprng.c \ - sshpubk.c sshrand.c sshrsa.c sshrsag.c sshserver.c \ - sshsh256.c sshsh512.c sshsha.c sshverstring.c sshzlib.c \ - stripctrl.c time.c timing.c tree234.c unix/procnet.c \ - unix/ux_x11.c unix/uxagentsock.c unix/uxfdsock.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ + timing.c tree234.c unix/procnet.c unix/ux_x11.c \ + unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ unix/uxpty.c unix/uxsel.c unix/uxserver.c \ @@ -379,10 +440,10 @@ check-local: testcrypt PUTTY_TESTCRYPT=./testcrypt $(srcdir)/test/cryptsuite.py if HAVE_GTK -man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 \ +man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 \ doc/pageant.1 doc/pterm.1 doc/putty.1 doc/puttytel.1 else -man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 +man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 endif if HAVE_SETID_CMD install-exec-local: diff --git a/Makefile.in b/Makefile.in index effd70a..9d1ae54 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.15.1 from Makefile.am. +# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2017 Free Software Foundation, Inc. +# Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -94,20 +94,21 @@ NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : @HAVE_GTK_FALSE@bin_PROGRAMS = plink$(EXEEXT) pscp$(EXEEXT) \ -@HAVE_GTK_FALSE@ psftp$(EXEEXT) puttygen$(EXEEXT) +@HAVE_GTK_FALSE@ psftp$(EXEEXT) psusan$(EXEEXT) \ +@HAVE_GTK_FALSE@ puttygen$(EXEEXT) @HAVE_GTK_TRUE@bin_PROGRAMS = plink$(EXEEXT) pscp$(EXEEXT) \ -@HAVE_GTK_TRUE@ psftp$(EXEEXT) puttygen$(EXEEXT) \ -@HAVE_GTK_TRUE@ pageant$(EXEEXT) pterm$(EXEEXT) putty$(EXEEXT) \ -@HAVE_GTK_TRUE@ puttytel$(EXEEXT) +@HAVE_GTK_TRUE@ psftp$(EXEEXT) psusan$(EXEEXT) \ +@HAVE_GTK_TRUE@ puttygen$(EXEEXT) pageant$(EXEEXT) \ +@HAVE_GTK_TRUE@ pterm$(EXEEXT) putty$(EXEEXT) puttytel$(EXEEXT) @HAVE_GTK_FALSE@noinst_PROGRAMS = cgtest$(EXEEXT) fuzzterm$(EXEEXT) \ -@HAVE_GTK_FALSE@ osxlaunch$(EXEEXT) testcrypt$(EXEEXT) \ -@HAVE_GTK_FALSE@ testsc$(EXEEXT) testzlib$(EXEEXT) \ -@HAVE_GTK_FALSE@ uppity$(EXEEXT) +@HAVE_GTK_FALSE@ osxlaunch$(EXEEXT) psocks$(EXEEXT) \ +@HAVE_GTK_FALSE@ testcrypt$(EXEEXT) testsc$(EXEEXT) \ +@HAVE_GTK_FALSE@ testzlib$(EXEEXT) uppity$(EXEEXT) @HAVE_GTK_TRUE@noinst_PROGRAMS = cgtest$(EXEEXT) fuzzterm$(EXEEXT) \ -@HAVE_GTK_TRUE@ osxlaunch$(EXEEXT) testcrypt$(EXEEXT) \ -@HAVE_GTK_TRUE@ testsc$(EXEEXT) testzlib$(EXEEXT) \ -@HAVE_GTK_TRUE@ uppity$(EXEEXT) ptermapp$(EXEEXT) \ -@HAVE_GTK_TRUE@ puttyapp$(EXEEXT) +@HAVE_GTK_TRUE@ osxlaunch$(EXEEXT) psocks$(EXEEXT) \ +@HAVE_GTK_TRUE@ testcrypt$(EXEEXT) testsc$(EXEEXT) \ +@HAVE_GTK_TRUE@ testzlib$(EXEEXT) uppity$(EXEEXT) \ +@HAVE_GTK_TRUE@ ptermapp$(EXEEXT) puttyapp$(EXEEXT) @AUTO_GIT_COMMIT_TRUE@am__append_1 = -DSOURCE_COMMIT=\"`git --git-dir=$(srcdir)/.git rev-parse HEAD 2>/dev/null`\" subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 @@ -122,6 +123,8 @@ mkinstalldirs = $(install_sh) -d CONFIG_HEADER = uxconfig.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" +PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) @@ -132,24 +135,26 @@ libversion_a_AR = $(AR) $(ARFLAGS) libversion_a_LIBADD = am_libversion_a_OBJECTS = libversion_a-version.$(OBJEXT) libversion_a_OBJECTS = $(am_libversion_a_OBJECTS) -am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" -PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am__dirstamp = $(am__leading_dot)dirstamp -am_cgtest_OBJECTS = cgtest.$(OBJEXT) conf.$(OBJEXT) ecc.$(OBJEXT) \ - import.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ - misc.$(OBJEXT) mpint.$(OBJEXT) notiming.$(OBJEXT) \ - sshaes.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshbcrypt.$(OBJEXT) \ +am_cgtest_OBJECTS = cgtest.$(OBJEXT) conf.$(OBJEXT) console.$(OBJEXT) \ + ecc.$(OBJEXT) import.$(OBJEXT) marshal.$(OBJEXT) \ + memory.$(OBJEXT) millerrabin.$(OBJEXT) misc.$(OBJEXT) \ + mpint.$(OBJEXT) mpunsafe.$(OBJEXT) notiming.$(OBJEXT) \ + pockle.$(OBJEXT) primecandidate.$(OBJEXT) \ + smallprimes.$(OBJEXT) sshaes.$(OBJEXT) sshargon2.$(OBJEXT) \ + sshauxcrypt.$(OBJEXT) sshbcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshdes.$(OBJEXT) sshdss.$(OBJEXT) \ sshdssg.$(OBJEXT) sshecc.$(OBJEXT) sshecdsag.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) sshprime.$(OBJEXT) \ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) sshsh256.$(OBJEXT) \ - sshsh512.$(OBJEXT) sshsha.$(OBJEXT) stripctrl.$(OBJEXT) \ - time.$(OBJEXT) tree234.$(OBJEXT) unix/uxcons.$(OBJEXT) \ - unix/uxgen.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ - unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ - unix/uxpoll.$(OBJEXT) unix/uxstore.$(OBJEXT) \ - unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) + sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ + stripctrl.$(OBJEXT) time.$(OBJEXT) tree234.$(OBJEXT) \ + unix/uxcons.$(OBJEXT) unix/uxgen.$(OBJEXT) \ + unix/uxmisc.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ + unix/uxnoise.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ + unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ + wcwidth.$(OBJEXT) cgtest_OBJECTS = $(am_cgtest_OBJECTS) cgtest_DEPENDENCIES = libversion.a am_fuzzterm_OBJECTS = be_none.$(OBJEXT) callback.$(OBJEXT) \ @@ -161,9 +166,9 @@ am_fuzzterm_OBJECTS = be_none.$(OBJEXT) callback.$(OBJEXT) \ config.$(OBJEXT) dialog.$(OBJEXT) fuzzterm.$(OBJEXT) \ logging.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ minibidi.$(OBJEXT) misc.$(OBJEXT) miscucs.$(OBJEXT) \ - sercfg.$(OBJEXT) settings.$(OBJEXT) stripctrl.$(OBJEXT) \ - terminal.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ - tree234.$(OBJEXT) unix/uxcfg.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ + settings.$(OBJEXT) stripctrl.$(OBJEXT) terminal.$(OBJEXT) \ + time.$(OBJEXT) timing.$(OBJEXT) tree234.$(OBJEXT) \ + unix/uxcfg.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ unix/uxnogtk.$(OBJEXT) unix/uxprint.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) @@ -173,174 +178,242 @@ am_osxlaunch_OBJECTS = unix/osxlaunch.$(OBJEXT) osxlaunch_OBJECTS = $(am_osxlaunch_OBJECTS) osxlaunch_LDADD = $(LDADD) am__pageant_SOURCES_DIST = aqsync.c be_misc.c be_none.c callback.c \ - conf.c ecc.c errsock.c logging.c marshal.c memory.c misc.c \ - mpint.c nocproxy.c nogss.c nullplug.c pageant.c proxy.c \ - settings.c sshaes.c sshauxcrypt.c sshdes.c sshdss.c sshecc.c \ - sshhmac.c sshmd5.c sshprng.c sshpubk.c sshrsa.c sshsh256.c \ - sshsh512.c sshsha.c stripctrl.c time.c timing.c tree234.c \ - unix/gtkask.c unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ - unix/uxagentsock.c unix/uxcons.c unix/uxfdsock.c unix/uxmisc.c \ - unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpgnt.c \ - unix/uxpoll.c unix/uxproxy.c unix/uxsel.c unix/uxsignal.c \ - unix/uxstore.c unix/uxutils.c utils.c wcwidth.c x11fwd.c + conf.c console.c ecc.c errsock.c logging.c marshal.c memory.c \ + misc.c mpint.c nocproxy.c nogss.c nullplug.c pageant.c proxy.c \ + settings.c sshaes.c sshargon2.c sshauxcrypt.c sshblake2.c \ + sshdes.c sshdss.c sshecc.c sshhmac.c sshmd5.c sshprng.c \ + sshpubk.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ + unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ + unix/uxagentsock.c unix/uxcliloop.c unix/uxcons.c \ + unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ + unix/uxpeer.c unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c \ + unix/uxsel.c unix/uxsignal.c unix/uxstore.c unix/uxutils.c \ + utils.c wcwidth.c x11fwd.c @HAVE_GTK_TRUE@am_pageant_OBJECTS = aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_none.$(OBJEXT) callback.$(OBJEXT) \ -@HAVE_GTK_TRUE@ conf.$(OBJEXT) ecc.$(OBJEXT) errsock.$(OBJEXT) \ -@HAVE_GTK_TRUE@ logging.$(OBJEXT) marshal.$(OBJEXT) \ -@HAVE_GTK_TRUE@ memory.$(OBJEXT) misc.$(OBJEXT) mpint.$(OBJEXT) \ +@HAVE_GTK_TRUE@ conf.$(OBJEXT) console.$(OBJEXT) ecc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ errsock.$(OBJEXT) logging.$(OBJEXT) \ +@HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ +@HAVE_GTK_TRUE@ misc.$(OBJEXT) mpint.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocproxy.$(OBJEXT) nogss.$(OBJEXT) \ @HAVE_GTK_TRUE@ nullplug.$(OBJEXT) pageant.$(OBJEXT) \ @HAVE_GTK_TRUE@ proxy.$(OBJEXT) settings.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sshaes.$(OBJEXT) sshauxcrypt.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshaes.$(OBJEXT) sshargon2.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdes.$(OBJEXT) sshdss.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshecc.$(OBJEXT) sshhmac.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshmd5.$(OBJEXT) sshprng.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshpubk.$(OBJEXT) sshrsa.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sshsha.$(OBJEXT) stripctrl.$(OBJEXT) \ -@HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ -@HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkask.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxagentc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ +@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) time.$(OBJEXT) \ +@HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkask.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/ux_x11.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxagentsock.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxcons.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxpgnt.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ -@HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) x11fwd.$(OBJEXT) +@HAVE_GTK_TRUE@ unix/uxcliloop.$(OBJEXT) unix/uxcons.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxnet.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpeer.$(OBJEXT) unix/uxpgnt.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) \ +@HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) \ +@HAVE_GTK_TRUE@ x11fwd.$(OBJEXT) pageant_OBJECTS = $(am_pageant_OBJECTS) am__DEPENDENCIES_1 = @HAVE_GTK_TRUE@pageant_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) am_plink_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) \ be_all_s.$(OBJEXT) be_misc.$(OBJEXT) callback.$(OBJEXT) \ - cmdline.$(OBJEXT) conf.$(OBJEXT) cproxy.$(OBJEXT) \ - ecc.$(OBJEXT) errsock.$(OBJEXT) ldisc.$(OBJEXT) \ - logging.$(OBJEXT) mainchan.$(OBJEXT) marshal.$(OBJEXT) \ - memory.$(OBJEXT) misc.$(OBJEXT) mpint.$(OBJEXT) \ - noterm.$(OBJEXT) nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ - pinger.$(OBJEXT) portfwd.$(OBJEXT) proxy.$(OBJEXT) \ - raw.$(OBJEXT) rlogin.$(OBJEXT) sessprep.$(OBJEXT) \ - settings.$(OBJEXT) ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ - ssh1censor.$(OBJEXT) ssh1connection-client.$(OBJEXT) \ - ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ - ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ - ssh2connection-client.$(OBJEXT) ssh2connection.$(OBJEXT) \ - ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ - ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ - sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshauxcrypt.$(OBJEXT) \ + clicons.$(OBJEXT) cmdline.$(OBJEXT) conf.$(OBJEXT) \ + console.$(OBJEXT) cproxy.$(OBJEXT) ecc.$(OBJEXT) \ + errsock.$(OBJEXT) ldisc.$(OBJEXT) logging.$(OBJEXT) \ + mainchan.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ + misc.$(OBJEXT) mpint.$(OBJEXT) noterm.$(OBJEXT) \ + nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ + portfwd.$(OBJEXT) proxy.$(OBJEXT) raw.$(OBJEXT) \ + rlogin.$(OBJEXT) sessprep.$(OBJEXT) settings.$(OBJEXT) \ + ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ + ssh1connection-client.$(OBJEXT) ssh1connection.$(OBJEXT) \ + ssh1login.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ + ssh2censor.$(OBJEXT) ssh2connection-client.$(OBJEXT) \ + ssh2connection.$(OBJEXT) ssh2kex-client.$(OBJEXT) \ + ssh2transhk.$(OBJEXT) ssh2transport.$(OBJEXT) \ + ssh2userauth.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ + sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshccp.$(OBJEXT) sshcommon.$(OBJEXT) \ sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) \ sshdh.$(OBJEXT) sshdss.$(OBJEXT) sshecc.$(OBJEXT) \ sshgssc.$(OBJEXT) sshhmac.$(OBJEXT) sshmac.$(OBJEXT) \ sshmd5.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) \ - sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshshare.$(OBJEXT) \ - sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) \ + sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ + sshshare.$(OBJEXT) sshutils.$(OBJEXT) sshverstring.$(OBJEXT) \ + sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) supdup.$(OBJEXT) \ telnet.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ tree234.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ - unix/uxagentc.$(OBJEXT) unix/uxcons.$(OBJEXT) \ - unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ - unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ - unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ - unix/uxpeer.$(OBJEXT) unix/uxplink.$(OBJEXT) \ - unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ - unix/uxsel.$(OBJEXT) unix/uxser.$(OBJEXT) \ - unix/uxshare.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ - unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ - wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) -plink_OBJECTS = $(am_plink_OBJECTS) -plink_DEPENDENCIES = libversion.a -am_pscp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ - be_ssh.$(OBJEXT) callback.$(OBJEXT) cmdline.$(OBJEXT) \ - conf.$(OBJEXT) cproxy.$(OBJEXT) ecc.$(OBJEXT) \ - errsock.$(OBJEXT) logging.$(OBJEXT) mainchan.$(OBJEXT) \ - marshal.$(OBJEXT) memory.$(OBJEXT) misc.$(OBJEXT) \ - mpint.$(OBJEXT) nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ - pinger.$(OBJEXT) portfwd.$(OBJEXT) proxy.$(OBJEXT) \ - pscp.$(OBJEXT) psftpcommon.$(OBJEXT) settings.$(OBJEXT) \ - sftp.$(OBJEXT) sftpcommon.$(OBJEXT) ssh.$(OBJEXT) \ - ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ - ssh1connection-client.$(OBJEXT) ssh1connection.$(OBJEXT) \ - ssh1login.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ - ssh2censor.$(OBJEXT) ssh2connection-client.$(OBJEXT) \ - ssh2connection.$(OBJEXT) ssh2kex-client.$(OBJEXT) \ - ssh2transhk.$(OBJEXT) ssh2transport.$(OBJEXT) \ - ssh2userauth.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ - sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) sshccp.$(OBJEXT) \ - sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ - sshdes.$(OBJEXT) sshdh.$(OBJEXT) sshdss.$(OBJEXT) \ - sshecc.$(OBJEXT) sshgssc.$(OBJEXT) sshhmac.$(OBJEXT) \ - sshmac.$(OBJEXT) sshmd5.$(OBJEXT) sshprng.$(OBJEXT) \ - sshpubk.$(OBJEXT) sshrand.$(OBJEXT) sshrsa.$(OBJEXT) \ - sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) sshsha.$(OBJEXT) \ - sshshare.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ - stripctrl.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ - tree234.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ + unix/uxagentc.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ unix/uxcons.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ unix/uxgss.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ unix/uxnet.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ - unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ - unix/uxsel.$(OBJEXT) unix/uxsftp.$(OBJEXT) \ - unix/uxshare.$(OBJEXT) unix/uxstore.$(OBJEXT) \ + unix/uxplink.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ + unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ + unix/uxser.$(OBJEXT) unix/uxshare.$(OBJEXT) \ + unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) \ wildcard.$(OBJEXT) x11fwd.$(OBJEXT) +plink_OBJECTS = $(am_plink_OBJECTS) +plink_DEPENDENCIES = libversion.a +am_pscp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ + be_ssh.$(OBJEXT) callback.$(OBJEXT) clicons.$(OBJEXT) \ + cmdline.$(OBJEXT) conf.$(OBJEXT) console.$(OBJEXT) \ + cproxy.$(OBJEXT) ecc.$(OBJEXT) errsock.$(OBJEXT) \ + logging.$(OBJEXT) mainchan.$(OBJEXT) marshal.$(OBJEXT) \ + memory.$(OBJEXT) misc.$(OBJEXT) mpint.$(OBJEXT) \ + nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ + portfwd.$(OBJEXT) proxy.$(OBJEXT) pscp.$(OBJEXT) \ + psftpcommon.$(OBJEXT) settings.$(OBJEXT) sftp.$(OBJEXT) \ + sftpcommon.$(OBJEXT) ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ + ssh1censor.$(OBJEXT) ssh1connection-client.$(OBJEXT) \ + ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ + ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ + ssh2connection-client.$(OBJEXT) ssh2connection.$(OBJEXT) \ + ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ + ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ + sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ + sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ + sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ + sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ + sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ + sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ + sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ + sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ + sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshshare.$(OBJEXT) \ + sshutils.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ + stripctrl.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ + tree234.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ + unix/uxcliloop.$(OBJEXT) unix/uxcons.$(OBJEXT) \ + unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ + unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ + unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ + unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ + unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ + unix/uxsftp.$(OBJEXT) unix/uxshare.$(OBJEXT) \ + unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ + wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) pscp_OBJECTS = $(am_pscp_OBJECTS) pscp_DEPENDENCIES = libversion.a am_psftp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ - be_ssh.$(OBJEXT) callback.$(OBJEXT) cmdline.$(OBJEXT) \ - conf.$(OBJEXT) cproxy.$(OBJEXT) ecc.$(OBJEXT) \ - errsock.$(OBJEXT) logging.$(OBJEXT) mainchan.$(OBJEXT) \ - marshal.$(OBJEXT) memory.$(OBJEXT) misc.$(OBJEXT) \ - mpint.$(OBJEXT) nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ - pinger.$(OBJEXT) portfwd.$(OBJEXT) proxy.$(OBJEXT) \ - psftp.$(OBJEXT) psftpcommon.$(OBJEXT) settings.$(OBJEXT) \ - sftp.$(OBJEXT) sftpcommon.$(OBJEXT) ssh.$(OBJEXT) \ - ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ - ssh1connection-client.$(OBJEXT) ssh1connection.$(OBJEXT) \ - ssh1login.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ - ssh2censor.$(OBJEXT) ssh2connection-client.$(OBJEXT) \ - ssh2connection.$(OBJEXT) ssh2kex-client.$(OBJEXT) \ - ssh2transhk.$(OBJEXT) ssh2transport.$(OBJEXT) \ - ssh2userauth.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ - sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) sshccp.$(OBJEXT) \ - sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ - sshdes.$(OBJEXT) sshdh.$(OBJEXT) sshdss.$(OBJEXT) \ - sshecc.$(OBJEXT) sshgssc.$(OBJEXT) sshhmac.$(OBJEXT) \ - sshmac.$(OBJEXT) sshmd5.$(OBJEXT) sshprng.$(OBJEXT) \ - sshpubk.$(OBJEXT) sshrand.$(OBJEXT) sshrsa.$(OBJEXT) \ - sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) sshsha.$(OBJEXT) \ - sshshare.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ + be_ssh.$(OBJEXT) callback.$(OBJEXT) clicons.$(OBJEXT) \ + cmdline.$(OBJEXT) conf.$(OBJEXT) console.$(OBJEXT) \ + cproxy.$(OBJEXT) ecc.$(OBJEXT) errsock.$(OBJEXT) \ + logging.$(OBJEXT) mainchan.$(OBJEXT) marshal.$(OBJEXT) \ + memory.$(OBJEXT) misc.$(OBJEXT) mpint.$(OBJEXT) \ + nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ + portfwd.$(OBJEXT) proxy.$(OBJEXT) psftp.$(OBJEXT) \ + psftpcommon.$(OBJEXT) settings.$(OBJEXT) sftp.$(OBJEXT) \ + sftpcommon.$(OBJEXT) ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ + ssh1censor.$(OBJEXT) ssh1connection-client.$(OBJEXT) \ + ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ + ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ + ssh2connection-client.$(OBJEXT) ssh2connection.$(OBJEXT) \ + ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ + ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ + sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ + sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ + sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ + sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ + sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ + sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ + sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ + sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ + sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshshare.$(OBJEXT) \ + sshutils.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ stripctrl.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ tree234.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ + unix/uxcliloop.$(OBJEXT) unix/uxcons.$(OBJEXT) \ + unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ + unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ + unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ + unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ + unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ + unix/uxsftp.$(OBJEXT) unix/uxshare.$(OBJEXT) \ + unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ + wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) +psftp_OBJECTS = $(am_psftp_OBJECTS) +psftp_DEPENDENCIES = libversion.a +am_psocks_OBJECTS = be_misc.$(OBJEXT) callback.$(OBJEXT) \ + conf.$(OBJEXT) console.$(OBJEXT) errsock.$(OBJEXT) \ + logging.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ + misc.$(OBJEXT) nocproxy.$(OBJEXT) norand.$(OBJEXT) \ + portfwd.$(OBJEXT) proxy.$(OBJEXT) psocks.$(OBJEXT) \ + sshutils.$(OBJEXT) stripctrl.$(OBJEXT) time.$(OBJEXT) \ + timing.$(OBJEXT) tree234.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ unix/uxcons.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ - unix/uxgss.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ + unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ + unix/uxnogtk.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ + unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ + unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ + unix/uxsocks.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) +psocks_OBJECTS = $(am_psocks_OBJECTS) +psocks_DEPENDENCIES = libversion.a +am_psusan_OBJECTS = be_misc.$(OBJEXT) be_none.$(OBJEXT) \ + callback.$(OBJEXT) conf.$(OBJEXT) cproxy.$(OBJEXT) \ + ecc.$(OBJEXT) errsock.$(OBJEXT) logging.$(OBJEXT) \ + marshal.$(OBJEXT) memory.$(OBJEXT) millerrabin.$(OBJEXT) \ + misc.$(OBJEXT) mpint.$(OBJEXT) mpunsafe.$(OBJEXT) \ + nogss.$(OBJEXT) nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ + pockle.$(OBJEXT) portfwd.$(OBJEXT) primecandidate.$(OBJEXT) \ + proxy.$(OBJEXT) scpserver.$(OBJEXT) sesschan.$(OBJEXT) \ + settings.$(OBJEXT) sftpcommon.$(OBJEXT) sftpserver.$(OBJEXT) \ + smallprimes.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ + ssh1connection-server.$(OBJEXT) ssh1connection.$(OBJEXT) \ + ssh1login-server.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) \ + ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ + ssh2connection-server.$(OBJEXT) ssh2connection.$(OBJEXT) \ + ssh2kex-server.$(OBJEXT) ssh2transhk.$(OBJEXT) \ + ssh2transport.$(OBJEXT) ssh2userauth-server.$(OBJEXT) \ + sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ + sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ + sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ + sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ + sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ + sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ + sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ + sshrand.$(OBJEXT) sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) \ + sshserver.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ + sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshutils.$(OBJEXT) \ + sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) \ + time.$(OBJEXT) timing.$(OBJEXT) tree234.$(OBJEXT) \ + unix/procnet.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ + unix/uxagentsock.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ + unix/uxfdsock.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ unix/uxnet.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ - unix/uxsel.$(OBJEXT) unix/uxsftp.$(OBJEXT) \ - unix/uxshare.$(OBJEXT) unix/uxstore.$(OBJEXT) \ + unix/uxpsusan.$(OBJEXT) unix/uxpty.$(OBJEXT) \ + unix/uxsel.$(OBJEXT) unix/uxsftpserver.$(OBJEXT) \ + unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) \ wildcard.$(OBJEXT) x11fwd.$(OBJEXT) -psftp_OBJECTS = $(am_psftp_OBJECTS) -psftp_DEPENDENCIES = libversion.a +psusan_OBJECTS = $(am_psusan_OBJECTS) +psusan_DEPENDENCIES = libversion.a am__pterm_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c cmdline.c conf.c \ config.c dialog.c ldisc.c logging.c marshal.c memory.c \ - minibidi.c misc.c miscucs.c nocproxy.c nogss.c sercfg.c \ - sessprep.c settings.c stripctrl.c terminal.c time.c timing.c \ - tree234.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ - unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c \ - unix/gtkwin.c unix/uxcfg.c unix/uxmisc.c unix/uxprint.c \ - unix/uxpterm.c unix/uxpty.c unix/uxsel.c unix/uxsignal.c \ - unix/uxstore.c unix/uxucs.c unix/x11misc.c unix/xkeysym.c \ - unix/xpmptcfg.c unix/xpmpterm.c utils.c wcwidth.c + minibidi.c misc.c miscucs.c nocproxy.c nogss.c sessprep.c \ + settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ + unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ + unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ + unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ + unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ + unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ + unix/xpmpterm.c utils.c wcwidth.c @HAVE_GTK_TRUE@am_pterm_OBJECTS = be_none.$(OBJEXT) callback.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @@ -357,22 +430,21 @@ am__pterm_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ @HAVE_GTK_TRUE@ memory.$(OBJEXT) minibidi.$(OBJEXT) \ @HAVE_GTK_TRUE@ misc.$(OBJEXT) miscucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocproxy.$(OBJEXT) nogss.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sercfg.$(OBJEXT) sessprep.$(OBJEXT) \ -@HAVE_GTK_TRUE@ settings.$(OBJEXT) stripctrl.$(OBJEXT) \ -@HAVE_GTK_TRUE@ terminal.$(OBJEXT) time.$(OBJEXT) \ -@HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmain.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/gtkwin.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxcfg.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxprint.$(OBJEXT) unix/uxpterm.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxpty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxucs.$(OBJEXT) unix/x11misc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmptcfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xpmpterm.$(OBJEXT) utils.$(OBJEXT) \ -@HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) +@HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ +@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) terminal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ +@HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkmain.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxprint.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpterm.$(OBJEXT) unix/uxpty.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xpmptcfg.$(OBJEXT) unix/xpmpterm.$(OBJEXT) \ +@HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) pterm_OBJECTS = $(am_pterm_OBJECTS) @HAVE_GTK_TRUE@pterm_DEPENDENCIES = libversion.a $(am__DEPENDENCIES_1) am__ptermapp_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ @@ -380,14 +452,14 @@ am__ptermapp_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c conf.c config.c \ dialog.c ldisc.c logging.c marshal.c memory.c minibidi.c \ - misc.c miscucs.c nocmdline.c nocproxy.c nogss.c sercfg.c \ - sessprep.c settings.c stripctrl.c terminal.c time.c timing.c \ - tree234.c unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c \ - unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c \ - unix/gtkwin.c unix/uxcfg.c unix/uxmisc.c unix/uxprint.c \ - unix/uxpterm.c unix/uxpty.c unix/uxsel.c unix/uxsignal.c \ - unix/uxstore.c unix/uxucs.c unix/x11misc.c unix/xkeysym.c \ - unix/xpmptcfg.c unix/xpmpterm.c utils.c wcwidth.c + misc.c miscucs.c nocmdline.c nocproxy.c nogss.c sessprep.c \ + settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ + unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ + unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c unix/gtkwin.c \ + unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ + unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ + unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ + unix/xpmpterm.c utils.c wcwidth.c @HAVE_GTK_TRUE@am_ptermapp_OBJECTS = be_none.$(OBJEXT) \ @HAVE_GTK_TRUE@ callback.$(OBJEXT) charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @@ -404,22 +476,21 @@ am__ptermapp_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ @HAVE_GTK_TRUE@ minibidi.$(OBJEXT) misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ miscucs.$(OBJEXT) nocmdline.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocproxy.$(OBJEXT) nogss.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sercfg.$(OBJEXT) sessprep.$(OBJEXT) \ -@HAVE_GTK_TRUE@ settings.$(OBJEXT) stripctrl.$(OBJEXT) \ -@HAVE_GTK_TRUE@ terminal.$(OBJEXT) time.$(OBJEXT) \ -@HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkapp.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/gtkwin.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxcfg.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxprint.$(OBJEXT) unix/uxpterm.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxpty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxucs.$(OBJEXT) unix/x11misc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmptcfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xpmpterm.$(OBJEXT) utils.$(OBJEXT) \ -@HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) +@HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ +@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) terminal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ +@HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkapp.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxprint.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpterm.$(OBJEXT) unix/uxpty.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xpmptcfg.$(OBJEXT) unix/xpmpterm.$(OBJEXT) \ +@HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) ptermapp_OBJECTS = $(am_ptermapp_OBJECTS) @HAVE_GTK_TRUE@ptermapp_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) @@ -431,26 +502,26 @@ am__putty_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ cproxy.c dialog.c ecc.c errsock.c ldisc.c logging.c mainchan.c \ marshal.c memory.c minibidi.c misc.c miscucs.c mpint.c \ nullplug.c pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ - sercfg.c sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ + sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection.c ssh1login.c \ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c ssh2connection-client.c \ ssh2connection.c ssh2kex-client.c ssh2transhk.c \ - ssh2transport.c ssh2userauth.c sshaes.c ssharcf.c \ - sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c sshcrc.c \ - sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c sshgssc.c \ - sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c sshrand.c \ - sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c telnet.c terminal.c \ - time.c timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ - unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ - unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ - unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ - unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ - unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ - unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ - unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ - unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ - x11fwd.c + ssh2transport.c ssh2userauth.c sshaes.c ssharcf.c sshargon2.c \ + sshauxcrypt.c sshblake2.c sshblowf.c sshccp.c sshcommon.c \ + sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ + sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ + sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + sshshare.c sshutils.c sshverstring.c sshzlib.c stripctrl.c \ + supdup.c telnet.c terminal.c time.c timing.c tree234.c \ + unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ + unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ + unix/ux_x11.c unix/uxagentc.c unix/uxcfg.c unix/uxfdsock.c \ + unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ + unix/uxpeer.c unix/uxpoll.c unix/uxprint.c unix/uxproxy.c \ + unix/uxputty.c unix/uxsel.c unix/uxser.c unix/uxshare.c \ + unix/uxsignal.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ + unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c unix/xpmputty.c \ + utils.c wcwidth.c wildcard.c x11fwd.c @HAVE_GTK_TRUE@am_putty_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_all_s.$(OBJEXT) be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ callback.$(OBJEXT) charset/fromucs.$(OBJEXT) \ @@ -472,9 +543,9 @@ am__putty_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @HAVE_GTK_TRUE@ nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ @HAVE_GTK_TRUE@ pinger.$(OBJEXT) portfwd.$(OBJEXT) \ @HAVE_GTK_TRUE@ proxy.$(OBJEXT) raw.$(OBJEXT) rlogin.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sercfg.$(OBJEXT) sessprep.$(OBJEXT) \ -@HAVE_GTK_TRUE@ settings.$(OBJEXT) ssh.$(OBJEXT) \ -@HAVE_GTK_TRUE@ ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ +@HAVE_GTK_TRUE@ ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ +@HAVE_GTK_TRUE@ ssh1censor.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection-client.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ @@ -484,7 +555,8 @@ am__putty_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @HAVE_GTK_TRUE@ ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ @@ -494,29 +566,31 @@ am__putty_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @HAVE_GTK_TRUE@ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshshare.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshshare.$(OBJEXT) sshutils.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ -@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) telnet.$(OBJEXT) \ -@HAVE_GTK_TRUE@ terminal.$(OBJEXT) time.$(OBJEXT) \ -@HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmain.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/gtkwin.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/ux_x11.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxcfg.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxgss.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxnet.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxprint.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxputty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxser.$(OBJEXT) unix/uxshare.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxucs.$(OBJEXT) unix/uxutils.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xpmpucfg.$(OBJEXT) unix/xpmputty.$(OBJEXT) \ -@HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) \ -@HAVE_GTK_TRUE@ wildcard.$(OBJEXT) x11fwd.$(OBJEXT) +@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) supdup.$(OBJEXT) \ +@HAVE_GTK_TRUE@ telnet.$(OBJEXT) terminal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ +@HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkmain.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxagentc.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxprint.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxproxy.$(OBJEXT) unix/uxputty.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxser.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxshare.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) unix/x11misc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmpucfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xpmputty.$(OBJEXT) utils.$(OBJEXT) \ +@HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) \ +@HAVE_GTK_TRUE@ x11fwd.$(OBJEXT) putty_OBJECTS = $(am_putty_OBJECTS) @HAVE_GTK_TRUE@putty_DEPENDENCIES = libversion.a $(am__DEPENDENCIES_1) am__puttyapp_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @@ -527,26 +601,26 @@ am__puttyapp_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ dialog.c ecc.c errsock.c ldisc.c logging.c mainchan.c \ marshal.c memory.c minibidi.c misc.c miscucs.c mpint.c \ nocmdline.c nullplug.c pgssapi.c pinger.c portfwd.c proxy.c \ - raw.c rlogin.c sercfg.c sessprep.c settings.c ssh.c ssh1bpp.c \ + raw.c rlogin.c sessprep.c settings.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c telnet.c terminal.c \ - time.c timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ - unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ - unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ - unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ - unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ - unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ - unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ - unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ - unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ - x11fwd.c + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ + timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c \ + unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c \ + unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c unix/uxcfg.c \ + unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ + unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxprint.c \ + unix/uxproxy.c unix/uxputty.c unix/uxsel.c unix/uxser.c \ + unix/uxshare.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ + unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ + unix/xpmputty.c utils.c wcwidth.c wildcard.c x11fwd.c @HAVE_GTK_TRUE@am_puttyapp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_all_s.$(OBJEXT) be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ callback.$(OBJEXT) charset/fromucs.$(OBJEXT) \ @@ -568,10 +642,9 @@ am__puttyapp_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @HAVE_GTK_TRUE@ nocmdline.$(OBJEXT) nullplug.$(OBJEXT) \ @HAVE_GTK_TRUE@ pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ @HAVE_GTK_TRUE@ portfwd.$(OBJEXT) proxy.$(OBJEXT) raw.$(OBJEXT) \ -@HAVE_GTK_TRUE@ rlogin.$(OBJEXT) sercfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ -@HAVE_GTK_TRUE@ ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ -@HAVE_GTK_TRUE@ ssh1censor.$(OBJEXT) \ +@HAVE_GTK_TRUE@ rlogin.$(OBJEXT) sessprep.$(OBJEXT) \ +@HAVE_GTK_TRUE@ settings.$(OBJEXT) ssh.$(OBJEXT) \ +@HAVE_GTK_TRUE@ ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection-client.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ @@ -581,7 +654,8 @@ am__puttyapp_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @HAVE_GTK_TRUE@ ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ @@ -591,47 +665,53 @@ am__puttyapp_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ @HAVE_GTK_TRUE@ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshshare.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ +@HAVE_GTK_TRUE@ sshshare.$(OBJEXT) sshutils.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ -@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) telnet.$(OBJEXT) \ -@HAVE_GTK_TRUE@ terminal.$(OBJEXT) time.$(OBJEXT) \ -@HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkapp.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/gtkwin.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/ux_x11.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxcfg.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxgss.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxnet.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxprint.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxputty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxser.$(OBJEXT) unix/uxshare.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxucs.$(OBJEXT) unix/uxutils.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xpmpucfg.$(OBJEXT) unix/xpmputty.$(OBJEXT) \ -@HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) \ -@HAVE_GTK_TRUE@ wildcard.$(OBJEXT) x11fwd.$(OBJEXT) +@HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) supdup.$(OBJEXT) \ +@HAVE_GTK_TRUE@ telnet.$(OBJEXT) terminal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ +@HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkapp.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxagentc.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxprint.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxproxy.$(OBJEXT) unix/uxputty.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxser.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxshare.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) unix/x11misc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmpucfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xpmputty.$(OBJEXT) utils.$(OBJEXT) \ +@HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) \ +@HAVE_GTK_TRUE@ x11fwd.$(OBJEXT) puttyapp_OBJECTS = $(am_puttyapp_OBJECTS) @HAVE_GTK_TRUE@puttyapp_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) -am_puttygen_OBJECTS = cmdgen.$(OBJEXT) conf.$(OBJEXT) ecc.$(OBJEXT) \ - import.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ - misc.$(OBJEXT) mpint.$(OBJEXT) notiming.$(OBJEXT) \ - sshaes.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshbcrypt.$(OBJEXT) \ +am_puttygen_OBJECTS = cmdgen.$(OBJEXT) conf.$(OBJEXT) \ + console.$(OBJEXT) ecc.$(OBJEXT) import.$(OBJEXT) \ + marshal.$(OBJEXT) memory.$(OBJEXT) millerrabin.$(OBJEXT) \ + misc.$(OBJEXT) mpint.$(OBJEXT) mpunsafe.$(OBJEXT) \ + notiming.$(OBJEXT) pockle.$(OBJEXT) primecandidate.$(OBJEXT) \ + smallprimes.$(OBJEXT) sshaes.$(OBJEXT) sshargon2.$(OBJEXT) \ + sshauxcrypt.$(OBJEXT) sshbcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshdes.$(OBJEXT) sshdss.$(OBJEXT) \ sshdssg.$(OBJEXT) sshecc.$(OBJEXT) sshecdsag.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) sshprime.$(OBJEXT) \ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) sshsh256.$(OBJEXT) \ - sshsh512.$(OBJEXT) sshsha.$(OBJEXT) stripctrl.$(OBJEXT) \ - time.$(OBJEXT) tree234.$(OBJEXT) unix/uxcons.$(OBJEXT) \ - unix/uxgen.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ - unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ - unix/uxpoll.$(OBJEXT) unix/uxstore.$(OBJEXT) \ - unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) + sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ + stripctrl.$(OBJEXT) time.$(OBJEXT) tree234.$(OBJEXT) \ + unix/uxcons.$(OBJEXT) unix/uxgen.$(OBJEXT) \ + unix/uxmisc.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ + unix/uxnoise.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ + unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ + wcwidth.$(OBJEXT) puttygen_OBJECTS = $(am_puttygen_OBJECTS) puttygen_DEPENDENCIES = libversion.a am__puttytel_SOURCES_DIST = be_misc.c be_nos_s.c callback.c \ @@ -640,16 +720,16 @@ am__puttytel_SOURCES_DIST = be_misc.c be_nos_s.c callback.c \ charset/slookup.c charset/toucs.c charset/utf8.c \ charset/xenc.c cmdline.c conf.c config.c dialog.c errsock.c \ ldisc.c logging.c marshal.c memory.c minibidi.c misc.c \ - miscucs.c nocproxy.c nogss.c pinger.c proxy.c raw.c rlogin.c \ - sercfg.c sessprep.c settings.c stripctrl.c telnet.c terminal.c \ - time.c timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ - unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ - unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c \ - unix/uxmisc.c unix/uxnet.c unix/uxpeer.c unix/uxpoll.c \ - unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ - unix/uxser.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ - unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ - unix/xpmputty.c utils.c wcwidth.c + miscucs.c nocproxy.c nogss.c norand.c pinger.c proxy.c raw.c \ + rlogin.c sessprep.c settings.c stripctrl.c supdup.c telnet.c \ + terminal.c time.c timing.c tree234.c unix/gtkcfg.c \ + unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ + unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c \ + unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxpeer.c \ + unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxputty.c \ + unix/uxsel.c unix/uxser.c unix/uxsignal.c unix/uxstore.c \ + unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ + unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c @HAVE_GTK_TRUE@am_puttytel_OBJECTS = be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_nos_s.$(OBJEXT) callback.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/fromucs.$(OBJEXT) \ @@ -667,51 +747,58 @@ am__puttytel_SOURCES_DIST = be_misc.c be_nos_s.c callback.c \ @HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ @HAVE_GTK_TRUE@ minibidi.$(OBJEXT) misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ miscucs.$(OBJEXT) nocproxy.$(OBJEXT) \ -@HAVE_GTK_TRUE@ nogss.$(OBJEXT) pinger.$(OBJEXT) \ -@HAVE_GTK_TRUE@ proxy.$(OBJEXT) raw.$(OBJEXT) rlogin.$(OBJEXT) \ -@HAVE_GTK_TRUE@ sercfg.$(OBJEXT) sessprep.$(OBJEXT) \ +@HAVE_GTK_TRUE@ nogss.$(OBJEXT) norand.$(OBJEXT) \ +@HAVE_GTK_TRUE@ pinger.$(OBJEXT) proxy.$(OBJEXT) raw.$(OBJEXT) \ +@HAVE_GTK_TRUE@ rlogin.$(OBJEXT) sessprep.$(OBJEXT) \ @HAVE_GTK_TRUE@ settings.$(OBJEXT) stripctrl.$(OBJEXT) \ -@HAVE_GTK_TRUE@ telnet.$(OBJEXT) terminal.$(OBJEXT) \ -@HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ -@HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkmain.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxnet.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxprint.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxproxy.$(OBJEXT) unix/uxputty.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxser.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/uxucs.$(OBJEXT) unix/uxutils.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ -@HAVE_GTK_TRUE@ unix/xpmpucfg.$(OBJEXT) unix/xpmputty.$(OBJEXT) \ -@HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) +@HAVE_GTK_TRUE@ supdup.$(OBJEXT) telnet.$(OBJEXT) \ +@HAVE_GTK_TRUE@ terminal.$(OBJEXT) time.$(OBJEXT) \ +@HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmain.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/gtkwin.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxcfg.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxprint.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxputty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxser.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) unix/x11misc.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmpucfg.$(OBJEXT) \ +@HAVE_GTK_TRUE@ unix/xpmputty.$(OBJEXT) utils.$(OBJEXT) \ +@HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) puttytel_OBJECTS = $(am_puttytel_OBJECTS) @HAVE_GTK_TRUE@puttytel_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) am_testcrypt_OBJECTS = ecc.$(OBJEXT) marshal.$(OBJEXT) \ - memory.$(OBJEXT) mpint.$(OBJEXT) sshaes.$(OBJEXT) \ - ssharcf.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) \ - sshccp.$(OBJEXT) sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ - sshdes.$(OBJEXT) sshdh.$(OBJEXT) sshdss.$(OBJEXT) \ - sshecc.$(OBJEXT) sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) \ - sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshrsa.$(OBJEXT) \ - sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) sshsha.$(OBJEXT) \ + memory.$(OBJEXT) millerrabin.$(OBJEXT) mpint.$(OBJEXT) \ + mpunsafe.$(OBJEXT) pockle.$(OBJEXT) primecandidate.$(OBJEXT) \ + smallprimes.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ + sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ + sshblowf.$(OBJEXT) sshccp.$(OBJEXT) sshcrc.$(OBJEXT) \ + sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ + sshdss.$(OBJEXT) sshdssg.$(OBJEXT) sshecc.$(OBJEXT) \ + sshecdsag.$(OBJEXT) sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) \ + sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ + sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) sshsh256.$(OBJEXT) \ + sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ testcrypt.$(OBJEXT) tree234.$(OBJEXT) unix/uxutils.$(OBJEXT) \ utils.$(OBJEXT) testcrypt_OBJECTS = $(am_testcrypt_OBJECTS) testcrypt_LDADD = $(LDADD) am_testsc_OBJECTS = ecc.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ mpint.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ - sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) sshccp.$(OBJEXT) \ - sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) \ - sshdh.$(OBJEXT) sshdss.$(OBJEXT) sshecc.$(OBJEXT) \ - sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ + sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ + sshblowf.$(OBJEXT) sshccp.$(OBJEXT) sshcrc.$(OBJEXT) \ + sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ + sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshhmac.$(OBJEXT) \ + sshmac.$(OBJEXT) sshmd5.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ - sshsha.$(OBJEXT) testsc.$(OBJEXT) tree234.$(OBJEXT) \ - unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wildcard.$(OBJEXT) + sshsha.$(OBJEXT) sshsha3.$(OBJEXT) testsc.$(OBJEXT) \ + tree234.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ + wildcard.$(OBJEXT) testsc_OBJECTS = $(am_testsc_OBJECTS) testsc_LDADD = $(LDADD) am_testzlib_OBJECTS = marshal.$(OBJEXT) memory.$(OBJEXT) \ @@ -721,18 +808,21 @@ testzlib_LDADD = $(LDADD) am_uppity_OBJECTS = be_misc.$(OBJEXT) be_none.$(OBJEXT) \ callback.$(OBJEXT) conf.$(OBJEXT) cproxy.$(OBJEXT) \ ecc.$(OBJEXT) errsock.$(OBJEXT) logging.$(OBJEXT) \ - marshal.$(OBJEXT) memory.$(OBJEXT) misc.$(OBJEXT) \ - mpint.$(OBJEXT) nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ - portfwd.$(OBJEXT) proxy.$(OBJEXT) scpserver.$(OBJEXT) \ - sesschan.$(OBJEXT) settings.$(OBJEXT) sftpcommon.$(OBJEXT) \ - sftpserver.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ + marshal.$(OBJEXT) memory.$(OBJEXT) millerrabin.$(OBJEXT) \ + misc.$(OBJEXT) mpint.$(OBJEXT) mpunsafe.$(OBJEXT) \ + nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pockle.$(OBJEXT) \ + portfwd.$(OBJEXT) primecandidate.$(OBJEXT) proxy.$(OBJEXT) \ + scpserver.$(OBJEXT) sesschan.$(OBJEXT) settings.$(OBJEXT) \ + sftpcommon.$(OBJEXT) sftpserver.$(OBJEXT) \ + smallprimes.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ ssh1connection-server.$(OBJEXT) ssh1connection.$(OBJEXT) \ - ssh1login-server.$(OBJEXT) ssh2bpp.$(OBJEXT) \ - ssh2censor.$(OBJEXT) ssh2connection-server.$(OBJEXT) \ - ssh2connection.$(OBJEXT) ssh2kex-server.$(OBJEXT) \ - ssh2transhk.$(OBJEXT) ssh2transport.$(OBJEXT) \ - ssh2userauth-server.$(OBJEXT) sshaes.$(OBJEXT) \ - ssharcf.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblowf.$(OBJEXT) \ + ssh1login-server.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) \ + ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ + ssh2connection-server.$(OBJEXT) ssh2connection.$(OBJEXT) \ + ssh2kex-server.$(OBJEXT) ssh2transhk.$(OBJEXT) \ + ssh2transport.$(OBJEXT) ssh2userauth-server.$(OBJEXT) \ + sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ + sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ @@ -740,19 +830,20 @@ am_uppity_OBJECTS = be_misc.$(OBJEXT) be_none.$(OBJEXT) \ sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) \ sshserver.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ - sshsha.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ - stripctrl.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ - tree234.$(OBJEXT) unix/procnet.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ - unix/uxagentsock.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ - unix/uxgss.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ - unix/uxnet.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ - unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ - unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ - unix/uxpty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ - unix/uxserver.$(OBJEXT) unix/uxsftpserver.$(OBJEXT) \ - unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ - unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) \ - wildcard.$(OBJEXT) x11fwd.$(OBJEXT) + sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshutils.$(OBJEXT) \ + sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) \ + time.$(OBJEXT) timing.$(OBJEXT) tree234.$(OBJEXT) \ + unix/procnet.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ + unix/uxagentsock.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ + unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ + unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ + unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ + unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ + unix/uxproxy.$(OBJEXT) unix/uxpty.$(OBJEXT) \ + unix/uxsel.$(OBJEXT) unix/uxserver.$(OBJEXT) \ + unix/uxsftpserver.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ + unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ + wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) uppity_OBJECTS = $(am_uppity_OBJECTS) uppity_DEPENDENCIES = libversion.a SCRIPTS = $(noinst_SCRIPTS) @@ -770,7 +861,105 @@ am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/agentf.Po ./$(DEPDIR)/aqsync.Po \ + ./$(DEPDIR)/be_all_s.Po ./$(DEPDIR)/be_misc.Po \ + ./$(DEPDIR)/be_none.Po ./$(DEPDIR)/be_nos_s.Po \ + ./$(DEPDIR)/be_ssh.Po ./$(DEPDIR)/callback.Po \ + ./$(DEPDIR)/cgtest.Po ./$(DEPDIR)/clicons.Po \ + ./$(DEPDIR)/cmdgen.Po ./$(DEPDIR)/cmdline.Po \ + ./$(DEPDIR)/conf.Po ./$(DEPDIR)/config.Po \ + ./$(DEPDIR)/console.Po ./$(DEPDIR)/cproxy.Po \ + ./$(DEPDIR)/dialog.Po ./$(DEPDIR)/ecc.Po \ + ./$(DEPDIR)/errsock.Po ./$(DEPDIR)/fuzzterm.Po \ + ./$(DEPDIR)/import.Po ./$(DEPDIR)/ldisc.Po \ + ./$(DEPDIR)/libversion_a-version.Po ./$(DEPDIR)/logging.Po \ + ./$(DEPDIR)/mainchan.Po ./$(DEPDIR)/marshal.Po \ + ./$(DEPDIR)/memory.Po ./$(DEPDIR)/millerrabin.Po \ + ./$(DEPDIR)/minibidi.Po ./$(DEPDIR)/misc.Po \ + ./$(DEPDIR)/miscucs.Po ./$(DEPDIR)/mpint.Po \ + ./$(DEPDIR)/mpunsafe.Po ./$(DEPDIR)/nocmdline.Po \ + ./$(DEPDIR)/nocproxy.Po ./$(DEPDIR)/nogss.Po \ + ./$(DEPDIR)/norand.Po ./$(DEPDIR)/noterm.Po \ + ./$(DEPDIR)/notiming.Po ./$(DEPDIR)/nullplug.Po \ + ./$(DEPDIR)/pageant.Po ./$(DEPDIR)/pgssapi.Po \ + ./$(DEPDIR)/pinger.Po ./$(DEPDIR)/pockle.Po \ + ./$(DEPDIR)/portfwd.Po ./$(DEPDIR)/primecandidate.Po \ + ./$(DEPDIR)/proxy.Po ./$(DEPDIR)/pscp.Po ./$(DEPDIR)/psftp.Po \ + ./$(DEPDIR)/psftpcommon.Po ./$(DEPDIR)/psocks.Po \ + ./$(DEPDIR)/raw.Po ./$(DEPDIR)/rlogin.Po \ + ./$(DEPDIR)/scpserver.Po ./$(DEPDIR)/sesschan.Po \ + ./$(DEPDIR)/sessprep.Po ./$(DEPDIR)/settings.Po \ + ./$(DEPDIR)/sftp.Po ./$(DEPDIR)/sftpcommon.Po \ + ./$(DEPDIR)/sftpserver.Po ./$(DEPDIR)/smallprimes.Po \ + ./$(DEPDIR)/ssh.Po ./$(DEPDIR)/ssh1bpp.Po \ + ./$(DEPDIR)/ssh1censor.Po ./$(DEPDIR)/ssh1connection-client.Po \ + ./$(DEPDIR)/ssh1connection-server.Po \ + ./$(DEPDIR)/ssh1connection.Po ./$(DEPDIR)/ssh1login-server.Po \ + ./$(DEPDIR)/ssh1login.Po ./$(DEPDIR)/ssh2bpp-bare.Po \ + ./$(DEPDIR)/ssh2bpp.Po ./$(DEPDIR)/ssh2censor.Po \ + ./$(DEPDIR)/ssh2connection-client.Po \ + ./$(DEPDIR)/ssh2connection-server.Po \ + ./$(DEPDIR)/ssh2connection.Po ./$(DEPDIR)/ssh2kex-client.Po \ + ./$(DEPDIR)/ssh2kex-server.Po ./$(DEPDIR)/ssh2transhk.Po \ + ./$(DEPDIR)/ssh2transport.Po \ + ./$(DEPDIR)/ssh2userauth-server.Po ./$(DEPDIR)/ssh2userauth.Po \ + ./$(DEPDIR)/sshaes.Po ./$(DEPDIR)/ssharcf.Po \ + ./$(DEPDIR)/sshargon2.Po ./$(DEPDIR)/sshauxcrypt.Po \ + ./$(DEPDIR)/sshbcrypt.Po ./$(DEPDIR)/sshblake2.Po \ + ./$(DEPDIR)/sshblowf.Po ./$(DEPDIR)/sshccp.Po \ + ./$(DEPDIR)/sshcommon.Po ./$(DEPDIR)/sshcrc.Po \ + ./$(DEPDIR)/sshcrcda.Po ./$(DEPDIR)/sshdes.Po \ + ./$(DEPDIR)/sshdh.Po ./$(DEPDIR)/sshdss.Po \ + ./$(DEPDIR)/sshdssg.Po ./$(DEPDIR)/sshecc.Po \ + ./$(DEPDIR)/sshecdsag.Po ./$(DEPDIR)/sshgssc.Po \ + ./$(DEPDIR)/sshhmac.Po ./$(DEPDIR)/sshmac.Po \ + ./$(DEPDIR)/sshmd5.Po ./$(DEPDIR)/sshprime.Po \ + ./$(DEPDIR)/sshprng.Po ./$(DEPDIR)/sshpubk.Po \ + ./$(DEPDIR)/sshrand.Po ./$(DEPDIR)/sshrsa.Po \ + ./$(DEPDIR)/sshrsag.Po ./$(DEPDIR)/sshserver.Po \ + ./$(DEPDIR)/sshsh256.Po ./$(DEPDIR)/sshsh512.Po \ + ./$(DEPDIR)/sshsha.Po ./$(DEPDIR)/sshsha3.Po \ + ./$(DEPDIR)/sshshare.Po ./$(DEPDIR)/sshutils.Po \ + ./$(DEPDIR)/sshverstring.Po ./$(DEPDIR)/sshzlib.Po \ + ./$(DEPDIR)/stripctrl.Po ./$(DEPDIR)/supdup.Po \ + ./$(DEPDIR)/telnet.Po ./$(DEPDIR)/terminal.Po \ + ./$(DEPDIR)/testcrypt.Po ./$(DEPDIR)/testsc.Po \ + ./$(DEPDIR)/testzlib.Po ./$(DEPDIR)/time.Po \ + ./$(DEPDIR)/timing.Po ./$(DEPDIR)/tree234.Po \ + ./$(DEPDIR)/utils.Po ./$(DEPDIR)/wcwidth.Po \ + ./$(DEPDIR)/wildcard.Po ./$(DEPDIR)/x11fwd.Po \ + charset/$(DEPDIR)/fromucs.Po charset/$(DEPDIR)/localenc.Po \ + charset/$(DEPDIR)/macenc.Po charset/$(DEPDIR)/mimeenc.Po \ + charset/$(DEPDIR)/sbcs.Po charset/$(DEPDIR)/sbcsdat.Po \ + charset/$(DEPDIR)/slookup.Po charset/$(DEPDIR)/toucs.Po \ + charset/$(DEPDIR)/utf8.Po charset/$(DEPDIR)/xenc.Po \ + unix/$(DEPDIR)/gtkapp.Po unix/$(DEPDIR)/gtkask.Po \ + unix/$(DEPDIR)/gtkcfg.Po unix/$(DEPDIR)/gtkcols.Po \ + unix/$(DEPDIR)/gtkcomm.Po unix/$(DEPDIR)/gtkdlg.Po \ + unix/$(DEPDIR)/gtkfont.Po unix/$(DEPDIR)/gtkmain.Po \ + unix/$(DEPDIR)/gtkmisc.Po unix/$(DEPDIR)/gtkwin.Po \ + unix/$(DEPDIR)/osxlaunch.Po unix/$(DEPDIR)/procnet.Po \ + unix/$(DEPDIR)/ux_x11.Po unix/$(DEPDIR)/uxagentc.Po \ + unix/$(DEPDIR)/uxagentsock.Po unix/$(DEPDIR)/uxcfg.Po \ + unix/$(DEPDIR)/uxcliloop.Po unix/$(DEPDIR)/uxcons.Po \ + unix/$(DEPDIR)/uxfdsock.Po unix/$(DEPDIR)/uxgen.Po \ + unix/$(DEPDIR)/uxgss.Po unix/$(DEPDIR)/uxmisc.Po \ + unix/$(DEPDIR)/uxnet.Po unix/$(DEPDIR)/uxnogtk.Po \ + unix/$(DEPDIR)/uxnoise.Po unix/$(DEPDIR)/uxpeer.Po \ + unix/$(DEPDIR)/uxpgnt.Po unix/$(DEPDIR)/uxplink.Po \ + unix/$(DEPDIR)/uxpoll.Po unix/$(DEPDIR)/uxprint.Po \ + unix/$(DEPDIR)/uxproxy.Po unix/$(DEPDIR)/uxpsusan.Po \ + unix/$(DEPDIR)/uxpterm.Po unix/$(DEPDIR)/uxpty.Po \ + unix/$(DEPDIR)/uxputty.Po unix/$(DEPDIR)/uxsel.Po \ + unix/$(DEPDIR)/uxser.Po unix/$(DEPDIR)/uxserver.Po \ + unix/$(DEPDIR)/uxsftp.Po unix/$(DEPDIR)/uxsftpserver.Po \ + unix/$(DEPDIR)/uxshare.Po unix/$(DEPDIR)/uxsignal.Po \ + unix/$(DEPDIR)/uxsocks.Po unix/$(DEPDIR)/uxstore.Po \ + unix/$(DEPDIR)/uxucs.Po unix/$(DEPDIR)/uxutils.Po \ + unix/$(DEPDIR)/x11misc.Po unix/$(DEPDIR)/xkeysym.Po \ + unix/$(DEPDIR)/xpmptcfg.Po unix/$(DEPDIR)/xpmpterm.Po \ + unix/$(DEPDIR)/xpmpucfg.Po unix/$(DEPDIR)/xpmputty.Po am__mv = mv -f AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) @@ -791,18 +980,19 @@ am__v_CCLD_1 = SOURCES = $(libversion_a_SOURCES) $(cgtest_SOURCES) \ $(fuzzterm_SOURCES) $(osxlaunch_SOURCES) $(pageant_SOURCES) \ $(plink_SOURCES) $(pscp_SOURCES) $(psftp_SOURCES) \ - $(pterm_SOURCES) $(ptermapp_SOURCES) $(putty_SOURCES) \ - $(puttyapp_SOURCES) $(puttygen_SOURCES) $(puttytel_SOURCES) \ - $(testcrypt_SOURCES) $(testsc_SOURCES) $(testzlib_SOURCES) \ - $(uppity_SOURCES) + $(psocks_SOURCES) $(psusan_SOURCES) $(pterm_SOURCES) \ + $(ptermapp_SOURCES) $(putty_SOURCES) $(puttyapp_SOURCES) \ + $(puttygen_SOURCES) $(puttytel_SOURCES) $(testcrypt_SOURCES) \ + $(testsc_SOURCES) $(testzlib_SOURCES) $(uppity_SOURCES) DIST_SOURCES = $(libversion_a_SOURCES) $(cgtest_SOURCES) \ $(fuzzterm_SOURCES) $(osxlaunch_SOURCES) \ $(am__pageant_SOURCES_DIST) $(plink_SOURCES) $(pscp_SOURCES) \ - $(psftp_SOURCES) $(am__pterm_SOURCES_DIST) \ - $(am__ptermapp_SOURCES_DIST) $(am__putty_SOURCES_DIST) \ - $(am__puttyapp_SOURCES_DIST) $(puttygen_SOURCES) \ - $(am__puttytel_SOURCES_DIST) $(testcrypt_SOURCES) \ - $(testsc_SOURCES) $(testzlib_SOURCES) $(uppity_SOURCES) + $(psftp_SOURCES) $(psocks_SOURCES) $(psusan_SOURCES) \ + $(am__pterm_SOURCES_DIST) $(am__ptermapp_SOURCES_DIST) \ + $(am__putty_SOURCES_DIST) $(am__puttyapp_SOURCES_DIST) \ + $(puttygen_SOURCES) $(am__puttytel_SOURCES_DIST) \ + $(testcrypt_SOURCES) $(testsc_SOURCES) $(testzlib_SOURCES) \ + $(uppity_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -985,55 +1175,62 @@ allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ charset/enum.c charset/fromucs.c charset/internal.h \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ - charset/toucs.c charset/utf8.c charset/xenc.c cmdgen.c \ - cmdline.c conf.c config.c cproxy.c defs.h dialog.c dialog.h \ - ecc.c ecc.h errsock.c fuzzterm.c import.c ldisc.c ldisc.h \ - licence.h logging.c mainchan.c marshal.c marshal.h memory.c \ + charset/toucs.c charset/utf8.c charset/xenc.c clicons.c \ + cmdgen.c cmdline.c conf.c config.c console.c console.h \ + cproxy.c defs.h dialog.c dialog.h ecc.c ecc.h errsock.c \ + fuzzterm.c import.c ldisc.c ldisc.h licence.h logging.c \ + mainchan.c marshal.c marshal.h memory.c millerrabin.c \ minibidi.c misc.c misc.h miscucs.c mpint.c mpint.h mpint_i.h \ - network.h nocmdline.c nocproxy.c nogss.c noshare.c noterm.c \ - notiming.c nullplug.c pageant.c pageant.h pgssapi.c \ - pgssapi.h pinger.c portfwd.c proxy.c proxy.h pscp.c psftp.c \ - psftp.h psftpcommon.c putty.h puttymem.h puttyps.h raw.c \ - rlogin.c scpserver.c sercfg.c sesschan.c sessprep.c \ - settings.c sftp.c sftp.h sftpcommon.c sftpserver.c ssh.c \ - ssh.h ssh1bpp.c ssh1censor.c ssh1connection-client.c \ - ssh1connection-server.c ssh1connection.c ssh1connection.h \ - ssh1login-server.c ssh1login.c ssh2bpp-bare.c ssh2bpp.c \ - ssh2censor.c ssh2connection-client.c ssh2connection-server.c \ + mpunsafe.c mpunsafe.h network.h nocmdline.c nocproxy.c \ + nogss.c norand.c noshare.c noterm.c notiming.c nullplug.c \ + pageant.c pageant.h pgssapi.c pgssapi.h pinger.c pockle.c \ + portfwd.c primecandidate.c proxy.c proxy.h pscp.c psftp.c \ + psftp.h psftpcommon.c psocks.c psocks.h putty.h puttymem.h \ + puttyps.h raw.c rlogin.c scpserver.c sesschan.c sessprep.c \ + settings.c sftp.c sftp.h sftpcommon.c sftpserver.c \ + smallprimes.c ssh.c ssh.h ssh1bpp.c ssh1censor.c \ + ssh1connection-client.c ssh1connection-server.c \ + ssh1connection.c ssh1connection.h ssh1login-server.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ + ssh2connection-client.c ssh2connection-server.c \ ssh2connection.c ssh2connection.h ssh2kex-client.c \ ssh2kex-server.c ssh2transhk.c ssh2transport.c \ ssh2transport.h ssh2userauth-server.c ssh2userauth.c \ - sshaes.c ssharcf.c sshauxcrypt.c sshbcrypt.c sshblowf.c \ - sshblowf.h sshbpp.h sshccp.c sshchan.h sshcommon.c sshcr.h \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshdssg.c \ - sshecc.c sshecdsag.c sshgss.h sshgssc.c sshgssc.h sshhmac.c \ - sshmac.c sshmd5.c sshppl.h sshprime.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshrsag.c sshserver.c sshserver.h \ - sshsh256.c sshsh512.c sshsha.c sshshare.c sshsignals.h \ - sshttymodes.h sshverstring.c sshzlib.c storage.h stripctrl.c \ - telnet.c terminal.c terminal.h testcrypt.c testcrypt.h \ - testsc.c testzlib.c time.c timing.c tree234.c tree234.h \ - unix/gtkapp.c unix/gtkask.c unix/gtkcfg.c unix/gtkcols.c \ - unix/gtkcols.h unix/gtkcomm.c unix/gtkcompat.h unix/gtkdlg.c \ - unix/gtkfont.c unix/gtkfont.h unix/gtkmain.c unix/gtkmisc.c \ - unix/gtkmisc.h unix/gtkwin.c unix/osxlaunch.c unix/procnet.c \ - unix/unix.h unix/ux_x11.c unix/uxagentc.c unix/uxagentsock.c \ - unix/uxcfg.c unix/uxcons.c unix/uxfdsock.c unix/uxgen.c \ + sshaes.c ssharcf.c sshargon2.c sshauxcrypt.c sshbcrypt.c \ + sshblake2.c sshblowf.c sshblowf.h sshbpp.h sshccp.c \ + sshchan.h sshcommon.c sshcr.h sshcrc.c sshcrcda.c sshdes.c \ + sshdh.c sshdss.c sshdssg.c sshecc.c sshecdsag.c sshgss.h \ + sshgssc.c sshgssc.h sshhmac.c sshkeygen.h sshmac.c sshmd5.c \ + sshppl.h sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c \ + sshrsag.c sshserver.c sshserver.h sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshsignals.h sshttymodes.h \ + sshutils.c sshverstring.c sshzlib.c storage.h stripctrl.c \ + supdup.c telnet.c terminal.c terminal.h testcrypt.c \ + testcrypt.h testsc.c testzlib.c time.c timing.c tree234.c \ + tree234.h unix/gtkapp.c unix/gtkask.c unix/gtkcfg.c \ + unix/gtkcols.c unix/gtkcols.h unix/gtkcomm.c \ + unix/gtkcompat.h unix/gtkdlg.c unix/gtkfont.c unix/gtkfont.h \ + unix/gtkmain.c unix/gtkmisc.c unix/gtkmisc.h unix/gtkwin.c \ + unix/osxlaunch.c unix/procnet.c unix/unix.h unix/ux_x11.c \ + unix/uxagentc.c unix/uxagentsock.c unix/uxcfg.c \ + unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c unix/uxgen.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpgnt.c unix/uxplink.c \ - unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxpterm.c \ - unix/uxpty.c unix/uxputty.c unix/uxsel.c unix/uxser.c \ - unix/uxserver.c unix/uxsftp.c unix/uxsftpserver.c \ - unix/uxshare.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ - unix/uxutils.c unix/x11misc.c unix/x11misc.h unix/xkeysym.c \ + unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxpsusan.c \ + unix/uxpterm.c unix/uxpty.c unix/uxputty.c unix/uxsel.c \ + unix/uxser.c unix/uxserver.c unix/uxsftp.c \ + unix/uxsftpserver.c unix/uxshare.c unix/uxsignal.c \ + unix/uxsocks.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ + unix/uxutils.h unix/x11misc.c unix/x11misc.h unix/xkeysym.c \ unix/xpmptcfg.c unix/xpmpterm.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c version.c version.h wcwidth.c \ - wildcard.c windows/pageant.rc windows/plink.rc \ - windows/pscp.rc windows/psftp.rc windows/putty.rc \ - windows/puttygen.rc windows/puttytel.rc windows/rcstuff.h \ - windows/sizetip.c windows/version.rc2 windows/win_res.h \ - windows/win_res.rc2 windows/wincapi.c windows/wincapi.h \ - windows/wincfg.c windows/wincons.c windows/winctrls.c \ + wildcard.c windows/pageant-rc.h windows/pageant.rc \ + windows/plink.rc windows/pscp.rc windows/psftp.rc \ + windows/putty.rc windows/puttygen-rc.h windows/puttygen.rc \ + windows/puttytel.rc windows/rcstuff.h windows/sizetip.c \ + windows/version.rc2 windows/win_res.h windows/win_res.rc2 \ + windows/wincapi.c windows/wincapi.h windows/wincfg.c \ + windows/wincliloop.c windows/wincons.c windows/winctrls.c \ windows/windefs.c windows/windlg.c windows/window.c \ windows/wingss.c windows/winhandl.c windows/winhelp.c \ windows/winhelp.h windows/winhelp.rc2 windows/winhsock.c \ @@ -1042,10 +1239,12 @@ allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ windows/winnojmp.c windows/winnpc.c windows/winnps.c \ windows/winpgen.c windows/winpgnt.c windows/winpgntc.c \ windows/winplink.c windows/winprint.c windows/winproxy.c \ - windows/winsecur.c windows/winsecur.h windows/winser.c \ - windows/winsftp.c windows/winshare.c windows/winstore.c \ - windows/winstuff.h windows/wintime.c windows/winucs.c \ - windows/winutils.c windows/winx11.c x11fwd.c + windows/winseat.h windows/winsecur.c windows/winsecur.h \ + windows/winselcli.c windows/winselgui.c windows/winser.c \ + windows/winsftp.c windows/winshare.c windows/winsocks.c \ + windows/winstore.c windows/winstuff.h windows/wintime.c \ + windows/winucs.c windows/winutils.c windows/winx11.c \ + x11fwd.c AM_CPPFLAGS = -I$(srcdir)/./ -I$(srcdir)/charset/ -I$(srcdir)/windows/ \ -I$(srcdir)/unix/ @@ -1056,14 +1255,16 @@ libversion_a_SOURCES = version.c libversion_a_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) \ $(am__append_1) noinst_LIBRARIES = libversion.a -cgtest_SOURCES = cgtest.c conf.c ecc.c import.c marshal.c memory.c misc.c \ - mpint.c notiming.c sshaes.c sshauxcrypt.c sshbcrypt.c \ - sshblowf.c sshdes.c sshdss.c sshdssg.c sshecc.c sshecdsag.c \ - sshhmac.c sshmd5.c sshprime.c sshprng.c sshpubk.c sshrand.c \ - sshrsa.c sshrsag.c sshsh256.c sshsh512.c sshsha.c \ - stripctrl.c time.c tree234.c unix/uxcons.c unix/uxgen.c \ - unix/uxmisc.c unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c \ - unix/uxstore.c unix/uxutils.c utils.c wcwidth.c +cgtest_SOURCES = cgtest.c conf.c console.c ecc.c import.c marshal.c memory.c \ + millerrabin.c misc.c mpint.c mpunsafe.c notiming.c pockle.c \ + primecandidate.c smallprimes.c sshaes.c sshargon2.c \ + sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ + sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ + tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ + unix/uxutils.c utils.c wcwidth.c cgtest_LDADD = libversion.a fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ @@ -1071,41 +1272,43 @@ fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ fuzzterm.c logging.c marshal.c memory.c minibidi.c misc.c \ - miscucs.c sercfg.c settings.c stripctrl.c terminal.c time.c \ - timing.c tree234.c unix/uxcfg.c unix/uxmisc.c unix/uxnogtk.c \ + miscucs.c settings.c stripctrl.c terminal.c time.c timing.c \ + tree234.c unix/uxcfg.c unix/uxmisc.c unix/uxnogtk.c \ unix/uxprint.c unix/uxstore.c unix/uxucs.c utils.c wcwidth.c fuzzterm_LDADD = libversion.a osxlaunch_SOURCES = unix/osxlaunch.c -@HAVE_GTK_TRUE@pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c ecc.c \ -@HAVE_GTK_TRUE@ errsock.c logging.c marshal.c memory.c misc.c mpint.c \ +@HAVE_GTK_TRUE@pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c console.c \ +@HAVE_GTK_TRUE@ ecc.c errsock.c logging.c marshal.c memory.c misc.c mpint.c \ @HAVE_GTK_TRUE@ nocproxy.c nogss.c nullplug.c pageant.c proxy.c settings.c \ -@HAVE_GTK_TRUE@ sshaes.c sshauxcrypt.c sshdes.c sshdss.c sshecc.c sshhmac.c \ -@HAVE_GTK_TRUE@ sshmd5.c sshprng.c sshpubk.c sshrsa.c sshsh256.c sshsh512.c \ -@HAVE_GTK_TRUE@ sshsha.c stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ +@HAVE_GTK_TRUE@ sshaes.c sshargon2.c sshauxcrypt.c sshblake2.c sshdes.c \ +@HAVE_GTK_TRUE@ sshdss.c sshecc.c sshhmac.c sshmd5.c sshprng.c sshpubk.c \ +@HAVE_GTK_TRUE@ sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ +@HAVE_GTK_TRUE@ stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ -@HAVE_GTK_TRUE@ unix/uxagentsock.c unix/uxcons.c unix/uxfdsock.c \ -@HAVE_GTK_TRUE@ unix/uxmisc.c unix/uxnet.c unix/uxnoise.c unix/uxpeer.c \ -@HAVE_GTK_TRUE@ unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ -@HAVE_GTK_TRUE@ unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ -@HAVE_GTK_TRUE@ wcwidth.c x11fwd.c +@HAVE_GTK_TRUE@ unix/uxagentsock.c unix/uxcliloop.c unix/uxcons.c \ +@HAVE_GTK_TRUE@ unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ +@HAVE_GTK_TRUE@ unix/uxpeer.c unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c \ +@HAVE_GTK_TRUE@ unix/uxsel.c unix/uxsignal.c unix/uxstore.c unix/uxutils.c \ +@HAVE_GTK_TRUE@ utils.c wcwidth.c x11fwd.c @HAVE_GTK_TRUE@pageant_LDADD = libversion.a $(GTK_LIBS) -plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c cmdline.c \ - conf.c cproxy.c ecc.c errsock.c ldisc.c logging.c mainchan.c \ - marshal.c memory.c misc.c mpint.c noterm.c nullplug.c \ - pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ - sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ - ssh1connection-client.c ssh1connection.c ssh1login.c \ - ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ +plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c clicons.c \ + cmdline.c conf.c console.c cproxy.c ecc.c errsock.c ldisc.c \ + logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ + noterm.c nullplug.c pgssapi.c pinger.c portfwd.c proxy.c \ + raw.c rlogin.c sessprep.c settings.c ssh.c ssh1bpp.c \ + ssh1censor.c ssh1connection-client.c ssh1connection.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c telnet.c time.c \ - timing.c tree234.c unix/ux_x11.c unix/uxagentc.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c supdup.c telnet.c time.c timing.c \ + tree234.c unix/ux_x11.c unix/uxagentc.c unix/uxcliloop.c \ unix/uxcons.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c \ unix/uxplink.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ @@ -1113,55 +1316,92 @@ plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c cmdline.c \ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c plink_LDADD = libversion.a -pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ - conf.c cproxy.c ecc.c errsock.c logging.c mainchan.c \ - marshal.c memory.c misc.c mpint.c nullplug.c pgssapi.c \ - pinger.c portfwd.c proxy.c pscp.c psftpcommon.c settings.c \ - sftp.c sftpcommon.c ssh.c ssh1bpp.c ssh1censor.c \ - ssh1connection-client.c ssh1connection.c ssh1login.c \ - ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ +pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ + cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ + logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ + nullplug.c pgssapi.c pinger.c portfwd.c proxy.c pscp.c \ + psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ + ssh1censor.c ssh1connection-client.c ssh1connection.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c time.c timing.c \ - tree234.c unix/uxagentc.c unix/uxcons.c unix/uxfdsock.c \ - unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ - unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ - unix/uxsel.c unix/uxsftp.c unix/uxshare.c unix/uxstore.c \ - unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c time.c timing.c tree234.c \ + unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ + unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ + unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ + unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ + x11fwd.c pscp_LDADD = libversion.a -psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c cmdline.c \ - conf.c cproxy.c ecc.c errsock.c logging.c mainchan.c \ - marshal.c memory.c misc.c mpint.c nullplug.c pgssapi.c \ - pinger.c portfwd.c proxy.c psftp.c psftpcommon.c settings.c \ - sftp.c sftpcommon.c ssh.c ssh1bpp.c ssh1censor.c \ - ssh1connection-client.c ssh1connection.c ssh1login.c \ - ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ +psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ + cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ + logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ + nullplug.c pgssapi.c pinger.c portfwd.c proxy.c psftp.c \ + psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ + ssh1censor.c ssh1connection-client.c ssh1connection.c \ + ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ - sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ - sshverstring.c sshzlib.c stripctrl.c time.c timing.c \ - tree234.c unix/uxagentc.c unix/uxcons.c unix/uxfdsock.c \ - unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ - unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ - unix/uxsel.c unix/uxsftp.c unix/uxshare.c unix/uxstore.c \ - unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ + sshzlib.c stripctrl.c time.c timing.c tree234.c \ + unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ + unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ + unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ + unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ + x11fwd.c psftp_LDADD = libversion.a +psocks_SOURCES = be_misc.c callback.c conf.c console.c errsock.c logging.c \ + marshal.c memory.c misc.c nocproxy.c norand.c portfwd.c \ + proxy.c psocks.c sshutils.c stripctrl.c time.c timing.c \ + tree234.c unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c \ + unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxpeer.c \ + unix/uxpoll.c unix/uxproxy.c unix/uxsel.c unix/uxsignal.c \ + unix/uxsocks.c utils.c wcwidth.c + +psocks_LDADD = libversion.a +psusan_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ + errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ + mpint.c mpunsafe.c nogss.c nullplug.c pgssapi.c pockle.c \ + portfwd.c primecandidate.c proxy.c scpserver.c sesschan.c \ + settings.c sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ + ssh1censor.c ssh1connection-server.c ssh1connection.c \ + ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ + ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ + ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ + timing.c tree234.c unix/procnet.c unix/ux_x11.c \ + unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ + unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c \ + unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c unix/uxpsusan.c \ + unix/uxpty.c unix/uxsel.c unix/uxsftpserver.c \ + unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ + wcwidth.c wildcard.c x11fwd.c + +psusan_LDADD = libversion.a @HAVE_GTK_TRUE@pterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ @HAVE_GTK_TRUE@ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ @HAVE_GTK_TRUE@ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ @HAVE_GTK_TRUE@ charset/utf8.c charset/xenc.c cmdline.c conf.c config.c \ @HAVE_GTK_TRUE@ dialog.c ldisc.c logging.c marshal.c memory.c minibidi.c \ -@HAVE_GTK_TRUE@ misc.c miscucs.c nocproxy.c nogss.c sercfg.c sessprep.c \ -@HAVE_GTK_TRUE@ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ +@HAVE_GTK_TRUE@ misc.c miscucs.c nocproxy.c nogss.c sessprep.c settings.c \ +@HAVE_GTK_TRUE@ stripctrl.c terminal.c time.c timing.c tree234.c \ @HAVE_GTK_TRUE@ unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ @HAVE_GTK_TRUE@ unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ @@ -1175,7 +1415,7 @@ psftp_LDADD = libversion.a @HAVE_GTK_TRUE@ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ @HAVE_GTK_TRUE@ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ @HAVE_GTK_TRUE@ ldisc.c logging.c marshal.c memory.c minibidi.c misc.c \ -@HAVE_GTK_TRUE@ miscucs.c nocmdline.c nocproxy.c nogss.c sercfg.c sessprep.c \ +@HAVE_GTK_TRUE@ miscucs.c nocmdline.c nocproxy.c nogss.c sessprep.c \ @HAVE_GTK_TRUE@ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ @HAVE_GTK_TRUE@ unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ @HAVE_GTK_TRUE@ unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c unix/gtkwin.c \ @@ -1192,18 +1432,19 @@ psftp_LDADD = libversion.a @HAVE_GTK_TRUE@ charset/xenc.c cmdline.c conf.c config.c cproxy.c dialog.c \ @HAVE_GTK_TRUE@ ecc.c errsock.c ldisc.c logging.c mainchan.c marshal.c \ @HAVE_GTK_TRUE@ memory.c minibidi.c misc.c miscucs.c mpint.c nullplug.c \ -@HAVE_GTK_TRUE@ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c sercfg.c \ +@HAVE_GTK_TRUE@ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ @HAVE_GTK_TRUE@ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ @HAVE_GTK_TRUE@ ssh1connection-client.c ssh1connection.c ssh1login.c \ @HAVE_GTK_TRUE@ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ @HAVE_GTK_TRUE@ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ @HAVE_GTK_TRUE@ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ -@HAVE_GTK_TRUE@ ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ -@HAVE_GTK_TRUE@ sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ -@HAVE_GTK_TRUE@ sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ -@HAVE_GTK_TRUE@ sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ -@HAVE_GTK_TRUE@ sshverstring.c sshzlib.c stripctrl.c telnet.c terminal.c \ -@HAVE_GTK_TRUE@ time.c timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ +@HAVE_GTK_TRUE@ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ +@HAVE_GTK_TRUE@ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ +@HAVE_GTK_TRUE@ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ +@HAVE_GTK_TRUE@ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ +@HAVE_GTK_TRUE@ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ +@HAVE_GTK_TRUE@ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ +@HAVE_GTK_TRUE@ timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ @HAVE_GTK_TRUE@ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ @@ -1222,18 +1463,19 @@ psftp_LDADD = libversion.a @HAVE_GTK_TRUE@ charset/xenc.c conf.c config.c cproxy.c dialog.c ecc.c \ @HAVE_GTK_TRUE@ errsock.c ldisc.c logging.c mainchan.c marshal.c memory.c \ @HAVE_GTK_TRUE@ minibidi.c misc.c miscucs.c mpint.c nocmdline.c nullplug.c \ -@HAVE_GTK_TRUE@ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c sercfg.c \ +@HAVE_GTK_TRUE@ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ @HAVE_GTK_TRUE@ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ @HAVE_GTK_TRUE@ ssh1connection-client.c ssh1connection.c ssh1login.c \ @HAVE_GTK_TRUE@ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ @HAVE_GTK_TRUE@ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ @HAVE_GTK_TRUE@ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ -@HAVE_GTK_TRUE@ ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ -@HAVE_GTK_TRUE@ sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ -@HAVE_GTK_TRUE@ sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ -@HAVE_GTK_TRUE@ sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshshare.c \ -@HAVE_GTK_TRUE@ sshverstring.c sshzlib.c stripctrl.c telnet.c terminal.c \ -@HAVE_GTK_TRUE@ time.c timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ +@HAVE_GTK_TRUE@ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ +@HAVE_GTK_TRUE@ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ +@HAVE_GTK_TRUE@ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ +@HAVE_GTK_TRUE@ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ +@HAVE_GTK_TRUE@ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ +@HAVE_GTK_TRUE@ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ +@HAVE_GTK_TRUE@ timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ @HAVE_GTK_TRUE@ unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ @@ -1245,14 +1487,16 @@ psftp_LDADD = libversion.a @HAVE_GTK_TRUE@ x11fwd.c @HAVE_GTK_TRUE@puttyapp_LDADD = libversion.a $(GTK_LIBS) -puttygen_SOURCES = cmdgen.c conf.c ecc.c import.c marshal.c memory.c misc.c \ - mpint.c notiming.c sshaes.c sshauxcrypt.c sshbcrypt.c \ - sshblowf.c sshdes.c sshdss.c sshdssg.c sshecc.c sshecdsag.c \ - sshhmac.c sshmd5.c sshprime.c sshprng.c sshpubk.c sshrand.c \ - sshrsa.c sshrsag.c sshsh256.c sshsh512.c sshsha.c \ - stripctrl.c time.c tree234.c unix/uxcons.c unix/uxgen.c \ - unix/uxmisc.c unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c \ - unix/uxstore.c unix/uxutils.c utils.c wcwidth.c +puttygen_SOURCES = cmdgen.c conf.c console.c ecc.c import.c marshal.c \ + memory.c millerrabin.c misc.c mpint.c mpunsafe.c notiming.c \ + pockle.c primecandidate.c smallprimes.c sshaes.c sshargon2.c \ + sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ + sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ + tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ + unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ + unix/uxutils.c utils.c wcwidth.c puttygen_LDADD = libversion.a @HAVE_GTK_TRUE@puttytel_SOURCES = be_misc.c be_nos_s.c callback.c charset/fromucs.c \ @@ -1261,46 +1505,52 @@ puttygen_LDADD = libversion.a @HAVE_GTK_TRUE@ charset/toucs.c charset/utf8.c charset/xenc.c cmdline.c \ @HAVE_GTK_TRUE@ conf.c config.c dialog.c errsock.c ldisc.c logging.c \ @HAVE_GTK_TRUE@ marshal.c memory.c minibidi.c misc.c miscucs.c nocproxy.c \ -@HAVE_GTK_TRUE@ nogss.c pinger.c proxy.c raw.c rlogin.c sercfg.c sessprep.c \ -@HAVE_GTK_TRUE@ settings.c stripctrl.c telnet.c terminal.c time.c timing.c \ -@HAVE_GTK_TRUE@ tree234.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ -@HAVE_GTK_TRUE@ unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c \ -@HAVE_GTK_TRUE@ unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c unix/uxmisc.c \ -@HAVE_GTK_TRUE@ unix/uxnet.c unix/uxpeer.c unix/uxpoll.c unix/uxprint.c \ -@HAVE_GTK_TRUE@ unix/uxproxy.c unix/uxputty.c unix/uxsel.c unix/uxser.c \ -@HAVE_GTK_TRUE@ unix/uxsignal.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ -@HAVE_GTK_TRUE@ unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ +@HAVE_GTK_TRUE@ nogss.c norand.c pinger.c proxy.c raw.c rlogin.c sessprep.c \ +@HAVE_GTK_TRUE@ settings.c stripctrl.c supdup.c telnet.c terminal.c time.c \ +@HAVE_GTK_TRUE@ timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ +@HAVE_GTK_TRUE@ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ +@HAVE_GTK_TRUE@ unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c \ +@HAVE_GTK_TRUE@ unix/uxmisc.c unix/uxnet.c unix/uxpeer.c unix/uxpoll.c \ +@HAVE_GTK_TRUE@ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ +@HAVE_GTK_TRUE@ unix/uxser.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ +@HAVE_GTK_TRUE@ unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ @HAVE_GTK_TRUE@ unix/xpmputty.c utils.c wcwidth.c @HAVE_GTK_TRUE@puttytel_LDADD = libversion.a $(GTK_LIBS) -testcrypt_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ - sshauxcrypt.c sshblowf.c sshccp.c sshcrc.c sshcrcda.c \ - sshdes.c sshdh.c sshdss.c sshecc.c sshhmac.c sshmd5.c \ - sshprime.c sshprng.c sshrsa.c sshsh256.c sshsh512.c sshsha.c \ - testcrypt.c tree234.c unix/uxutils.c utils.c +testcrypt_SOURCES = ecc.c marshal.c memory.c millerrabin.c mpint.c \ + mpunsafe.c pockle.c primecandidate.c smallprimes.c sshaes.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c \ + sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c sshprime.c \ + sshprng.c sshpubk.c sshrsa.c sshrsag.c sshsh256.c sshsh512.c \ + sshsha.c sshsha3.c testcrypt.c tree234.c unix/uxutils.c \ + utils.c testsc_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ - sshauxcrypt.c sshblowf.c sshccp.c sshcrc.c sshcrcda.c \ - sshdes.c sshdh.c sshdss.c sshecc.c sshhmac.c sshmac.c \ - sshmd5.c sshrsa.c sshsh256.c sshsh512.c sshsha.c testsc.c \ - tree234.c unix/uxutils.c utils.c wildcard.c + sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c sshccp.c \ + sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ + sshhmac.c sshmac.c sshmd5.c sshpubk.c sshrsa.c sshsh256.c \ + sshsh512.c sshsha.c sshsha3.c testsc.c tree234.c \ + unix/uxutils.c utils.c wildcard.c testzlib_SOURCES = marshal.c memory.c sshzlib.c testzlib.c utils.c uppity_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ - errsock.c logging.c marshal.c memory.c misc.c mpint.c \ - nullplug.c pgssapi.c portfwd.c proxy.c scpserver.c \ - sesschan.c settings.c sftpcommon.c sftpserver.c ssh1bpp.c \ + errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ + mpint.c mpunsafe.c nullplug.c pgssapi.c pockle.c portfwd.c \ + primecandidate.c proxy.c scpserver.c sesschan.c settings.c \ + sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ ssh1censor.c ssh1connection-server.c ssh1connection.c \ - ssh1login-server.c ssh2bpp.c ssh2censor.c \ + ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ - ssharcf.c sshauxcrypt.c sshblowf.c sshccp.c sshcommon.c \ - sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ - sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprime.c sshprng.c \ - sshpubk.c sshrand.c sshrsa.c sshrsag.c sshserver.c \ - sshsh256.c sshsh512.c sshsha.c sshverstring.c sshzlib.c \ - stripctrl.c time.c timing.c tree234.c unix/procnet.c \ - unix/ux_x11.c unix/uxagentsock.c unix/uxfdsock.c \ + ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ + sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ + sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ + sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ + sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ + sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ + timing.c tree234.c unix/procnet.c unix/ux_x11.c \ + unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ unix/uxpty.c unix/uxsel.c unix/uxserver.c \ @@ -1310,8 +1560,8 @@ uppity_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ uppity_LDADD = libversion.a @AUTO_GIT_COMMIT_TRUE@BUILT_SOURCES = empty.h @AUTO_GIT_COMMIT_TRUE@CLEANFILES = empty.h -@HAVE_GTK_FALSE@man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 -@HAVE_GTK_TRUE@man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 \ +@HAVE_GTK_FALSE@man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 +@HAVE_GTK_TRUE@man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 \ @HAVE_GTK_TRUE@ doc/pageant.1 doc/pterm.1 doc/putty.1 doc/puttytel.1 @HAVE_QUARTZ_TRUE@noinst_SCRIPTS = unix/PuTTY.app unix/Pterm.app @@ -1341,8 +1591,8 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -1368,14 +1618,6 @@ $(srcdir)/uxconfig.in: $(am__configure_deps) distclean-hdr: -rm -f uxconfig.h stamp-h1 - -clean-noinstLIBRARIES: - -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) - -libversion.a: $(libversion_a_OBJECTS) $(libversion_a_DEPENDENCIES) $(EXTRA_libversion_a_DEPENDENCIES) - $(AM_V_at)-rm -f libversion.a - $(AM_V_AR)$(libversion_a_AR) libversion.a $(libversion_a_OBJECTS) $(libversion_a_LIBADD) - $(AM_V_at)$(RANLIB) libversion.a install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ @@ -1421,6 +1663,14 @@ clean-binPROGRAMS: clean-noinstPROGRAMS: -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +libversion.a: $(libversion_a_OBJECTS) $(libversion_a_DEPENDENCIES) $(EXTRA_libversion_a_DEPENDENCIES) + $(AM_V_at)-rm -f libversion.a + $(AM_V_AR)$(libversion_a_AR) libversion.a $(libversion_a_OBJECTS) $(libversion_a_LIBADD) + $(AM_V_at)$(RANLIB) libversion.a unix/$(am__dirstamp): @$(MKDIR_P) unix @: > unix/$(am__dirstamp) @@ -1499,6 +1749,8 @@ unix/uxagentc.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxagentsock.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) +unix/uxcliloop.$(OBJEXT): unix/$(am__dirstamp) \ + unix/$(DEPDIR)/$(am__dirstamp) unix/uxfdsock.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxnet.$(OBJEXT): unix/$(am__dirstamp) \ @@ -1539,6 +1791,24 @@ pscp$(EXEEXT): $(pscp_OBJECTS) $(pscp_DEPENDENCIES) $(EXTRA_pscp_DEPENDENCIES) psftp$(EXEEXT): $(psftp_OBJECTS) $(psftp_DEPENDENCIES) $(EXTRA_psftp_DEPENDENCIES) @rm -f psftp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(psftp_OBJECTS) $(psftp_LDADD) $(LIBS) +unix/uxsocks.$(OBJEXT): unix/$(am__dirstamp) \ + unix/$(DEPDIR)/$(am__dirstamp) + +psocks$(EXEEXT): $(psocks_OBJECTS) $(psocks_DEPENDENCIES) $(EXTRA_psocks_DEPENDENCIES) + @rm -f psocks$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(psocks_OBJECTS) $(psocks_LDADD) $(LIBS) +unix/procnet.$(OBJEXT): unix/$(am__dirstamp) \ + unix/$(DEPDIR)/$(am__dirstamp) +unix/uxpsusan.$(OBJEXT): unix/$(am__dirstamp) \ + unix/$(DEPDIR)/$(am__dirstamp) +unix/uxpty.$(OBJEXT): unix/$(am__dirstamp) \ + unix/$(DEPDIR)/$(am__dirstamp) +unix/uxsftpserver.$(OBJEXT): unix/$(am__dirstamp) \ + unix/$(DEPDIR)/$(am__dirstamp) + +psusan$(EXEEXT): $(psusan_OBJECTS) $(psusan_DEPENDENCIES) $(EXTRA_psusan_DEPENDENCIES) + @rm -f psusan$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(psusan_OBJECTS) $(psusan_LDADD) $(LIBS) unix/gtkcfg.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkcols.$(OBJEXT): unix/$(am__dirstamp) \ @@ -1555,8 +1825,6 @@ unix/gtkwin.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpterm.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) -unix/uxpty.$(OBJEXT): unix/$(am__dirstamp) \ - unix/$(DEPDIR)/$(am__dirstamp) unix/x11misc.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/xkeysym.$(OBJEXT): unix/$(am__dirstamp) \ @@ -1609,12 +1877,8 @@ testsc$(EXEEXT): $(testsc_OBJECTS) $(testsc_DEPENDENCIES) $(EXTRA_testsc_DEPENDE testzlib$(EXEEXT): $(testzlib_OBJECTS) $(testzlib_DEPENDENCIES) $(EXTRA_testzlib_DEPENDENCIES) @rm -f testzlib$(EXEEXT) $(AM_V_CCLD)$(LINK) $(testzlib_OBJECTS) $(testzlib_LDADD) $(LIBS) -unix/procnet.$(OBJEXT): unix/$(am__dirstamp) \ - unix/$(DEPDIR)/$(am__dirstamp) unix/uxserver.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) -unix/uxsftpserver.$(OBJEXT): unix/$(am__dirstamp) \ - unix/$(DEPDIR)/$(am__dirstamp) uppity$(EXEEXT): $(uppity_OBJECTS) $(uppity_DEPENDENCIES) $(EXTRA_uppity_DEPENDENCIES) @rm -f uppity$(EXEEXT) @@ -1628,183 +1892,205 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agentf.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aqsync.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_all_s.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_misc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_none.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_nos_s.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_ssh.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgtest.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdgen.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdline.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cproxy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialog.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errsock.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzzterm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldisc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libversion_a-version.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logging.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainchan.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/marshal.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minibidi.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miscucs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpint.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nocmdline.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nocproxy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nogss.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/noterm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notiming.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nullplug.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageant.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgssapi.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pinger.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/portfwd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pscp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psftp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psftpcommon.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rlogin.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scpserver.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sercfg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sesschan.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sessprep.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpcommon.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpserver.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1bpp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1censor.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection-client.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection-server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1login-server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1login.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2bpp-bare.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2bpp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2censor.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection-client.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection-server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2kex-client.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2kex-server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2transhk.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2transport.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2userauth-server.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2userauth.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshaes.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssharcf.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshauxcrypt.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshbcrypt.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshblowf.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshccp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcommon.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcrc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcrcda.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdes.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdh.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdss.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdssg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshecc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshecdsag.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshgssc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshhmac.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshmac.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshmd5.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshprime.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshprng.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshpubk.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrand.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrsa.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrsag.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshserver.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsh256.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsh512.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsha.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshshare.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshverstring.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshzlib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stripctrl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/telnet.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testcrypt.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testzlib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timing.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree234.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wcwidth.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wildcard.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x11fwd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/fromucs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/localenc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/macenc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/mimeenc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/sbcs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/sbcsdat.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/slookup.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/toucs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/utf8.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/xenc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkapp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkask.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcfg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcols.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcomm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkdlg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkfont.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkmain.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkmisc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkwin.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/osxlaunch.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/procnet.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/ux_x11.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxagentc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxagentsock.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcfg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcons.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxfdsock.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxgen.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxgss.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxmisc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnet.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnogtk.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnoise.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpeer.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpgnt.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxplink.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpoll.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxprint.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxproxy.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpterm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpty.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxputty.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsel.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxserver.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsftp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsftpserver.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxshare.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsignal.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxstore.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxucs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxutils.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/x11misc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xkeysym.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmptcfg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmpterm.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmpucfg.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmputty.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agentf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aqsync.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_all_s.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_misc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_none.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_nos_s.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_ssh.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgtest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clicons.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdgen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cproxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialog.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errsock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzzterm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldisc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libversion_a-version.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logging.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainchan.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/marshal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/millerrabin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minibidi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miscucs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpint.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpunsafe.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nocmdline.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nocproxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nogss.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/norand.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/noterm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notiming.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nullplug.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageant.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgssapi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pinger.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pockle.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/portfwd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/primecandidate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pscp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psftp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psftpcommon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psocks.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rlogin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scpserver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sesschan.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sessprep.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpcommon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpserver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smallprimes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1bpp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1censor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection-client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1login-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1login.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2bpp-bare.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2bpp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2censor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection-client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2kex-client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2kex-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2transhk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2transport.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2userauth-server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2userauth.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshaes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssharcf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshargon2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshauxcrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshbcrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshblake2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshblowf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshccp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcommon.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcrc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcrcda.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdes.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdh.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdss.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdssg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshecc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshecdsag.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshgssc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshhmac.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshmac.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshmd5.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshprime.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshprng.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshpubk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrand.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrsa.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrsag.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshserver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsh256.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsh512.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsha.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsha3.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshshare.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshverstring.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshzlib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stripctrl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/supdup.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/telnet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testcrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testzlib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timing.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree234.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wcwidth.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wildcard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x11fwd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/fromucs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/localenc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/macenc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/mimeenc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/sbcs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/sbcsdat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/slookup.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/toucs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/utf8.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/xenc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkapp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkask.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcfg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcols.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcomm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkdlg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkfont.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkmain.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkmisc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkwin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/osxlaunch.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/procnet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/ux_x11.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxagentc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxagentsock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcfg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcliloop.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcons.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxfdsock.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxgen.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxgss.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxmisc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnogtk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnoise.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpeer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpgnt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxplink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpoll.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxprint.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxproxy.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpsusan.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpterm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpty.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxputty.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxserver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsftp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsftpserver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxshare.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsignal.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsocks.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxstore.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxucs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/x11misc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xkeysym.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmptcfg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmpterm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmpucfg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmputty.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @@ -1936,7 +2222,10 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ @@ -2103,7 +2392,7 @@ check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile $(LIBRARIES) $(PROGRAMS) $(SCRIPTS) $(MANS) \ +all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(SCRIPTS) $(MANS) \ uxconfig.h installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \ @@ -2154,7 +2443,199 @@ clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf ./$(DEPDIR) charset/$(DEPDIR) unix/$(DEPDIR) + -rm -f ./$(DEPDIR)/agentf.Po + -rm -f ./$(DEPDIR)/aqsync.Po + -rm -f ./$(DEPDIR)/be_all_s.Po + -rm -f ./$(DEPDIR)/be_misc.Po + -rm -f ./$(DEPDIR)/be_none.Po + -rm -f ./$(DEPDIR)/be_nos_s.Po + -rm -f ./$(DEPDIR)/be_ssh.Po + -rm -f ./$(DEPDIR)/callback.Po + -rm -f ./$(DEPDIR)/cgtest.Po + -rm -f ./$(DEPDIR)/clicons.Po + -rm -f ./$(DEPDIR)/cmdgen.Po + -rm -f ./$(DEPDIR)/cmdline.Po + -rm -f ./$(DEPDIR)/conf.Po + -rm -f ./$(DEPDIR)/config.Po + -rm -f ./$(DEPDIR)/console.Po + -rm -f ./$(DEPDIR)/cproxy.Po + -rm -f ./$(DEPDIR)/dialog.Po + -rm -f ./$(DEPDIR)/ecc.Po + -rm -f ./$(DEPDIR)/errsock.Po + -rm -f ./$(DEPDIR)/fuzzterm.Po + -rm -f ./$(DEPDIR)/import.Po + -rm -f ./$(DEPDIR)/ldisc.Po + -rm -f ./$(DEPDIR)/libversion_a-version.Po + -rm -f ./$(DEPDIR)/logging.Po + -rm -f ./$(DEPDIR)/mainchan.Po + -rm -f ./$(DEPDIR)/marshal.Po + -rm -f ./$(DEPDIR)/memory.Po + -rm -f ./$(DEPDIR)/millerrabin.Po + -rm -f ./$(DEPDIR)/minibidi.Po + -rm -f ./$(DEPDIR)/misc.Po + -rm -f ./$(DEPDIR)/miscucs.Po + -rm -f ./$(DEPDIR)/mpint.Po + -rm -f ./$(DEPDIR)/mpunsafe.Po + -rm -f ./$(DEPDIR)/nocmdline.Po + -rm -f ./$(DEPDIR)/nocproxy.Po + -rm -f ./$(DEPDIR)/nogss.Po + -rm -f ./$(DEPDIR)/norand.Po + -rm -f ./$(DEPDIR)/noterm.Po + -rm -f ./$(DEPDIR)/notiming.Po + -rm -f ./$(DEPDIR)/nullplug.Po + -rm -f ./$(DEPDIR)/pageant.Po + -rm -f ./$(DEPDIR)/pgssapi.Po + -rm -f ./$(DEPDIR)/pinger.Po + -rm -f ./$(DEPDIR)/pockle.Po + -rm -f ./$(DEPDIR)/portfwd.Po + -rm -f ./$(DEPDIR)/primecandidate.Po + -rm -f ./$(DEPDIR)/proxy.Po + -rm -f ./$(DEPDIR)/pscp.Po + -rm -f ./$(DEPDIR)/psftp.Po + -rm -f ./$(DEPDIR)/psftpcommon.Po + -rm -f ./$(DEPDIR)/psocks.Po + -rm -f ./$(DEPDIR)/raw.Po + -rm -f ./$(DEPDIR)/rlogin.Po + -rm -f ./$(DEPDIR)/scpserver.Po + -rm -f ./$(DEPDIR)/sesschan.Po + -rm -f ./$(DEPDIR)/sessprep.Po + -rm -f ./$(DEPDIR)/settings.Po + -rm -f ./$(DEPDIR)/sftp.Po + -rm -f ./$(DEPDIR)/sftpcommon.Po + -rm -f ./$(DEPDIR)/sftpserver.Po + -rm -f ./$(DEPDIR)/smallprimes.Po + -rm -f ./$(DEPDIR)/ssh.Po + -rm -f ./$(DEPDIR)/ssh1bpp.Po + -rm -f ./$(DEPDIR)/ssh1censor.Po + -rm -f ./$(DEPDIR)/ssh1connection-client.Po + -rm -f ./$(DEPDIR)/ssh1connection-server.Po + -rm -f ./$(DEPDIR)/ssh1connection.Po + -rm -f ./$(DEPDIR)/ssh1login-server.Po + -rm -f ./$(DEPDIR)/ssh1login.Po + -rm -f ./$(DEPDIR)/ssh2bpp-bare.Po + -rm -f ./$(DEPDIR)/ssh2bpp.Po + -rm -f ./$(DEPDIR)/ssh2censor.Po + -rm -f ./$(DEPDIR)/ssh2connection-client.Po + -rm -f ./$(DEPDIR)/ssh2connection-server.Po + -rm -f ./$(DEPDIR)/ssh2connection.Po + -rm -f ./$(DEPDIR)/ssh2kex-client.Po + -rm -f ./$(DEPDIR)/ssh2kex-server.Po + -rm -f ./$(DEPDIR)/ssh2transhk.Po + -rm -f ./$(DEPDIR)/ssh2transport.Po + -rm -f ./$(DEPDIR)/ssh2userauth-server.Po + -rm -f ./$(DEPDIR)/ssh2userauth.Po + -rm -f ./$(DEPDIR)/sshaes.Po + -rm -f ./$(DEPDIR)/ssharcf.Po + -rm -f ./$(DEPDIR)/sshargon2.Po + -rm -f ./$(DEPDIR)/sshauxcrypt.Po + -rm -f ./$(DEPDIR)/sshbcrypt.Po + -rm -f ./$(DEPDIR)/sshblake2.Po + -rm -f ./$(DEPDIR)/sshblowf.Po + -rm -f ./$(DEPDIR)/sshccp.Po + -rm -f ./$(DEPDIR)/sshcommon.Po + -rm -f ./$(DEPDIR)/sshcrc.Po + -rm -f ./$(DEPDIR)/sshcrcda.Po + -rm -f ./$(DEPDIR)/sshdes.Po + -rm -f ./$(DEPDIR)/sshdh.Po + -rm -f ./$(DEPDIR)/sshdss.Po + -rm -f ./$(DEPDIR)/sshdssg.Po + -rm -f ./$(DEPDIR)/sshecc.Po + -rm -f ./$(DEPDIR)/sshecdsag.Po + -rm -f ./$(DEPDIR)/sshgssc.Po + -rm -f ./$(DEPDIR)/sshhmac.Po + -rm -f ./$(DEPDIR)/sshmac.Po + -rm -f ./$(DEPDIR)/sshmd5.Po + -rm -f ./$(DEPDIR)/sshprime.Po + -rm -f ./$(DEPDIR)/sshprng.Po + -rm -f ./$(DEPDIR)/sshpubk.Po + -rm -f ./$(DEPDIR)/sshrand.Po + -rm -f ./$(DEPDIR)/sshrsa.Po + -rm -f ./$(DEPDIR)/sshrsag.Po + -rm -f ./$(DEPDIR)/sshserver.Po + -rm -f ./$(DEPDIR)/sshsh256.Po + -rm -f ./$(DEPDIR)/sshsh512.Po + -rm -f ./$(DEPDIR)/sshsha.Po + -rm -f ./$(DEPDIR)/sshsha3.Po + -rm -f ./$(DEPDIR)/sshshare.Po + -rm -f ./$(DEPDIR)/sshutils.Po + -rm -f ./$(DEPDIR)/sshverstring.Po + -rm -f ./$(DEPDIR)/sshzlib.Po + -rm -f ./$(DEPDIR)/stripctrl.Po + -rm -f ./$(DEPDIR)/supdup.Po + -rm -f ./$(DEPDIR)/telnet.Po + -rm -f ./$(DEPDIR)/terminal.Po + -rm -f ./$(DEPDIR)/testcrypt.Po + -rm -f ./$(DEPDIR)/testsc.Po + -rm -f ./$(DEPDIR)/testzlib.Po + -rm -f ./$(DEPDIR)/time.Po + -rm -f ./$(DEPDIR)/timing.Po + -rm -f ./$(DEPDIR)/tree234.Po + -rm -f ./$(DEPDIR)/utils.Po + -rm -f ./$(DEPDIR)/wcwidth.Po + -rm -f ./$(DEPDIR)/wildcard.Po + -rm -f ./$(DEPDIR)/x11fwd.Po + -rm -f charset/$(DEPDIR)/fromucs.Po + -rm -f charset/$(DEPDIR)/localenc.Po + -rm -f charset/$(DEPDIR)/macenc.Po + -rm -f charset/$(DEPDIR)/mimeenc.Po + -rm -f charset/$(DEPDIR)/sbcs.Po + -rm -f charset/$(DEPDIR)/sbcsdat.Po + -rm -f charset/$(DEPDIR)/slookup.Po + -rm -f charset/$(DEPDIR)/toucs.Po + -rm -f charset/$(DEPDIR)/utf8.Po + -rm -f charset/$(DEPDIR)/xenc.Po + -rm -f unix/$(DEPDIR)/gtkapp.Po + -rm -f unix/$(DEPDIR)/gtkask.Po + -rm -f unix/$(DEPDIR)/gtkcfg.Po + -rm -f unix/$(DEPDIR)/gtkcols.Po + -rm -f unix/$(DEPDIR)/gtkcomm.Po + -rm -f unix/$(DEPDIR)/gtkdlg.Po + -rm -f unix/$(DEPDIR)/gtkfont.Po + -rm -f unix/$(DEPDIR)/gtkmain.Po + -rm -f unix/$(DEPDIR)/gtkmisc.Po + -rm -f unix/$(DEPDIR)/gtkwin.Po + -rm -f unix/$(DEPDIR)/osxlaunch.Po + -rm -f unix/$(DEPDIR)/procnet.Po + -rm -f unix/$(DEPDIR)/ux_x11.Po + -rm -f unix/$(DEPDIR)/uxagentc.Po + -rm -f unix/$(DEPDIR)/uxagentsock.Po + -rm -f unix/$(DEPDIR)/uxcfg.Po + -rm -f unix/$(DEPDIR)/uxcliloop.Po + -rm -f unix/$(DEPDIR)/uxcons.Po + -rm -f unix/$(DEPDIR)/uxfdsock.Po + -rm -f unix/$(DEPDIR)/uxgen.Po + -rm -f unix/$(DEPDIR)/uxgss.Po + -rm -f unix/$(DEPDIR)/uxmisc.Po + -rm -f unix/$(DEPDIR)/uxnet.Po + -rm -f unix/$(DEPDIR)/uxnogtk.Po + -rm -f unix/$(DEPDIR)/uxnoise.Po + -rm -f unix/$(DEPDIR)/uxpeer.Po + -rm -f unix/$(DEPDIR)/uxpgnt.Po + -rm -f unix/$(DEPDIR)/uxplink.Po + -rm -f unix/$(DEPDIR)/uxpoll.Po + -rm -f unix/$(DEPDIR)/uxprint.Po + -rm -f unix/$(DEPDIR)/uxproxy.Po + -rm -f unix/$(DEPDIR)/uxpsusan.Po + -rm -f unix/$(DEPDIR)/uxpterm.Po + -rm -f unix/$(DEPDIR)/uxpty.Po + -rm -f unix/$(DEPDIR)/uxputty.Po + -rm -f unix/$(DEPDIR)/uxsel.Po + -rm -f unix/$(DEPDIR)/uxser.Po + -rm -f unix/$(DEPDIR)/uxserver.Po + -rm -f unix/$(DEPDIR)/uxsftp.Po + -rm -f unix/$(DEPDIR)/uxsftpserver.Po + -rm -f unix/$(DEPDIR)/uxshare.Po + -rm -f unix/$(DEPDIR)/uxsignal.Po + -rm -f unix/$(DEPDIR)/uxsocks.Po + -rm -f unix/$(DEPDIR)/uxstore.Po + -rm -f unix/$(DEPDIR)/uxucs.Po + -rm -f unix/$(DEPDIR)/uxutils.Po + -rm -f unix/$(DEPDIR)/x11misc.Po + -rm -f unix/$(DEPDIR)/xkeysym.Po + -rm -f unix/$(DEPDIR)/xpmptcfg.Po + -rm -f unix/$(DEPDIR)/xpmpterm.Po + -rm -f unix/$(DEPDIR)/xpmpucfg.Po + -rm -f unix/$(DEPDIR)/xpmputty.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-tags @@ -2202,7 +2683,199 @@ installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf ./$(DEPDIR) charset/$(DEPDIR) unix/$(DEPDIR) + -rm -f ./$(DEPDIR)/agentf.Po + -rm -f ./$(DEPDIR)/aqsync.Po + -rm -f ./$(DEPDIR)/be_all_s.Po + -rm -f ./$(DEPDIR)/be_misc.Po + -rm -f ./$(DEPDIR)/be_none.Po + -rm -f ./$(DEPDIR)/be_nos_s.Po + -rm -f ./$(DEPDIR)/be_ssh.Po + -rm -f ./$(DEPDIR)/callback.Po + -rm -f ./$(DEPDIR)/cgtest.Po + -rm -f ./$(DEPDIR)/clicons.Po + -rm -f ./$(DEPDIR)/cmdgen.Po + -rm -f ./$(DEPDIR)/cmdline.Po + -rm -f ./$(DEPDIR)/conf.Po + -rm -f ./$(DEPDIR)/config.Po + -rm -f ./$(DEPDIR)/console.Po + -rm -f ./$(DEPDIR)/cproxy.Po + -rm -f ./$(DEPDIR)/dialog.Po + -rm -f ./$(DEPDIR)/ecc.Po + -rm -f ./$(DEPDIR)/errsock.Po + -rm -f ./$(DEPDIR)/fuzzterm.Po + -rm -f ./$(DEPDIR)/import.Po + -rm -f ./$(DEPDIR)/ldisc.Po + -rm -f ./$(DEPDIR)/libversion_a-version.Po + -rm -f ./$(DEPDIR)/logging.Po + -rm -f ./$(DEPDIR)/mainchan.Po + -rm -f ./$(DEPDIR)/marshal.Po + -rm -f ./$(DEPDIR)/memory.Po + -rm -f ./$(DEPDIR)/millerrabin.Po + -rm -f ./$(DEPDIR)/minibidi.Po + -rm -f ./$(DEPDIR)/misc.Po + -rm -f ./$(DEPDIR)/miscucs.Po + -rm -f ./$(DEPDIR)/mpint.Po + -rm -f ./$(DEPDIR)/mpunsafe.Po + -rm -f ./$(DEPDIR)/nocmdline.Po + -rm -f ./$(DEPDIR)/nocproxy.Po + -rm -f ./$(DEPDIR)/nogss.Po + -rm -f ./$(DEPDIR)/norand.Po + -rm -f ./$(DEPDIR)/noterm.Po + -rm -f ./$(DEPDIR)/notiming.Po + -rm -f ./$(DEPDIR)/nullplug.Po + -rm -f ./$(DEPDIR)/pageant.Po + -rm -f ./$(DEPDIR)/pgssapi.Po + -rm -f ./$(DEPDIR)/pinger.Po + -rm -f ./$(DEPDIR)/pockle.Po + -rm -f ./$(DEPDIR)/portfwd.Po + -rm -f ./$(DEPDIR)/primecandidate.Po + -rm -f ./$(DEPDIR)/proxy.Po + -rm -f ./$(DEPDIR)/pscp.Po + -rm -f ./$(DEPDIR)/psftp.Po + -rm -f ./$(DEPDIR)/psftpcommon.Po + -rm -f ./$(DEPDIR)/psocks.Po + -rm -f ./$(DEPDIR)/raw.Po + -rm -f ./$(DEPDIR)/rlogin.Po + -rm -f ./$(DEPDIR)/scpserver.Po + -rm -f ./$(DEPDIR)/sesschan.Po + -rm -f ./$(DEPDIR)/sessprep.Po + -rm -f ./$(DEPDIR)/settings.Po + -rm -f ./$(DEPDIR)/sftp.Po + -rm -f ./$(DEPDIR)/sftpcommon.Po + -rm -f ./$(DEPDIR)/sftpserver.Po + -rm -f ./$(DEPDIR)/smallprimes.Po + -rm -f ./$(DEPDIR)/ssh.Po + -rm -f ./$(DEPDIR)/ssh1bpp.Po + -rm -f ./$(DEPDIR)/ssh1censor.Po + -rm -f ./$(DEPDIR)/ssh1connection-client.Po + -rm -f ./$(DEPDIR)/ssh1connection-server.Po + -rm -f ./$(DEPDIR)/ssh1connection.Po + -rm -f ./$(DEPDIR)/ssh1login-server.Po + -rm -f ./$(DEPDIR)/ssh1login.Po + -rm -f ./$(DEPDIR)/ssh2bpp-bare.Po + -rm -f ./$(DEPDIR)/ssh2bpp.Po + -rm -f ./$(DEPDIR)/ssh2censor.Po + -rm -f ./$(DEPDIR)/ssh2connection-client.Po + -rm -f ./$(DEPDIR)/ssh2connection-server.Po + -rm -f ./$(DEPDIR)/ssh2connection.Po + -rm -f ./$(DEPDIR)/ssh2kex-client.Po + -rm -f ./$(DEPDIR)/ssh2kex-server.Po + -rm -f ./$(DEPDIR)/ssh2transhk.Po + -rm -f ./$(DEPDIR)/ssh2transport.Po + -rm -f ./$(DEPDIR)/ssh2userauth-server.Po + -rm -f ./$(DEPDIR)/ssh2userauth.Po + -rm -f ./$(DEPDIR)/sshaes.Po + -rm -f ./$(DEPDIR)/ssharcf.Po + -rm -f ./$(DEPDIR)/sshargon2.Po + -rm -f ./$(DEPDIR)/sshauxcrypt.Po + -rm -f ./$(DEPDIR)/sshbcrypt.Po + -rm -f ./$(DEPDIR)/sshblake2.Po + -rm -f ./$(DEPDIR)/sshblowf.Po + -rm -f ./$(DEPDIR)/sshccp.Po + -rm -f ./$(DEPDIR)/sshcommon.Po + -rm -f ./$(DEPDIR)/sshcrc.Po + -rm -f ./$(DEPDIR)/sshcrcda.Po + -rm -f ./$(DEPDIR)/sshdes.Po + -rm -f ./$(DEPDIR)/sshdh.Po + -rm -f ./$(DEPDIR)/sshdss.Po + -rm -f ./$(DEPDIR)/sshdssg.Po + -rm -f ./$(DEPDIR)/sshecc.Po + -rm -f ./$(DEPDIR)/sshecdsag.Po + -rm -f ./$(DEPDIR)/sshgssc.Po + -rm -f ./$(DEPDIR)/sshhmac.Po + -rm -f ./$(DEPDIR)/sshmac.Po + -rm -f ./$(DEPDIR)/sshmd5.Po + -rm -f ./$(DEPDIR)/sshprime.Po + -rm -f ./$(DEPDIR)/sshprng.Po + -rm -f ./$(DEPDIR)/sshpubk.Po + -rm -f ./$(DEPDIR)/sshrand.Po + -rm -f ./$(DEPDIR)/sshrsa.Po + -rm -f ./$(DEPDIR)/sshrsag.Po + -rm -f ./$(DEPDIR)/sshserver.Po + -rm -f ./$(DEPDIR)/sshsh256.Po + -rm -f ./$(DEPDIR)/sshsh512.Po + -rm -f ./$(DEPDIR)/sshsha.Po + -rm -f ./$(DEPDIR)/sshsha3.Po + -rm -f ./$(DEPDIR)/sshshare.Po + -rm -f ./$(DEPDIR)/sshutils.Po + -rm -f ./$(DEPDIR)/sshverstring.Po + -rm -f ./$(DEPDIR)/sshzlib.Po + -rm -f ./$(DEPDIR)/stripctrl.Po + -rm -f ./$(DEPDIR)/supdup.Po + -rm -f ./$(DEPDIR)/telnet.Po + -rm -f ./$(DEPDIR)/terminal.Po + -rm -f ./$(DEPDIR)/testcrypt.Po + -rm -f ./$(DEPDIR)/testsc.Po + -rm -f ./$(DEPDIR)/testzlib.Po + -rm -f ./$(DEPDIR)/time.Po + -rm -f ./$(DEPDIR)/timing.Po + -rm -f ./$(DEPDIR)/tree234.Po + -rm -f ./$(DEPDIR)/utils.Po + -rm -f ./$(DEPDIR)/wcwidth.Po + -rm -f ./$(DEPDIR)/wildcard.Po + -rm -f ./$(DEPDIR)/x11fwd.Po + -rm -f charset/$(DEPDIR)/fromucs.Po + -rm -f charset/$(DEPDIR)/localenc.Po + -rm -f charset/$(DEPDIR)/macenc.Po + -rm -f charset/$(DEPDIR)/mimeenc.Po + -rm -f charset/$(DEPDIR)/sbcs.Po + -rm -f charset/$(DEPDIR)/sbcsdat.Po + -rm -f charset/$(DEPDIR)/slookup.Po + -rm -f charset/$(DEPDIR)/toucs.Po + -rm -f charset/$(DEPDIR)/utf8.Po + -rm -f charset/$(DEPDIR)/xenc.Po + -rm -f unix/$(DEPDIR)/gtkapp.Po + -rm -f unix/$(DEPDIR)/gtkask.Po + -rm -f unix/$(DEPDIR)/gtkcfg.Po + -rm -f unix/$(DEPDIR)/gtkcols.Po + -rm -f unix/$(DEPDIR)/gtkcomm.Po + -rm -f unix/$(DEPDIR)/gtkdlg.Po + -rm -f unix/$(DEPDIR)/gtkfont.Po + -rm -f unix/$(DEPDIR)/gtkmain.Po + -rm -f unix/$(DEPDIR)/gtkmisc.Po + -rm -f unix/$(DEPDIR)/gtkwin.Po + -rm -f unix/$(DEPDIR)/osxlaunch.Po + -rm -f unix/$(DEPDIR)/procnet.Po + -rm -f unix/$(DEPDIR)/ux_x11.Po + -rm -f unix/$(DEPDIR)/uxagentc.Po + -rm -f unix/$(DEPDIR)/uxagentsock.Po + -rm -f unix/$(DEPDIR)/uxcfg.Po + -rm -f unix/$(DEPDIR)/uxcliloop.Po + -rm -f unix/$(DEPDIR)/uxcons.Po + -rm -f unix/$(DEPDIR)/uxfdsock.Po + -rm -f unix/$(DEPDIR)/uxgen.Po + -rm -f unix/$(DEPDIR)/uxgss.Po + -rm -f unix/$(DEPDIR)/uxmisc.Po + -rm -f unix/$(DEPDIR)/uxnet.Po + -rm -f unix/$(DEPDIR)/uxnogtk.Po + -rm -f unix/$(DEPDIR)/uxnoise.Po + -rm -f unix/$(DEPDIR)/uxpeer.Po + -rm -f unix/$(DEPDIR)/uxpgnt.Po + -rm -f unix/$(DEPDIR)/uxplink.Po + -rm -f unix/$(DEPDIR)/uxpoll.Po + -rm -f unix/$(DEPDIR)/uxprint.Po + -rm -f unix/$(DEPDIR)/uxproxy.Po + -rm -f unix/$(DEPDIR)/uxpsusan.Po + -rm -f unix/$(DEPDIR)/uxpterm.Po + -rm -f unix/$(DEPDIR)/uxpty.Po + -rm -f unix/$(DEPDIR)/uxputty.Po + -rm -f unix/$(DEPDIR)/uxsel.Po + -rm -f unix/$(DEPDIR)/uxser.Po + -rm -f unix/$(DEPDIR)/uxserver.Po + -rm -f unix/$(DEPDIR)/uxsftp.Po + -rm -f unix/$(DEPDIR)/uxsftpserver.Po + -rm -f unix/$(DEPDIR)/uxshare.Po + -rm -f unix/$(DEPDIR)/uxsignal.Po + -rm -f unix/$(DEPDIR)/uxsocks.Po + -rm -f unix/$(DEPDIR)/uxstore.Po + -rm -f unix/$(DEPDIR)/uxucs.Po + -rm -f unix/$(DEPDIR)/uxutils.Po + -rm -f unix/$(DEPDIR)/x11misc.Po + -rm -f unix/$(DEPDIR)/xkeysym.Po + -rm -f unix/$(DEPDIR)/xpmptcfg.Po + -rm -f unix/$(DEPDIR)/xpmpterm.Po + -rm -f unix/$(DEPDIR)/xpmpucfg.Po + -rm -f unix/$(DEPDIR)/xpmputty.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -2224,10 +2897,10 @@ uninstall-man: uninstall-man1 .MAKE: all check check-am install install-am install-strip -.PHONY: CTAGS GTAGS TAGS all all-am am--refresh check check-am \ - check-local clean clean-binPROGRAMS clean-cscope clean-generic \ - clean-noinstLIBRARIES clean-noinstPROGRAMS cscope \ - cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \ + check-am check-local clean clean-binPROGRAMS clean-cscope \ + clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS \ + cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-compile distclean-generic \ distclean-hdr distclean-tags distcleancheck distdir \ diff --git a/README.md b/README.md deleted file mode 100644 index d0f6ca7..0000000 --- a/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# putty4far2l -**Readme in english is below** - -Форк [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) 0.73 с поддержкой [расширений](https://github.com/cyd01/KiTTY/issues/74#issuecomment-626917718) терминала [far2l](https://github.com/elfmz/far2l) (на данный момент готова поддержка сочетаний клавиш, необходимых far2l, и синхронизация буфера обмена). На данный момент расширения работают только в версии putty для Windows (поддержка расширений в сборке для unix-like систем пока на очень ранней стадии реализации), используйте встроенный терминал графической версии far2l на unix-подобных системах. - -[putty.exe для x86](https://github.com/unxed/putty4far2l/raw/master/windows/putty.exe) - -Все изменения сосредоточены в файлах `windows/window.c` и `terminal.c`, ищите по строчке `far2l`. - -Кросс-компиляция на Ubuntu 18.04: -``` -sudo apt install mingw-w64 -git clone https://github.com/unxed/putty4far2l.git -cd putty4far2l/windows -``` - -А потом, для сборки версии для x86: - -`make TOOLPATH=i686-w64-mingw32- -f Makefile.mgw putty.exe` - -Или для x86_64: - -`make TOOLPATH=x86_64-w64-mingw32- -f Makefile.mgw putty.exe` - -Если вы планируете собирать PuTTY на Linux и тестировать в wine (я именно так и делаю), может потребоваться [снять все галки](https://bugs.winehq.org/show_bug.cgi?id=48196) в Connection-SSH-Auth-GSSAPI, а то будет вылетать (или же сделать `sudo apt install libkrb5-3:i386 libgssapi-krb5-2:i386`). - -Штуки, которые можно улучшить (конкретных планов по ним, впрочем, у меня нет): -- Лучше обрабатывать ошибки -- Доработать поддержку буфера обмена (запоминать разрешения доступа для конкретных клиентов) -- Выделять буфер APC динамически (сейчас лимит объема загружаемого с сервера буфера обмена ~680Кб) -- Добавить поддержку других типов запросов (события мышки, уведомления etc) -- Добавить поддержку escape последовательностей управления формой курсора -- Отображать псевдографику без сглаживания (или можно использовать шрифт [Consolas](https://en.wikipedia.org/wiki/Consolas) на Windows :) - -Вся остальная информация - в оригинальном [PuTTY README](https://github.com/unxed/putty4far2l/blob/master/README). - ---- - -[PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) 0.73 downstream fork with [far2l](https://github.com/elfmz/far2l) terminal -[extensions](https://github.com/cyd01/KiTTY/issues/74#issuecomment-626917718) (support for required by far2l key combinations and clipboard sync support are ready for now). Extensions currently work only in Windows putty version (support for extensions in build for unix-like systems is still in preliminary state), use built-in terminal of graphical far2l version on unix-like systems. - -[putty.exe for x86](https://github.com/unxed/putty4far2l/raw/master/windows/putty.exe) - -All changes are in files `windows/window.c` and `terminal.c`, you may search by `far2l` string to find them. - -Cross-compilation on Ubuntu 18.04: -``` -sudo apt install mingw-w64 -git clone https://github.com/unxed/putty4far2l.git -cd putty4far2l/windows -``` - -And then, for x86: - -`make TOOLPATH=i686-w64-mingw32- -f Makefile.mgw putty.exe` - -Or for x86_64: - -`make TOOLPATH=x86_64-w64-mingw32- -f Makefile.mgw putty.exe` - -If you plan to build PuTTY on Linux and test in wine (as do I), you may need to [uncheck](https://bugs.winehq.org/show_bug.cgi?id=48196) all checkboxes in Connection-SSH-Auth-GSSAPI to avoid pagefaults (or do `sudo apt install libkrb5-3:i386 libgssapi-krb5-2:i386`). - -Things that can be made better (I have no concrete plans on it all, though): -- Better errors processing -- Better clipboard support (option to allow sync permanently for specific clients) -- Dynamic APC buffer allocation (current clipboard size download limit is ~680Kb) -- Other requests implementation (mouse, notifications, etc) -- Add cursor shape escape sequences support -- Display line drawing characters with antialiasing disabled (or just use [Consolas](https://en.wikipedia.org/wiki/Consolas) font on Windows :) - -For additional stuff see original [PuTTY README](https://github.com/unxed/putty4far2l/blob/master/README). - diff --git a/Recipe b/Recipe index 4bad3f3..ea561b6 100644 --- a/Recipe +++ b/Recipe @@ -97,11 +97,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=/DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # @@ -192,10 +187,10 @@ install-strip: # List the man pages for the automake makefile. !begin am if HAVE_GTK -man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 \ +man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 \ doc/pageant.1 doc/pterm.1 doc/putty.1 doc/puttytel.1 else -man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 +man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 endif !end @@ -244,32 +239,32 @@ TERMINAL = terminal stripctrl wcwidth logging tree234 minibidi # GUI front end and terminal emulator (putty, puttytel). GUITERM = TERMINAL window windlg winctrls sizetip winprint winutils - + wincfg sercfg winhelp winjump sessprep + + wincfg winhelp winjump sessprep winselgui # Same thing on Unix. -UXTERM = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs +UXTERM = TERMINAL uxcfg uxucs uxprint timing callback miscucs GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym + x11misc gtkcomm sessprep GTKMAIN = gtkmain cmdline # Non-SSH back ends (putty, puttytel, plink). -NONSSH = telnet raw rlogin ldisc pinger +NONSSH = telnet raw rlogin supdup ldisc pinger # SSH back end (putty, plink, pscp, psftp). ARITH = mpint ecc -SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512 +SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512 sshsha3 sshblake2 sshargon2 + sshrsa sshdss sshecc + sshdes sshblowf sshaes sshccp ssharcf + sshdh sshcrc sshcrcda sshauxcrypt + sshhmac -SSHCOMMON = sshcommon sshprng sshrand SSHCRYPTO +SSHCOMMON = sshcommon sshutils sshprng sshrand SSHCRYPTO + sshverstring + sshpubk sshzlib + sshmac marshal nullplug + sshgssc pgssapi wildcard ssh1censor ssh2censor ssh2bpp + ssh2transport ssh2transhk ssh2connection portfwd x11fwd - + ssh1connection ssh1bpp -SSH = SSHCOMMON ssh ssh2bpp-bare + + ssh1connection ssh1bpp ssh2bpp-bare +SSH = SSHCOMMON ssh + ssh1login ssh2userauth + pinger + sshshare aqsync agentf @@ -281,6 +276,9 @@ UXSSH = SSH uxnoise uxagentc uxgss uxshare # SFTP implementation (pscp, psftp). SFTP = psftpcommon sftp sftpcommon logging cmdline +# Components of the prime-generation system. +SSHPRIME = sshprime smallprimes primecandidate millerrabin pockle mpunsafe + # Miscellaneous objects appearing in all the utilities, or all the # network ones, or the Unix or Windows subsets of those in turn. MISC = misc utils marshal memory stripctrl wcwidth @@ -294,7 +292,7 @@ UXMISC = MISCNET UXMISCCOMMON uxproxy uxutils # SSH server. SSHSERVER = SSHCOMMON sshserver settings be_none logging ssh2kex-server - + ssh2userauth-server sshrsag sshprime ssh2connection-server + + ssh2userauth-server sshrsag SSHPRIME ssh2connection-server + sesschan sftpcommon sftpserver proxy cproxy ssh1login-server + ssh1connection-server scpserver @@ -312,15 +310,18 @@ LIBS = advapi32.lib user32.lib gdi32.lib comdlg32.lib # Network backend sets. This also brings in the relevant attachment # to proxy.c depending on whether we're crypto-avoidant or not. BE_ALL = be_all cproxy -BE_NOSSH = be_nossh nocproxy +BE_NOSSH = be_nossh norand nocproxy BE_SSH = be_ssh cproxy BE_NONE = be_none nocproxy # More backend sets, with the additional Windows serial-port module. W_BE_ALL = be_all_s winser cproxy -W_BE_NOSSH = be_nos_s winser nocproxy +W_BE_NOSSH = be_nos_s norand winser nocproxy # And with the Unix serial-port module. U_BE_ALL = be_all_s uxser cproxy -U_BE_NOSSH = be_nos_s uxser nocproxy +U_BE_NOSSH = be_nos_s norand uxser nocproxy + +# Auxiliary crypto modules used by key generators. +KEYGEN = sshrsag sshdssg sshecdsag # ------------------------------------------------------------ # Definitions of actual programs. The program name, followed by a @@ -330,23 +331,28 @@ U_BE_NOSSH = be_nos_s uxser nocproxy putty : [G] GUITERM NONSSH WINSSH W_BE_ALL WINMISC winx11 putty.res LIBS puttytel : [G] GUITERM NONSSH W_BE_NOSSH WINMISC puttytel.res nogss LIBS -plink : [C] winplink wincons NONSSH WINSSH W_BE_ALL logging WINMISC - + winx11 plink.res winnojmp sessprep noterm winnohlp LIBS +plink : [C] winplink wincons console NONSSH WINSSH W_BE_ALL logging WINMISC + + winx11 plink.res winnojmp sessprep noterm winnohlp winselcli + + clicons wincliloop console LIBS pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC - + pscp.res winnojmp winnohlp LIBS + + pscp.res winnojmp winnohlp winselcli clicons wincliloop + + console LIBS psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC - + psftp.res winnojmp winnohlp LIBS + + psftp.res winnojmp winnohlp winselcli clicons wincliloop + + console LIBS pageant : [G] winpgnt pageant sshrsa sshpubk sshdes ARITH sshmd5 version + tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 + sshsh512 winutils sshecc winmisc winmiscs winhelp conf pageant.res - + sshauxcrypt sshhmac LIBS + + sshauxcrypt sshhmac wincapi winnps winnpc winhsock errsock winnet + + winhandl callback be_misc winselgui winhandl sshsha3 sshblake2 + + sshargon2 LIBS -puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes ARITH sshmd5 version +puttygen : [G] winpgen KEYGEN SSHPRIME sshdes ARITH sshmd5 version + sshrand winnoise sshsha winstore MISC winctrls sshrsa sshdss winmisc + sshpubk sshaes sshsh256 sshsh512 IMPORT winutils puttygen.res + tree234 notiming winhelp winnojmp CONF LIBS wintime sshecc sshprng - + sshecdsag sshauxcrypt sshhmac winsecur winmiscs + + sshauxcrypt sshhmac winsecur winmiscs sshsha3 sshblake2 sshargon2 pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg @@ -359,24 +365,27 @@ puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH + nogss utils memory GTKMAIN plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal - + ux_x11 noterm uxnogtk sessprep cmdline + + ux_x11 noterm uxnogtk sessprep cmdline clicons uxcliloop console -PUTTYGEN_UNIX = sshrsag sshdssg sshprime sshdes ARITH sshmd5 version sshprng +PUTTYGEN_UNIX = KEYGEN SSHPRIME sshdes ARITH sshmd5 version sshprng + sshrand uxnoise sshsha MISC sshrsa sshdss uxcons uxstore uxmisc + sshpubk sshaes sshsh256 sshsh512 IMPORT puttygen.res time tree234 - + uxgen notiming CONF sshecc sshecdsag uxnogtk sshauxcrypt sshhmac - + uxpoll uxutils + + uxgen notiming CONF sshecc sshsha3 uxnogtk sshauxcrypt sshhmac + + uxpoll uxutils sshblake2 sshargon2 console puttygen : [U] cmdgen PUTTYGEN_UNIX cgtest : [UT] cgtest PUTTYGEN_UNIX pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC uxnogtk + + clicons uxcliloop console psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC uxnogtk + + clicons uxcliloop console pageant : [X] uxpgnt uxagentc aqsync pageant sshrsa sshpubk sshdes ARITH + sshmd5 version tree234 misc sshaes sshsha sshdss sshsh256 sshsh512 + sshecc CONF uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons + gtkask gtkmisc nullplug logging UXMISC uxagentsock utils memory - + sshauxcrypt sshhmac sshprng uxnoise + + sshauxcrypt sshhmac sshprng uxnoise uxcliloop sshsha3 sshblake2 + + sshargon2 console ptermapp : [XT] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET uxpterm version time xpmpterm xpmptcfg @@ -388,16 +397,25 @@ osxlaunch : [UT] osxlaunch fuzzterm : [UT] UXTERM CHARSET MISC version uxmisc uxucs fuzzterm time settings + uxstore be_none uxnogtk memory -testcrypt : [UT] testcrypt SSHCRYPTO sshprng sshprime marshal utils - + memory tree234 uxutils -testcrypt : [C] testcrypt SSHCRYPTO sshprng sshprime marshal utils - + memory tree234 winmiscs +testcrypt : [UT] testcrypt SSHCRYPTO sshprng SSHPRIME sshpubk marshal utils + + memory tree234 uxutils KEYGEN +testcrypt : [C] testcrypt SSHCRYPTO sshprng SSHPRIME sshpubk marshal utils + + memory tree234 winmiscs KEYGEN testsc : [UT] testsc SSHCRYPTO marshal utils memory tree234 wildcard - + sshmac uxutils + + sshmac uxutils sshpubk testzlib : [UT] testzlib sshzlib utils marshal memory uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk - + uxpty uxsftpserver ux_x11 uxagentsock procnet + + uxpty uxsftpserver ux_x11 uxagentsock procnet uxcliloop +psusan : [U] uxpsusan SSHSERVER UXMISC uxsignal uxnoise nogss uxnogtk + + uxpty uxsftpserver ux_x11 uxagentsock procnet uxcliloop + +PSOCKS = psocks portfwd conf sshutils logging proxy nocproxy timing callback + + time tree234 version errsock be_misc norand MISC +psocks : [C] PSOCKS winsocks wincons winproxy winnet winmisc winselcli + + winhsock winhandl winmiscs winnohlp wincliloop console LIBS +psocks : [UT] PSOCKS uxsocks uxcons uxproxy uxnet uxmisc uxpoll uxsel uxnogtk + + uxpeer uxfdsock uxcliloop uxsignal console # ---------------------------------------------------------------------- # On Windows, provide a means of removing local test binaries that we @@ -405,9 +423,9 @@ uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk # in the first place, so that we find out about build breakage early.) !begin vc cleantestprogs: - -del $(BUILDDIR)testcrypt.exe + -del $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe !end !begin clangcl cleantestprogs: - -rm -f $(BUILDDIR)testcrypt.exe + -rm -f $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe !end diff --git a/aclocal.m4 b/aclocal.m4 index 598c657..66dbd88 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15.1 -*- Autoconf -*- +# generated automatically by aclocal 1.16.1 -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -23,6 +23,10 @@ To do so, use the procedure documented by the package, typically 'autoreconf'.]) # Configure paths for GTK+ # Owen Taylor 1997-2001 +# Version number used by aclocal, see `info automake Serials`. +# Increment on every change. +#serial 1 + dnl AM_PATH_GTK_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) dnl Test for GTK+, and define GTK_CFLAGS and GTK_LIBS, if gthread is specified in MODULES, dnl pass to pkg-config @@ -209,6 +213,10 @@ main () # Configure paths for GTK+ # Owen Taylor 1997-2001 +# Version number used by aclocal, see `info automake Serials`. +# Increment on every change. +#serial 1 + dnl AM_PATH_GTK_3_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) dnl Test for GTK+, and define GTK_CFLAGS and GTK_LIBS, if gthread is specified in MODULES, dnl pass to pkg-config @@ -693,7 +701,7 @@ AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR -# Copyright (C) 2002-2017 Free Software Foundation, Inc. +# Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -705,10 +713,10 @@ AS_VAR_IF([$1], [""], [$5], [$4])dnl # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.15' +[am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.15.1], [], +m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -724,12 +732,12 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.15.1])dnl +[AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) -# Copyright (C) 2011-2017 Free Software Foundation, Inc. +# Copyright (C) 2011-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -791,7 +799,7 @@ AC_SUBST([AR])dnl # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -843,7 +851,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -874,7 +882,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1065,13 +1073,12 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. - # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], @@ -1079,49 +1086,41 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS @@ -1130,18 +1129,17 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # -# This code is only required when automatic dependency tracking -# is enabled. FIXME. This creates each '.P' file that we will -# need in order to bootstrap the dependency handling code. +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], - [AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir"]) -]) + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1228,8 +1226,8 @@ AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. @@ -1296,7 +1294,7 @@ END Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -1338,7 +1336,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1359,7 +1357,7 @@ if test x"${install_sh+set}" != xset; then fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2017 Free Software Foundation, Inc. +# Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1380,7 +1378,7 @@ AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1388,49 +1386,42 @@ AC_SUBST([am__leading_dot])]) # AM_MAKE_INCLUDE() # ----------------- -# Check to see how make treats includes. +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], -[am_make=${MAKE-make} -cat > confinc << 'END' +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -AC_MSG_CHECKING([for style of include used by $am_make]) am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD - ;; - esac -fi -AC_SUBST([am__include]) -AC_SUBST([am__quote]) -AC_MSG_RESULT([$_am_result]) -rm -f confinc confmf -]) +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2017 Free Software Foundation, Inc. +# Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1469,7 +1460,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1498,7 +1489,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1545,7 +1536,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1564,7 +1555,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1645,7 +1636,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2017 Free Software Foundation, Inc. +# Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1705,7 +1696,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2017 Free Software Foundation, Inc. +# Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1733,7 +1724,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2017 Free Software Foundation, Inc. +# Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1752,7 +1743,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2017 Free Software Foundation, Inc. +# Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/agentf.c b/agentf.c index 4069fee..dc5bec0 100644 --- a/agentf.c +++ b/agentf.c @@ -147,29 +147,29 @@ static void agentf_send_eof(Channel *chan); static char *agentf_log_close_msg(Channel *chan); static void agentf_set_input_wanted(Channel *chan, bool wanted); -static const struct ChannelVtable agentf_channelvt = { - agentf_free, - chan_remotely_opened_confirmation, - chan_remotely_opened_failure, - agentf_send, - agentf_send_eof, - agentf_set_input_wanted, - agentf_log_close_msg, - chan_default_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - chan_no_request_response, +static const ChannelVtable agentf_channelvt = { + .free = agentf_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = agentf_send, + .send_eof = agentf_send_eof, + .set_input_wanted = agentf_set_input_wanted, + .log_close_msg = agentf_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, }; Channel *agentf_new(SshChannel *c) diff --git a/ar-lib b/ar-lib index 05094d3..0baa4f6 100755 --- a/ar-lib +++ b/ar-lib @@ -4,7 +4,7 @@ me=ar-lib scriptversion=2012-03-01.08; # UTC -# Copyright (C) 2010-2017 Free Software Foundation, Inc. +# Copyright (C) 2010-2018 Free Software Foundation, Inc. # Written by Peter Rosin . # # This program is free software; you can redistribute it and/or modify @@ -18,7 +18,7 @@ scriptversion=2012-03-01.08; # UTC # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a diff --git a/be_all.c b/be_all.c index 9673c79..668541b 100644 --- a/be_all.c +++ b/be_all.c @@ -16,16 +16,16 @@ */ const char *const appname = "PuTTY"; -#ifdef TELNET_DEFAULT -const int be_default_protocol = PROT_TELNET; -#else const int be_default_protocol = PROT_SSH; -#endif const struct BackendVtable *const backends[] = { &ssh_backend, &telnet_backend, &rlogin_backend, + &supdup_backend, &raw_backend, + &sshconn_backend, NULL }; + +const size_t n_ui_backends = 1; diff --git a/be_all_s.c b/be_all_s.c index 2f140ec..4e50293 100644 --- a/be_all_s.c +++ b/be_all_s.c @@ -16,17 +16,17 @@ */ const char *const appname = "PuTTY"; -#ifdef TELNET_DEFAULT -const int be_default_protocol = PROT_TELNET; -#else const int be_default_protocol = PROT_SSH; -#endif const struct BackendVtable *const backends[] = { &ssh_backend, + &serial_backend, &telnet_backend, &rlogin_backend, + &supdup_backend, &raw_backend, - &serial_backend, + &sshconn_backend, NULL }; + +const size_t n_ui_backends = 2; diff --git a/be_misc.c b/be_misc.c index 9bd3bdd..7f50a64 100644 --- a/be_misc.c +++ b/be_misc.c @@ -9,14 +9,14 @@ #include "network.h" void backend_socket_log(Seat *seat, LogContext *logctx, - int type, SockAddr *addr, int port, + PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code, Conf *conf, bool session_started) { char addrbuf[256], *msg; switch (type) { - case 0: + case PLUGLOG_CONNECT_TRYING: sk_getaddr(addr, addrbuf, lenof(addrbuf)); if (sk_addr_needs_port(addr)) { msg = dupprintf("Connecting to %s port %d", addrbuf, port); @@ -24,30 +24,33 @@ void backend_socket_log(Seat *seat, LogContext *logctx, msg = dupprintf("Connecting to %s", addrbuf); } break; - case 1: + case PLUGLOG_CONNECT_FAILED: sk_getaddr(addr, addrbuf, lenof(addrbuf)); msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); break; - case 2: + case PLUGLOG_CONNECT_SUCCESS: + sk_getaddr(addr, addrbuf, lenof(addrbuf)); + msg = dupprintf("Connected to %s", addrbuf); + break; + case PLUGLOG_PROXY_MSG: { /* Proxy-related log messages have their own identifying * prefix already, put on by our caller. */ - { - int len, log_to_term; + int len, log_to_term; - /* Suffix \r\n temporarily, so we can log to the terminal. */ - msg = dupprintf("%s\r\n", error_msg); - len = strlen(msg); - assert(len >= 2); + /* Suffix \r\n temporarily, so we can log to the terminal. */ + msg = dupprintf("%s\r\n", error_msg); + len = strlen(msg); + assert(len >= 2); - log_to_term = conf_get_int(conf, CONF_proxy_log_to_term); - if (log_to_term == AUTO) - log_to_term = session_started ? FORCE_OFF : FORCE_ON; - if (log_to_term == FORCE_ON) - seat_stderr(seat, msg, len); + log_to_term = conf_get_int(conf, CONF_proxy_log_to_term); + if (log_to_term == AUTO) + log_to_term = session_started ? FORCE_OFF : FORCE_ON; + if (log_to_term == FORCE_ON) + seat_stderr(seat, msg, len); - msg[len-2] = '\0'; /* remove the \r\n again */ - } + msg[len-2] = '\0'; /* remove the \r\n again */ break; + } default: msg = NULL; /* shouldn't happen, but placate optimiser */ break; @@ -114,7 +117,7 @@ void log_proxy_stderr(Plug *plug, ProxyStderrBuf *psb, endpos--; char *msg = dupprintf( "proxy: %.*s", (int)(endpos - pos), psb->buf + pos); - plug_log(plug, 2, NULL, 0, msg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); sfree(msg); pos = nlpos - psb->buf + 1; @@ -129,7 +132,7 @@ void log_proxy_stderr(Plug *plug, ProxyStderrBuf *psb, if (pos == 0 && psb->size == lenof(psb->buf)) { char *msg = dupprintf( "proxy (partial line): %.*s", (int)psb->size, psb->buf); - plug_log(plug, 2, NULL, 0, msg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); sfree(msg); pos = psb->size = 0; diff --git a/be_none.c b/be_none.c index abc0551..588bb1d 100644 --- a/be_none.c +++ b/be_none.c @@ -6,6 +6,10 @@ #include #include "putty.h" +const int be_default_protocol = -1; + const struct BackendVtable *const backends[] = { NULL }; + +const size_t n_ui_backends = 0; diff --git a/be_nos_s.c b/be_nos_s.c index 15906cf..097433a 100644 --- a/be_nos_s.c +++ b/be_nos_s.c @@ -12,23 +12,11 @@ const char *const appname = "PuTTYtel"; const struct BackendVtable *const backends[] = { &telnet_backend, + &serial_backend, &rlogin_backend, + &supdup_backend, &raw_backend, - &serial_backend, NULL }; -/* - * Stub implementations of functions not used in non-ssh versions. - */ -void random_save_seed(void) -{ -} - -void random_destroy_seed(void) -{ -} - -void noise_ultralight(NoiseSourceId id, unsigned long data) -{ -} +const size_t n_ui_backends = 2; diff --git a/be_nossh.c b/be_nossh.c index 184f28d..1c94f3a 100644 --- a/be_nossh.c +++ b/be_nossh.c @@ -13,21 +13,9 @@ const char *const appname = "PuTTYtel"; const struct BackendVtable *const backends[] = { &telnet_backend, &rlogin_backend, + &supdup_backend, &raw_backend, NULL }; -/* - * Stub implementations of functions not used in non-ssh versions. - */ -void random_save_seed(void) -{ -} - -void random_destroy_seed(void) -{ -} - -void noise_ultralight(NoiseSourceId id, unsigned long data) -{ -} +const size_t n_ui_backends = 1; diff --git a/be_ssh.c b/be_ssh.c index 40737e5..81be62d 100644 --- a/be_ssh.c +++ b/be_ssh.c @@ -1,8 +1,7 @@ /* - * Linking module for programs that are restricted to only using SSH - * (pscp and psftp). These do not support selection of backend, but - * must still have a backends[] array mentioning SSH because - * settings.c will want to consult it during session load. + * Linking module for programs that are restricted to only using + * SSH-type protocols (pscp and psftp). These still have a choice of + * two actual backends, because they can also speak PROT_SSHCONN. */ #include @@ -12,5 +11,8 @@ const int be_default_protocol = PROT_SSH; const struct BackendVtable *const backends[] = { &ssh_backend, + &sshconn_backend, NULL }; + +const size_t n_ui_backends = 0; /* not used in programs with a config UI */ diff --git a/callback.c b/callback.c index 076d7b4..ea647af 100644 --- a/callback.c +++ b/callback.c @@ -14,10 +14,10 @@ struct callback { void *ctx; }; -struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL; +static struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL; -toplevel_callback_notify_fn_t notify_frontend = NULL; -void *notify_ctx = NULL; +static toplevel_callback_notify_fn_t notify_frontend = NULL; +static void *notify_ctx = NULL; void request_callback_notifications(toplevel_callback_notify_fn_t fn, void *ctx) diff --git a/cgtest.c b/cgtest.c index 095ce72..ba87892 100644 --- a/cgtest.c +++ b/cgtest.c @@ -2,5 +2,787 @@ * cgtest.c: stub file to compile cmdgen.c in self-test mode */ -#define TEST_CMDGEN +/* + * Before we #include cmdgen.c, we override some function names for + * test purposes. We do this via #define, so that when we link against + * modules containing the original versions, we don't get a link-time + * symbol clash: + * + * - Calls to get_random_data() are replaced with the diagnostic + * function below, in order to avoid depleting the test system's + * /dev/random unnecessarily. + * + * - Calls to console_get_userpass_input() are replaced with the + * diagnostic function below, so that I can run tests in an + * automated manner and provide their interactive passphrase + * inputs. + * + * - The main() defined by cmdgen.c is renamed to cmdgen_main(); in + * this file I define another main() which calls the former + * repeatedly to run tests. + */ +#define get_random_data get_random_data_diagnostic +#define console_get_userpass_input console_get_userpass_input_diagnostic +#define main cmdgen_main +#define ppk_save_default_parameters ppk_save_cgtest_parameters + #include "cmdgen.c" + +#undef get_random_data +#undef console_get_userpass_input +#undef main + +static bool cgtest_verbose = false; + +const struct ppk_save_parameters ppk_save_cgtest_parameters = { + /* Replacement set of key derivation parameters that make this + * test suite run a bit faster and also add determinism: we don't + * try to auto-scale the number of passes (in case it gets + * different answers twice in the test suite when we were + * expecting two key files to compare equal), and we specify a + * passphrase salt. */ + .fmt_version = 3, + .argon2_flavour = Argon2id, + .argon2_mem = 16, + .argon2_passes_auto = false, + .argon2_passes = 2, + .argon2_parallelism = 1, + .salt = (const uint8_t *)"SameSaltEachTime", + .saltlen = 16, +}; + +/* + * Define the special versions of get_random_data and + * console_get_userpass_input that we need for this test rig. + */ + +char *get_random_data_diagnostic(int len, const char *device) +{ + char *buf = snewn(len, char); + memset(buf, 'x', len); + return buf; +} + +static int nprompts, promptsgot; +static const char *prompts[3]; +int console_get_userpass_input_diagnostic(prompts_t *p) +{ + size_t i; + int ret = 1; + for (i = 0; i < p->n_prompts; i++) { + if (promptsgot < nprompts) { + prompt_set_result(p->prompts[i], prompts[promptsgot++]); + if (cgtest_verbose) + printf(" prompt \"%s\": response \"%s\"\n", + p->prompts[i]->prompt, p->prompts[i]->result->s); + } else { + promptsgot++; /* track number of requests anyway */ + ret = 0; + if (cgtest_verbose) + printf(" prompt \"%s\": no response preloaded\n", + p->prompts[i]->prompt); + } + } + return ret; +} + +#include + +static int passes, fails; + +void setup_passphrases(char *first, ...) +{ + va_list ap; + char *next; + + nprompts = 0; + if (first) { + prompts[nprompts++] = first; + va_start(ap, first); + while ((next = va_arg(ap, char *)) != NULL) { + assert(nprompts < lenof(prompts)); + prompts[nprompts++] = next; + } + va_end(ap); + } +} + +void test(int retval, ...) +{ + va_list ap; + int i, argc, ret; + char **argv; + + argc = 0; + va_start(ap, retval); + while (va_arg(ap, char *) != NULL) + argc++; + va_end(ap); + + argv = snewn(argc+1, char *); + va_start(ap, retval); + for (i = 0; i <= argc; i++) + argv[i] = va_arg(ap, char *); + va_end(ap); + + promptsgot = 0; + if (cgtest_verbose) { + printf("run:"); + for (int i = 0; i < argc; i++) { + static const char okchars[] = + "0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ%+,-./:=[]^_"; + const char *arg = argv[i]; + + printf(" "); + if (arg[strspn(arg, okchars)]) { + printf("'"); + for (const char *c = argv[i]; *c; c++) { + if (*c == '\'') { + printf("'\\''"); + } else { + putchar(*c); + } + } + printf("'"); + } else { + fputs(arg, stdout); + } + } + printf("\n"); + } + ret = cmdgen_main(argc, argv); + random_clear(); + + if (ret != retval) { + printf("FAILED retval (exp %d got %d):", retval, ret); + for (i = 0; i < argc; i++) + printf(" %s", argv[i]); + printf("\n"); + fails++; + } else if (promptsgot != nprompts) { + printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot); + for (i = 0; i < argc; i++) + printf(" %s", argv[i]); + printf("\n"); + fails++; + } else { + passes++; + } + + sfree(argv); +} + +PRINTF_LIKE(3, 4) void filecmp(char *file1, char *file2, char *fmt, ...) +{ + /* + * Ideally I should do file comparison myself, to maximise the + * portability of this test suite once this application begins + * running on non-Unix platforms. For the moment, though, + * calling Unix diff is perfectly adequate. + */ + char *buf; + int ret; + + buf = dupprintf("diff -q '%s' '%s'", file1, file2); + ret = system(buf); + sfree(buf); + + if (ret) { + va_list ap; + + printf("FAILED diff (ret=%d): ", ret); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + printf("\n"); + + fails++; + } else + passes++; +} + +/* + * General-purpose flags word + */ +#define CGT_FLAGS(X) \ + X(CGT_TYPE_KNOWN_EARLY) \ + X(CGT_OPENSSH) \ + X(CGT_SSHCOM) \ + X(CGT_SSH_KEYGEN) \ + X(CGT_ED25519) \ + /* end of list */ + +#define FLAG_SHIFTS(name) name ## _shift, +enum { CGT_FLAGS(FLAG_SHIFTS) CGT_dummy_shift }; +#define FLAG_VALUES(name) name = 1 << name ## _shift, +enum { CGT_FLAGS(FLAG_VALUES) CGT_dummy_flag }; + +char *cleanup_fp(char *s, unsigned flags) +{ + ptrlen pl = ptrlen_from_asciz(s); + static const char separators[] = " \n\t"; + + /* Skip initial key type word if we find one */ + if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL) || + ptrlen_startswith(pl, PTRLEN_LITERAL("ecdsa-"), NULL)) + ptrlen_get_word(&pl, separators); + + /* Expect two words giving the key length and the hash */ + ptrlen bits = ptrlen_get_word(&pl, separators); + ptrlen hash = ptrlen_get_word(&pl, separators); + + if (flags & CGT_SSH_KEYGEN) { + /* Strip "MD5:" prefix if it's present, and do nothing if it isn't */ + ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash); + + if (flags & CGT_ED25519) { + /* OpenSSH ssh-keygen lists ed25519 keys as 256 bits, not 255 */ + if (ptrlen_eq_string(bits, "256")) + bits = PTRLEN_LITERAL("255"); + } + } + + return dupprintf("%.*s %.*s", PTRLEN_PRINTF(bits), PTRLEN_PRINTF(hash)); +} + +char *get_line(char *filename) +{ + FILE *fp; + char *line; + + fp = fopen(filename, "r"); + if (!fp) + return NULL; + line = fgetline(fp); + fclose(fp); + return line; +} + +char *get_fp(char *filename, unsigned flags) +{ + char *orig = get_line(filename); + if (!orig) + return NULL; + char *toret = cleanup_fp(orig, flags); + sfree(orig); + return toret; +} + +PRINTF_LIKE(3, 4) void check_fp(char *filename, char *fp, char *fmt, ...) +{ + char *newfp; + + if (!fp) + return; + + newfp = get_fp(filename, 0); + + if (!strcmp(fp, newfp)) { + passes++; + } else { + va_list ap; + + printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp); + + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + + printf("\n"); + + fails++; + } + + sfree(newfp); +} + +static const struct cgtest_keytype { + const char *name; + unsigned flags; +} cgtest_keytypes[] = { + { "rsa1", CGT_TYPE_KNOWN_EARLY }, + { "dsa", CGT_OPENSSH | CGT_SSHCOM }, + { "rsa", CGT_OPENSSH | CGT_SSHCOM }, + { "ecdsa", CGT_OPENSSH }, + { "ed25519", CGT_OPENSSH | CGT_ED25519 }, +}; + +int main(int argc, char **argv) +{ + int i; + int active[lenof(cgtest_keytypes)], active_value; + bool remove_files = true; + + active_value = 0; + for (i = 0; i < lenof(cgtest_keytypes); i++) + active[i] = active_value; + + while (--argc > 0) { + ptrlen arg = ptrlen_from_asciz(*++argv); + if (ptrlen_eq_string(arg, "-v") || + ptrlen_eq_string(arg, "--verbose")) { + cgtest_verbose = true; + } else if (ptrlen_eq_string(arg, "--keep")) { + remove_files = false; + } else if (ptrlen_eq_string(arg, "--help")) { + printf("usage: cgtest [options] [key types]\n"); + printf("options: -v, --verbose " + "print more output during tests\n"); + printf(" --keep " + "do not delete the temporary output files\n"); + printf(" --help " + "display this help text\n"); + printf("key types: "); + for (i = 0; i < lenof(cgtest_keytypes); i++) + printf("%s%s", i ? ", " : "", cgtest_keytypes[i].name); + printf("\n"); + return 0; + } else if (!ptrlen_startswith(arg, PTRLEN_LITERAL("-"), NULL)) { + for (i = 0; i < lenof(cgtest_keytypes); i++) + if (ptrlen_eq_string(arg, cgtest_keytypes[i].name)) + break; + if (i == lenof(cgtest_keytypes)) { + fprintf(stderr, "cgtest: unrecognised key type '%.*s'\n", + PTRLEN_PRINTF(arg)); + return 1; + } + active_value = 1; /* disables all keys not explicitly enabled */ + active[i] = active_value; + } else { + fprintf(stderr, "cgtest: unrecognised option '%.*s'\n", + PTRLEN_PRINTF(arg)); + return 1; + } + } + + passes = fails = 0; + + for (i = 0; i < lenof(cgtest_keytypes); i++) { + if (active[i] != active_value) + continue; + + const struct cgtest_keytype *keytype = &cgtest_keytypes[i]; + bool supports_openssh = keytype->flags & CGT_OPENSSH; + bool supports_sshcom = keytype->flags & CGT_SSHCOM; + bool type_known_early = keytype->flags & CGT_TYPE_KNOWN_EARLY; + + char filename[128], osfilename[128], scfilename[128]; + char pubfilename[128], tmpfilename1[128], tmpfilename2[128]; + char *fps[SSH_N_FPTYPES]; + + sprintf(filename, "test-%s.ppk", keytype->name); + sprintf(pubfilename, "test-%s.pub", keytype->name); + sprintf(osfilename, "test-%s.os", keytype->name); + sprintf(scfilename, "test-%s.sc", keytype->name); + sprintf(tmpfilename1, "test-%s.tmp1", keytype->name); + sprintf(tmpfilename2, "test-%s.tmp2", keytype->name); + + /* + * Create an encrypted key. + */ + setup_passphrases("sponge", "sponge", NULL); + test(0, "puttygen", "-t", keytype->name, "-o", filename, NULL); + + /* + * List the public key in OpenSSH format. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL); + for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) { + const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256"); + char *cmdbuf; + char *fp = NULL; + cmdbuf = dupprintf("ssh-keygen -E %s -l -f '%s' > '%s'", + fpname, pubfilename, tmpfilename1); + if (cgtest_verbose) + printf("OpenSSH %s fp check: %s\n", fpname, cmdbuf); + if (system(cmdbuf) || + (fp = get_fp(tmpfilename1, + CGT_SSH_KEYGEN | keytype->flags)) == NULL) { + printf("UNABLE to test fingerprint matching against " + "OpenSSH\n"); + } + sfree(cmdbuf); + if (fp && cgtest_verbose) { + char *line = get_line(tmpfilename1); + printf("OpenSSH %s fp: %s\n", fpname, line); + printf("Cleaned up: %s\n", fp); + sfree(line); + } + fps[fptype] = fp; + } + + /* + * List the public key in IETF/ssh.com format. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-p", filename, NULL); + + /* + * List the fingerprint of the key. + */ + setup_passphrases(NULL); + for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) { + const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256"); + test(0, "puttygen", "-E", fpname, "-l", filename, + "-o", tmpfilename1, NULL); + if (!fps[fptype]) { + /* + * If we can't test fingerprints against OpenSSH, we + * can at the very least test equality of all the + * fingerprints we generate of this key throughout + * testing. + */ + fps[fptype] = get_fp(tmpfilename1, 0); + } else { + check_fp(tmpfilename1, fps[fptype], "%s initial %s fp", + keytype->name, fpname); + } + } + + /* + * Change the comment of the key; this _does_ require a + * passphrase owing to the tamperproofing. + * + * NOTE: In SSH-1, this only requires a passphrase because + * of inadequacies of the loading and saving mechanisms. In + * _principle_, it should be perfectly possible to modify + * the comment on an SSH-1 key without requiring a + * passphrase; the only reason I can't do it is because my + * loading and saving mechanisms don't include a method of + * loading all the key data without also trying to decrypt + * the private section. + * + * I don't consider this to be a problem worth solving, + * because (a) to fix it would probably end up bloating + * PuTTY proper, and (b) SSH-1 is on the way out anyway so + * it shouldn't be highly significant. If it seriously + * bothers anyone then perhaps I _might_ be persuadable. + */ + setup_passphrases("sponge", NULL); + test(0, "puttygen", "-C", "new-comment", filename, NULL); + + /* + * Change the passphrase to nothing. + */ + setup_passphrases("sponge", "", "", NULL); + test(0, "puttygen", "-P", filename, NULL); + + /* + * Change the comment of the key again; this time we expect no + * passphrase to be required. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-C", "new-comment-2", filename, NULL); + + /* + * Export the private key into OpenSSH format; no passphrase + * should be required since the key is currently unencrypted. + */ + setup_passphrases(NULL); + test(supports_openssh ? 0 : 1, + "puttygen", "-O", "private-openssh", "-o", osfilename, + filename, NULL); + + if (supports_openssh) { + /* + * List the fingerprint of the OpenSSH-formatted key. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL); + check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], + "%s openssh clear fp", keytype->name); + + /* + * List the public half of the OpenSSH-formatted key in + * OpenSSH format. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-L", osfilename, NULL); + + /* + * List the public half of the OpenSSH-formatted key in + * IETF/ssh.com format. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-p", osfilename, NULL); + } + + /* + * Export the private key into ssh.com format; no passphrase + * should be required since the key is currently unencrypted. + */ + setup_passphrases(NULL); + test(supports_sshcom ? 0 : 1, + "puttygen", "-O", "private-sshcom", + "-o", scfilename, filename, NULL); + + if (supports_sshcom) { + /* + * List the fingerprint of the ssh.com-formatted key. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL); + check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], + "%s ssh.com clear fp", keytype->name); + + /* + * List the public half of the ssh.com-formatted key in + * OpenSSH format. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-L", scfilename, NULL); + + /* + * List the public half of the ssh.com-formatted key in + * IETF/ssh.com format. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-p", scfilename, NULL); + } + + if (supports_openssh && supports_sshcom) { + /* + * Convert from OpenSSH into ssh.com. + */ + setup_passphrases(NULL); + test(0, "puttygen", osfilename, "-o", tmpfilename1, + "-O", "private-sshcom", NULL); + + /* + * Convert from ssh.com back into a PuTTY key, + * supplying the same comment as we had before we + * started to ensure the comparison works. + */ + setup_passphrases(NULL); + test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", + "-o", tmpfilename2, NULL); + + /* + * See if the PuTTY key thus generated is the same as + * the original. + */ + filecmp(filename, tmpfilename2, + "p->o->s->p clear %s", keytype->name); + + /* + * Convert from ssh.com to OpenSSH. + */ + setup_passphrases(NULL); + test(0, "puttygen", scfilename, "-o", tmpfilename1, + "-O", "private-openssh", NULL); + + /* + * Convert from OpenSSH back into a PuTTY key, + * supplying the same comment as we had before we + * started to ensure the comparison works. + */ + setup_passphrases(NULL); + test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", + "-o", tmpfilename2, NULL); + + /* + * See if the PuTTY key thus generated is the same as + * the original. + */ + filecmp(filename, tmpfilename2, + "p->s->o->p clear %s", keytype->name); + + /* + * Finally, do a round-trip conversion between PuTTY + * and ssh.com without involving OpenSSH, to test that + * the key comment is preserved in that case. + */ + setup_passphrases(NULL); + test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1, + filename, NULL); + setup_passphrases(NULL); + test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL); + filecmp(filename, tmpfilename2, + "p->s->p clear %s", keytype->name); + } + + /* + * Check that mismatched passphrases cause an error. + */ + setup_passphrases("sponge2", "sponge3", NULL); + test(1, "puttygen", "-P", filename, NULL); + + /* + * Put a passphrase back on. + */ + setup_passphrases("sponge2", "sponge2", NULL); + test(0, "puttygen", "-P", filename, NULL); + + /* + * Export the private key into OpenSSH format, this time + * while encrypted. + */ + if (!supports_openssh && type_known_early) { + /* We'll know far enough in advance that this combination + * is going to fail that we never ask for the passphrase */ + setup_passphrases(NULL); + } else { + setup_passphrases("sponge2", NULL); + } + + test(supports_openssh ? 0 : 1, + "puttygen", "-O", "private-openssh", "-o", osfilename, + filename, NULL); + + if (supports_openssh) { + /* + * List the fingerprint of the OpenSSH-formatted key. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL); + check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], + "%s openssh encrypted fp", keytype->name); + + /* + * List the public half of the OpenSSH-formatted key in + * OpenSSH format. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-L", osfilename, NULL); + + /* + * List the public half of the OpenSSH-formatted key in + * IETF/ssh.com format. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-p", osfilename, NULL); + } + + /* + * Export the private key into ssh.com format, this time + * while encrypted. For RSA1 keys, this should give an + * error. + */ + if (!supports_sshcom && type_known_early) { + /* We'll know far enough in advance that this combination + * is going to fail that we never ask for the passphrase */ + setup_passphrases(NULL); + } else { + setup_passphrases("sponge2", NULL); + } + + test(supports_sshcom ? 0 : 1, + "puttygen", "-O", "private-sshcom", "-o", scfilename, + filename, NULL); + + if (supports_sshcom) { + /* + * List the fingerprint of the ssh.com-formatted key. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL); + check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], + "%s ssh.com encrypted fp", keytype->name); + + /* + * List the public half of the ssh.com-formatted key in + * OpenSSH format. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-L", scfilename, NULL); + + /* + * List the public half of the ssh.com-formatted key in + * IETF/ssh.com format. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-p", scfilename, NULL); + } + + if (supports_openssh && supports_sshcom) { + /* + * Convert from OpenSSH into ssh.com. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", osfilename, "-o", tmpfilename1, + "-O", "private-sshcom", NULL); + + /* + * Convert from ssh.com back into a PuTTY key, + * supplying the same comment as we had before we + * started to ensure the comparison works. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", + "-o", tmpfilename2, NULL); + + /* + * See if the PuTTY key thus generated is the same as + * the original. + */ + filecmp(filename, tmpfilename2, + "p->o->s->p encrypted %s", keytype->name); + + /* + * Convert from ssh.com to OpenSSH. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", scfilename, "-o", tmpfilename1, + "-O", "private-openssh", NULL); + + /* + * Convert from OpenSSH back into a PuTTY key, + * supplying the same comment as we had before we + * started to ensure the comparison works. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", + "-o", tmpfilename2, NULL); + + /* + * See if the PuTTY key thus generated is the same as + * the original. + */ + filecmp(filename, tmpfilename2, + "p->s->o->p encrypted %s", keytype->name); + + /* + * Finally, do a round-trip conversion between PuTTY + * and ssh.com without involving OpenSSH, to test that + * the key comment is preserved in that case. + */ + setup_passphrases("sponge2", NULL); + test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1, + filename, NULL); + setup_passphrases("sponge2", NULL); + test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL); + filecmp(filename, tmpfilename2, + "p->s->p encrypted %s", keytype->name); + } + + /* + * Load with the wrong passphrase. + */ + setup_passphrases("sponge8", NULL); + test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL); + + /* + * Load a totally bogus file. + */ + setup_passphrases(NULL); + test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL); + + for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) + sfree(fps[fptype]); + + if (remove_files) { + remove(filename); + remove(pubfilename); + remove(osfilename); + remove(scfilename); + remove(tmpfilename1); + remove(tmpfilename2); + } + } + printf("%d passes, %d fails\n", passes, fails); + return fails == 0 ? 0 : 1; +} diff --git a/clicons.c b/clicons.c new file mode 100644 index 0000000..2e88544 --- /dev/null +++ b/clicons.c @@ -0,0 +1,14 @@ +/* + * clicons.c: definitions limited to tools that link against both + * console.c and cmdline.c. + */ + +#include "putty.h" + +static const LogPolicyVtable console_cli_logpolicy_vt = { + .eventlog = console_eventlog, + .askappend = console_askappend, + .logging_error = console_logging_error, + .verbose = cmdline_lp_verbose, +}; +LogPolicy console_cli_logpolicy[1] = {{ &console_cli_logpolicy_vt }}; diff --git a/cmdgen.c b/cmdgen.c index eaaf2e4..ef9c2e1 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -2,8 +2,6 @@ * cmdgen.c - command-line form of PuTTYgen */ -#define PUTTY_DO_GLOBALS - #include #include #include @@ -15,90 +13,68 @@ #include "putty.h" #include "ssh.h" +#include "sshkeygen.h" +#include "mpint.h" -#ifdef TEST_CMDGEN -/* - * This section overrides some definitions below for test purposes. - * When compiled with -DTEST_CMDGEN (as cgtest.c will do): - * - * - Calls to get_random_data() are replaced with the diagnostic - * function below (I #define the name so that I can still link - * with the original set of modules without symbol clash), in - * order to avoid depleting the test system's /dev/random - * unnecessarily. - * - * - Calls to console_get_userpass_input() are replaced with the - * diagnostic function below, so that I can run tests in an - * automated manner and provide their interactive passphrase - * inputs. - * - * - main() is renamed to cmdgen_main(); at the bottom of the file - * I define another main() which calls the former repeatedly to - * run tests. - */ -bool cgtest_verbose = false; -#define get_random_data get_random_data_diagnostic -char *get_random_data(int len, const char *device) +static FILE *progress_fp = NULL; +static bool linear_progress_phase; +static unsigned last_progress_col; + +static ProgressPhase cmdgen_progress_add_linear( + ProgressReceiver *prog, double c) { - char *buf = snewn(len, char); - memset(buf, 'x', len); - return buf; + ProgressPhase ph = { .n = 0 }; + return ph; } -#define console_get_userpass_input console_get_userpass_input_diagnostic -int nprompts, promptsgot; -const char *prompts[3]; -int console_get_userpass_input(prompts_t *p) + +static ProgressPhase cmdgen_progress_add_probabilistic( + ProgressReceiver *prog, double c, double p) { - size_t i; - int ret = 1; - for (i = 0; i < p->n_prompts; i++) { - if (promptsgot < nprompts) { - p->prompts[i]->result = dupstr(prompts[promptsgot++]); - if (cgtest_verbose) - printf(" prompt \"%s\": response \"%s\"\n", - p->prompts[i]->prompt, p->prompts[i]->result); - } else { - promptsgot++; /* track number of requests anyway */ - ret = 0; - if (cgtest_verbose) - printf(" prompt \"%s\": no response preloaded\n", - p->prompts[i]->prompt); - } - } - return ret; + ProgressPhase ph = { .n = 1 }; + return ph; } -#define main cmdgen_main -#endif -struct progress { - int phase, current; -}; - -static void progress_update(void *param, int action, int phase, int iprogress) +static void cmdgen_progress_start_phase(ProgressReceiver *prog, + ProgressPhase p) { - struct progress *p = (struct progress *)param; - if (action != PROGFN_PROGRESS) - return; - if (phase > p->phase) { - if (p->phase >= 0) - fputc('\n', stderr); - p->phase = phase; - if (iprogress >= 0) - p->current = iprogress - 1; - else - p->current = iprogress; - } - while (p->current < iprogress) { - fputc('+', stdout); - p->current++; + linear_progress_phase = (p.n == 0); + last_progress_col = 0; +} +static void cmdgen_progress_report(ProgressReceiver *prog, double p) +{ + unsigned new_col = p * 64 + 0.5; + for (; last_progress_col < new_col; last_progress_col++) + fputc('+', progress_fp); +} +static void cmdgen_progress_report_attempt(ProgressReceiver *prog) +{ + if (progress_fp) { + fputc('+', progress_fp); + fflush(progress_fp); } - fflush(stdout); } - -static void no_progress(void *param, int action, int phase, int iprogress) +static void cmdgen_progress_report_phase_complete(ProgressReceiver *prog) { + if (linear_progress_phase) + cmdgen_progress_report(prog, 1.0); + if (progress_fp) { + fputc('\n', progress_fp); + fflush(progress_fp); + } } +static const ProgressReceiverVtable cmdgen_progress_vt = { + .add_linear = cmdgen_progress_add_linear, + .add_probabilistic = cmdgen_progress_add_probabilistic, + .ready = null_progress_ready, + .start_phase = cmdgen_progress_start_phase, + .report = cmdgen_progress_report, + .report_attempt = cmdgen_progress_report_attempt, + .report_phase_complete = cmdgen_progress_report_phase_complete, +}; + +static ProgressReceiver cmdgen_progress = { .vt = &cmdgen_progress_vt }; + /* * Stubs to let everything else link sensibly. */ @@ -138,8 +114,9 @@ void help(void) printf("PuTTYgen: key generator and converter for the PuTTY tools\n" "%s\n", ver); usage(false); - printf(" -t specify key type when generating (ed25519, ecdsa, rsa, " - "dsa, rsa1)\n" + printf(" -t specify key type when generating:\n" + " eddsa, ecdsa, rsa, dsa, rsa1 use with -b\n" + " ed25519, ed448 special cases of eddsa\n" " -b specify number of bits when generating key\n" " -C change or specify key comment\n" " -P change key passphrase\n" @@ -153,16 +130,43 @@ void help(void) " public RFC 4716 / ssh.com public key\n" " public-openssh OpenSSH public key\n" " fingerprint output the key fingerprint\n" + " text output the key components as " + "'name=0x####'\n" " -o specify output file\n" " -l equivalent to `-O fingerprint'\n" " -L equivalent to `-O public-openssh'\n" " -p equivalent to `-O public'\n" + " --dump equivalent to `-O text'\n" + " --reencrypt load a key and save it with fresh " + "encryption\n" " --old-passphrase file\n" " specify file containing old key passphrase\n" " --new-passphrase file\n" " specify file containing new key passphrase\n" " --random-device device\n" " specify device to read entropy from (e.g. /dev/urandom)\n" + " --primes select prime-generation method:\n" + " probable conventional probabilistic prime finding\n" + " proven numbers that have been proven to be prime\n" + " proven-even also try harder for an even distribution\n" + " --strong-rsa use \"strong\" primes as RSA key factors\n" + " --ppk-param =[,=,...]\n" + " specify parameters when writing PuTTY private key file " + "format:\n" + " version PPK format version (min 2, max 3, " + "default 3)\n" + " kdf key derivation function (argon2id, " + "argon2i, argon2d)\n" + " memory Kbyte of memory to use in passphrase " + "hash\n" + " (default 8192)\n" + " time approx milliseconds to hash for " + "(default 100)\n" + " passes number of hash passes to run " + "(alternative to 'time')\n" + " parallelism number of parallelisable threads in the " + "hash function\n" + " (default 1)\n" ); } @@ -217,14 +221,16 @@ int main(int argc, char **argv) { char *infile = NULL; Filename *infilename = NULL, *outfilename = NULL; - enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, ED25519 } keytype = NOKEYGEN; + LoadedFile *infile_lf = NULL; + BinarySource *infile_bs = NULL; + enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, EDDSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO, - OPENSSH_NEW, SSHCOM } outtype = PRIVATE; + OPENSSH_NEW, SSHCOM, TEXT } outtype = PRIVATE; int bits = -1; const char *comment = NULL; char *origcomment = NULL; - bool change_passphrase = false; + bool change_passphrase = false, reencrypt = false; bool errs = false, nogo = false; int intype = SSH_KEYTYPE_UNOPENABLE; int sshver = 0; @@ -234,8 +240,17 @@ int main(int argc, char **argv) char *ssh2alg = NULL; char *old_passphrase = NULL, *new_passphrase = NULL; bool load_encrypted; - progfn_t progressfn = is_interactive() ? progress_update : no_progress; const char *random_device = NULL; + int exit_status = 0; + const PrimeGenerationPolicy *primegen = &primegen_probabilistic; + bool strong_rsa = false; + ppk_save_parameters params = ppk_save_default_parameters; + FingerprintType fptype = SSH_FPTYPE_DEFAULT; + + if (is_interactive()) + progress_fp = stderr; + + #define RETURN(status) do { exit_status = (status); goto out; } while (0) /* ------------------------------------------------------------------ * Parse the command line to figure out what we've been asked to do. @@ -247,7 +262,7 @@ int main(int argc, char **argv) */ if (argc <= 1) { usage(true); - return 0; + RETURN(0); } /* @@ -255,98 +270,204 @@ int main(int argc, char **argv) */ while (--argc) { char *p = *++argv; - if (*p == '-') { + if (p[0] == '-' && p[1]) { /* * An option. */ while (p && *++p) { char c = *p; switch (c) { - case '-': + case '-': { /* * Long option. */ - { - char *opt, *val; - opt = p++; /* opt will have _one_ leading - */ - while (*p && *p != '=') - p++; /* find end of option */ - if (*p == '=') { - *p++ = '\0'; - val = p; - } else - val = NULL; - - if (!strcmp(opt, "-help")) { - if (val) { - errs = true; - fprintf(stderr, "puttygen: option `-%s'" - " expects no argument\n", opt); - } else { - help(); - nogo = true; - } - } else if (!strcmp(opt, "-version")) { - if (val) { - errs = true; - fprintf(stderr, "puttygen: option `-%s'" - " expects no argument\n", opt); - } else { - showversion(); - nogo = true; - } - } else if (!strcmp(opt, "-pgpfp")) { - if (val) { - errs = true; - fprintf(stderr, "puttygen: option `-%s'" - " expects no argument\n", opt); - } else { - /* support --pgpfp for consistency */ - pgp_fingerprints(); - nogo = true; - } - } else if (!strcmp(opt, "-old-passphrase")) { - if (!val && argc > 1) - --argc, val = *++argv; - if (!val) { - errs = true; - fprintf(stderr, "puttygen: option `-%s'" - " expects an argument\n", opt); - } else { - old_passphrase = readpassphrase(val); - if (!old_passphrase) + char *opt, *val; + opt = p++; /* opt will have _one_ leading - */ + while (*p && *p != '=') + p++; /* find end of option */ + if (*p == '=') { + *p++ = '\0'; + val = p; + } else + val = NULL; + + if (!strcmp(opt, "-help")) { + if (val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects no argument\n", opt); + } else { + help(); + nogo = true; + } + } else if (!strcmp(opt, "-version")) { + if (val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects no argument\n", opt); + } else { + showversion(); + nogo = true; + } + } else if (!strcmp(opt, "-pgpfp")) { + if (val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects no argument\n", opt); + } else { + /* support --pgpfp for consistency */ + pgp_fingerprints(); + nogo = true; + } + } else if (!strcmp(opt, "-old-passphrase")) { + if (!val && argc > 1) + --argc, val = *++argv; + if (!val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects an argument\n", opt); + } else { + old_passphrase = readpassphrase(val); + if (!old_passphrase) + errs = true; + } + } else if (!strcmp(opt, "-new-passphrase")) { + if (!val && argc > 1) + --argc, val = *++argv; + if (!val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects an argument\n", opt); + } else { + new_passphrase = readpassphrase(val); + if (!new_passphrase) + errs = true; + } + } else if (!strcmp(opt, "-random-device")) { + if (!val && argc > 1) + --argc, val = *++argv; + if (!val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects an argument\n", opt); + } else { + random_device = val; + } + } else if (!strcmp(opt, "-dump")) { + outtype = TEXT; + } else if (!strcmp(opt, "-primes")) { + if (!val && argc > 1) + --argc, val = *++argv; + if (!val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects an argument\n", opt); + } else if (!strcmp(val, "probable") || + !strcmp(val, "probabilistic")) { + primegen = &primegen_probabilistic; + } else if (!strcmp(val, "provable") || + !strcmp(val, "proven") || + !strcmp(val, "simple") || + !strcmp(val, "maurer-simple")) { + primegen = &primegen_provable_maurer_simple; + } else if (!strcmp(val, "provable-even") || + !strcmp(val, "proven-even") || + !strcmp(val, "even") || + !strcmp(val, "complex") || + !strcmp(val, "maurer-complex")) { + primegen = &primegen_provable_maurer_complex; + } else { + errs = true; + fprintf(stderr, "puttygen: unrecognised prime-" + "generation mode `%s'\n", val); + } + } else if (!strcmp(opt, "-strong-rsa")) { + strong_rsa = true; + } else if (!strcmp(opt, "-reencrypt")) { + reencrypt = true; + } else if (!strcmp(opt, "-ppk-param") || + !strcmp(opt, "-ppk-params")) { + if (!val && argc > 1) + --argc, val = *++argv; + if (!val) { + errs = true; + fprintf(stderr, "puttygen: option `-%s'" + " expects an argument\n", opt); + } else { + char *nextval; + for (; val; val = nextval) { + nextval = strchr(val, ','); + if (nextval) + *nextval++ = '\0'; + + char *optvalue = strchr(val, '='); + if (!optvalue) { errs = true; - } - } else if (!strcmp(opt, "-new-passphrase")) { - if (!val && argc > 1) - --argc, val = *++argv; - if (!val) { - errs = true; - fprintf(stderr, "puttygen: option `-%s'" - " expects an argument\n", opt); - } else { - new_passphrase = readpassphrase(val); - if (!new_passphrase) + fprintf(stderr, "puttygen: PPK parameter " + "'%s' expected a value\n", val); + continue; + } + *optvalue++ = '\0'; + + /* Non-numeric options */ + if (!strcmp(val, "kdf")) { + if (!strcmp(optvalue, "Argon2id") || + !strcmp(optvalue, "argon2id")) { + params.argon2_flavour = Argon2id; + } else if (!strcmp(optvalue, "Argon2i") || + !strcmp(optvalue, "argon2i")) { + params.argon2_flavour = Argon2i; + } else if (!strcmp(optvalue, "Argon2d") || + !strcmp(optvalue, "argon2d")) { + params.argon2_flavour = Argon2d; + } else { + errs = true; + fprintf(stderr, "puttygen: unrecognise" + "d kdf '%s'\n", optvalue); + } + continue; + } + + char *end; + unsigned long n = strtoul(optvalue, &end, 0); + if (!*optvalue || *end) { errs = true; + fprintf(stderr, "puttygen: value '%s' for " + "PPK parameter '%s': expected a " + "number\n", optvalue, val); + continue; + } + + if (!strcmp(val, "version")) { + params.fmt_version = n; + } else if (!strcmp(val, "memory") || + !strcmp(val, "mem")) { + params.argon2_mem = n; + } else if (!strcmp(val, "time")) { + params.argon2_passes_auto = true; + params.argon2_milliseconds = n; + } else if (!strcmp(val, "passes")) { + params.argon2_passes_auto = false; + params.argon2_passes = n; + } else if (!strcmp(val, "parallelism") || + !strcmp(val, "parallel")) { + params.argon2_parallelism = n; + } else { + errs = true; + fprintf(stderr, "puttygen: unrecognised " + "PPK parameter '%s'\n", val); + continue; + } } - } else if (!strcmp(opt, "-random-device")) { - if (!val && argc > 1) - --argc, val = *++argv; - if (!val) { - errs = true; - fprintf(stderr, "puttygen: option `-%s'" - " expects an argument\n", opt); - } else { - random_device = val; - } - } else { - errs = true; - fprintf(stderr, - "puttygen: no such option `-%s'\n", opt); } + } else { + errs = true; + fprintf(stderr, + "puttygen: no such option `-%s'\n", opt); } p = NULL; break; + } case 'h': case 'V': case 'P': @@ -379,7 +500,7 @@ int main(int argc, char **argv) outtype = PUBLIC; break; case 'q': - progressfn = no_progress; + progress_fp = NULL; break; } break; @@ -388,6 +509,7 @@ int main(int argc, char **argv) case 'C': case 'O': case 'o': + case 'E': /* * Option requiring parameter. */ @@ -412,8 +534,12 @@ int main(int argc, char **argv) keytype = DSA, sshver = 2; else if (!strcmp(p, "ecdsa")) keytype = ECDSA, sshver = 2; + else if (!strcmp(p, "eddsa")) + keytype = EDDSA, sshver = 2; else if (!strcmp(p, "ed25519")) - keytype = ED25519, sshver = 2; + keytype = EDDSA, bits = 255, sshver = 2; + else if (!strcmp(p, "ed448")) + keytype = EDDSA, bits = 448, sshver = 2; else { fprintf(stderr, "puttygen: unknown key type `%s'\n", p); @@ -441,6 +567,8 @@ int main(int argc, char **argv) outtype = OPENSSH_NEW, sshver = 2; else if (!strcmp(p, "private-sshcom")) outtype = SSHCOM, sshver = 2; + else if (!strcmp(p, "text")) + outtype = TEXT; else { fprintf(stderr, "puttygen: unknown output type `%s'\n", p); @@ -450,6 +578,17 @@ int main(int argc, char **argv) case 'o': outfile = p; break; + case 'E': + if (!strcmp(p, "md5")) + fptype = SSH_FPTYPE_MD5; + else if (!strcmp(p, "sha256")) + fptype = SSH_FPTYPE_SHA256; + else { + fprintf(stderr, "puttygen: unknown fingerprint " + "type `%s'\n", p); + errs = true; + } + break; } p = NULL; /* prevent continued processing */ break; @@ -485,8 +624,8 @@ int main(int argc, char **argv) case ECDSA: bits = 384; break; - case ED25519: - bits = 256; + case EDDSA: + bits = 255; break; default: bits = DEFAULT_RSADSA_BITS; @@ -494,14 +633,29 @@ int main(int argc, char **argv) } } - if (keytype == ECDSA && (bits != 256 && bits != 384 && bits != 521)) { - fprintf(stderr, "puttygen: invalid bits for ECDSA, choose 256, 384 or 521\n"); - errs = true; - } - - if (keytype == ED25519 && (bits != 256)) { - fprintf(stderr, "puttygen: invalid bits for ED25519, choose 256\n"); - errs = true; + if (keytype == ECDSA || keytype == EDDSA) { + const char *name = (keytype == ECDSA ? "ECDSA" : "EdDSA"); + const int *valid_lengths = (keytype == ECDSA ? ec_nist_curve_lengths : + ec_ed_curve_lengths); + size_t n_lengths = (keytype == ECDSA ? n_ec_nist_curve_lengths : + n_ec_ed_curve_lengths); + bool (*alg_and_curve_by_bits)(int, const struct ec_curve **, + const ssh_keyalg **) = + (keytype == ECDSA ? ec_nist_alg_and_curve_by_bits : + ec_ed_alg_and_curve_by_bits); + + const struct ec_curve *curve; + const ssh_keyalg *alg; + + if (!alg_and_curve_by_bits(bits, &curve, &alg)) { + fprintf(stderr, "puttygen: invalid bits for %s, choose", name); + for (size_t i = 0; i < n_lengths; i++) + fprintf(stderr, "%s%d", (i == 0 ? " " : + i == n_lengths-1 ? " or " : ", "), + valid_lengths[i]); + fputc('\n', stderr); + errs = true; + } } if (keytype == RSA2 || keytype == RSA1 || keytype == DSA) { @@ -518,10 +672,10 @@ int main(int argc, char **argv) } if (errs) - return 1; + RETURN(1); if (nogo) - return 0; + RETURN(0); /* * If run with at least one argument _but_ not the required @@ -529,7 +683,7 @@ int main(int argc, char **argv) */ if (!infile && keytype == NOKEYGEN) { usage(true); - return 1; + RETURN(1); } /* ------------------------------------------------------------------ @@ -542,7 +696,7 @@ int main(int argc, char **argv) */ if (keytype != NOKEYGEN && infile) { fprintf(stderr, "puttygen: cannot both load and generate a key\n"); - return 1; + RETURN(1); } /* @@ -550,10 +704,10 @@ int main(int argc, char **argv) */ if (keytype != NOKEYGEN && (outtype != PRIVATE && outtype != OPENSSH_AUTO && - outtype != OPENSSH_NEW && outtype != SSHCOM)) { + outtype != OPENSSH_NEW && outtype != SSHCOM && outtype != TEXT)) { fprintf(stderr, "puttygen: this would generate a new key but " "discard the private part\n"); - return 1; + RETURN(1); } /* @@ -561,23 +715,37 @@ int main(int argc, char **argv) * course of action. */ if (infile) { + const char *load_error; + infilename = filename_from_str(infile); + if (!strcmp(infile, "-")) + infile_lf = lf_load_keyfile_fp(stdin, &load_error); + else + infile_lf = lf_load_keyfile(infilename, &load_error); - intype = key_type(infilename); + if (!infile_lf) { + fprintf(stderr, "puttygen: unable to load file `%s': %s\n", + infile, load_error); + RETURN(1); + } + + infile_bs = BinarySource_UPCAST(infile_lf); + intype = key_type_s(infile_bs); + BinarySource_REWIND(infile_bs); switch (intype) { case SSH_KEYTYPE_UNOPENABLE: case SSH_KEYTYPE_UNKNOWN: fprintf(stderr, "puttygen: unable to load file `%s': %s\n", infile, key_type_to_str(intype)); - return 1; + RETURN(1); case SSH_KEYTYPE_SSH1: case SSH_KEYTYPE_SSH1_PUBLIC: if (sshver == 2) { fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys" " not supported\n"); - return 1; + RETURN(1); } sshver = 1; break; @@ -591,7 +759,7 @@ int main(int argc, char **argv) if (sshver == 1) { fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys" " not supported\n"); - return 1; + RETURN(1); } sshver = 2; break; @@ -619,13 +787,13 @@ int main(int argc, char **argv) (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) { if (!outfile) { outfile = infile; - outfiletmp = dupcat(outfile, ".tmp", NULL); + outfiletmp = dupcat(outfile, ".tmp"); } - if (!change_passphrase && !comment) { + if (!change_passphrase && !comment && !reencrypt) { fprintf(stderr, "puttygen: this command would perform no useful" " action\n"); - return 1; + RETURN(1); } } else { if (!outfile) { @@ -636,32 +804,40 @@ int main(int argc, char **argv) if (outtype == PRIVATE || outtype == OPENSSH_AUTO || outtype == OPENSSH_NEW || outtype == SSHCOM) { fprintf(stderr, "puttygen: need to specify an output file\n"); - return 1; + RETURN(1); } } } /* * Figure out whether we need to load the encrypted part of the - * key. This will be the case if either (a) we need to write - * out a private key format, or (b) the entire input key file - * is encrypted. + * key. This will be the case if (a) we need to write out + * a private key format, (b) the entire input key file is + * encrypted, or (c) we're outputting TEXT, in which case we + * want all of the input file including private material if it + * exists. */ - if (outtype == PRIVATE || outtype == OPENSSH_AUTO || - outtype == OPENSSH_NEW || outtype == SSHCOM || + bool intype_entirely_encrypted = intype == SSH_KEYTYPE_OPENSSH_PEM || intype == SSH_KEYTYPE_OPENSSH_NEW || - intype == SSH_KEYTYPE_SSHCOM) + intype == SSH_KEYTYPE_SSHCOM; + bool intype_has_private = + !(intype == SSH_KEYTYPE_SSH1_PUBLIC || + intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || + intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH); + bool outtype_has_private = + outtype == PRIVATE || outtype == OPENSSH_AUTO || + outtype == OPENSSH_NEW || outtype == SSHCOM; + if (outtype_has_private || intype_entirely_encrypted || + (outtype == TEXT && intype_has_private)) load_encrypted = true; else load_encrypted = false; - if (load_encrypted && (intype == SSH_KEYTYPE_SSH1_PUBLIC || - intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || - intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)) { + if (load_encrypted && !intype_has_private) { fprintf(stderr, "puttygen: cannot perform this action on a " "public-key-only input file\n"); - return 1; + RETURN(1); } /* ------------------------------------------------------------------ @@ -675,18 +851,16 @@ int main(int argc, char **argv) char *entropy; char default_comment[80]; struct tm tm; - struct progress prog; - - prog.phase = -1; - prog.current = -1; tm = ltime(); if (keytype == DSA) strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm); else if (keytype == ECDSA) strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm); - else if (keytype == ED25519) + else if (keytype == EDDSA && bits == 255) strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm); + else if (keytype == EDDSA) + strftime(default_comment, 30, "eddsa-key-%Y%m%d", &tm); else strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm); @@ -694,34 +868,36 @@ int main(int argc, char **argv) if (!entropy) { fprintf(stderr, "puttygen: failed to collect entropy, " "could not generate key\n"); - return 1; + RETURN(1); } random_setup_special(); random_reseed(make_ptrlen(entropy, bits / 8)); smemclr(entropy, bits/8); sfree(entropy); + PrimeGenerationContext *pgc = primegen_new_context(primegen); + if (keytype == DSA) { struct dss_key *dsskey = snew(struct dss_key); - dsa_generate(dsskey, bits, progressfn, &prog); + dsa_generate(dsskey, bits, pgc, &cmdgen_progress); ssh2key = snew(ssh2_userkey); ssh2key->key = &dsskey->sshk; ssh1key = NULL; } else if (keytype == ECDSA) { struct ecdsa_key *ek = snew(struct ecdsa_key); - ecdsa_generate(ek, bits, progressfn, &prog); + ecdsa_generate(ek, bits); ssh2key = snew(ssh2_userkey); ssh2key->key = &ek->sshk; ssh1key = NULL; - } else if (keytype == ED25519) { + } else if (keytype == EDDSA) { struct eddsa_key *ek = snew(struct eddsa_key); - eddsa_generate(ek, bits, progressfn, &prog); + eddsa_generate(ek, bits); ssh2key = snew(ssh2_userkey); ssh2key->key = &ek->sshk; ssh1key = NULL; } else { RSAKey *rsakey = snew(RSAKey); - rsa_generate(rsakey, bits, progressfn, &prog); + rsa_generate(rsakey, bits, strong_rsa, pgc, &cmdgen_progress); rsakey->comment = NULL; if (keytype == RSA1) { ssh1key = rsakey; @@ -730,7 +906,8 @@ int main(int argc, char **argv) ssh2key->key = &rsakey->sshk; } } - progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1); + + primegen_free_context(pgc); if (ssh2key) ssh2key->comment = dupstr(default_comment); @@ -750,11 +927,13 @@ int main(int argc, char **argv) * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) - encrypted = rsa_ssh1_encrypted(infilename, &origcomment); + encrypted = rsa1_encrypted_s(infile_bs, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) - encrypted = ssh2_userkey_encrypted(infilename, &origcomment); + encrypted = ppk_encrypted_s(infile_bs, &origcomment); else - encrypted = import_encrypted(infilename, intype, &origcomment); + encrypted = import_encrypted_s(infilename, infile_bs, + intype, &origcomment); + BinarySource_REWIND(infile_bs); /* * If so, ask for a passphrase. @@ -772,9 +951,9 @@ int main(int argc, char **argv) if (!ret) { free_prompts(p); perror("puttygen: unable to read passphrase"); - return 1; + RETURN(1); } else { - old_passphrase = dupstr(p->prompts[0]->result); + old_passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); } } @@ -788,6 +967,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_SSH1: case SSH_KEYTYPE_SSH1_PUBLIC: ssh1key = snew(RSAKey); + memset(ssh1key, 0, sizeof(RSAKey)); if (!load_encrypted) { strbuf *blob; BinarySource src[1]; @@ -797,8 +977,8 @@ int main(int argc, char **argv) blob = strbuf_new(); - ret = rsa_ssh1_loadpub(infilename, BinarySink_UPCAST(blob), - &origcomment, &error); + ret = rsa1_loadpub_s(infile_bs, BinarySink_UPCAST(blob), + &origcomment, &error); BinarySource_BARE_INIT(src, blob->u, blob->len); get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST); strbuf_free(blob); @@ -809,9 +989,9 @@ int main(int argc, char **argv) ssh1key->q = NULL; ssh1key->iqmp = NULL; } else { - ret = rsa_ssh1_loadkey( - infilename, ssh1key, old_passphrase, &error); + ret = rsa1_load_s(infile_bs, ssh1key, old_passphrase, &error); } + BinarySource_REWIND(infile_bs); if (ret > 0) error = NULL; else if (!error) @@ -825,8 +1005,9 @@ int main(int argc, char **argv) sfree(origcomment); origcomment = NULL; ssh2blob = strbuf_new(); - if (ssh2_userkey_loadpub(infilename, &ssh2alg, BinarySink_UPCAST(ssh2blob), - &origcomment, &error)) { + if (ppk_loadpub_s(infile_bs, &ssh2alg, + BinarySink_UPCAST(ssh2blob), + &origcomment, &error)) { const ssh_keyalg *alg = find_pubkey_alg(ssh2alg); if (alg) bits = ssh_key_public_bits( @@ -839,9 +1020,9 @@ int main(int argc, char **argv) } sfree(ssh2alg); } else { - ssh2key = ssh2_load_userkey(infilename, old_passphrase, - &error); + ssh2key = ppk_load_s(infile_bs, old_passphrase, &error); } + BinarySource_REWIND(infile_bs); if ((ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) || ssh2blob) error = NULL; else if (!error) { @@ -855,7 +1036,7 @@ int main(int argc, char **argv) case SSH_KEYTYPE_OPENSSH_PEM: case SSH_KEYTYPE_OPENSSH_NEW: case SSH_KEYTYPE_SSHCOM: - ssh2key = import_ssh2(infilename, intype, old_passphrase, &error); + ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; @@ -872,7 +1053,7 @@ int main(int argc, char **argv) if (error) { fprintf(stderr, "puttygen: error loading `%s': %s\n", infile, error); - return 1; + RETURN(1); } } @@ -901,9 +1082,14 @@ int main(int argc, char **argv) /* * Prompt for a new passphrase if we have been asked to, or if * we have just generated a key. + * + * In the latter case, an exception is if we're producing text + * output, because that output format doesn't support encryption + * in any case. */ - if (!new_passphrase && (change_passphrase || keytype != NOKEYGEN)) { - prompts_t *p = new_prompts(NULL); + if (!new_passphrase && (change_passphrase || + (keytype != NOKEYGEN && outtype != TEXT))) { + prompts_t *p = new_prompts(); int ret; p->to_server = false; @@ -916,14 +1102,15 @@ int main(int argc, char **argv) if (!ret) { free_prompts(p); perror("puttygen: unable to read new passphrase"); - return 1; + RETURN(1); } else { - if (strcmp(p->prompts[0]->result, p->prompts[1]->result)) { + if (strcmp(prompt_get_result_ref(p->prompts[0]), + prompt_get_result_ref(p->prompts[1]))) { free_prompts(p); fprintf(stderr, "puttygen: passphrases do not match\n"); - return 1; + RETURN(1); } - new_passphrase = dupstr(p->prompts[0]->result); + new_passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); } } @@ -953,95 +1140,94 @@ int main(int argc, char **argv) random_ref(); /* we'll need a few random bytes in the save file */ if (sshver == 1) { assert(ssh1key); - ret = rsa_ssh1_savekey(outfilename, ssh1key, new_passphrase); + ret = rsa1_save_f(outfilename, ssh1key, new_passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-1 private key\n"); - return 1; + RETURN(1); } } else { assert(ssh2key); - ret = ssh2_save_userkey(outfilename, ssh2key, new_passphrase); + ret = ppk_save_f(outfilename, ssh2key, new_passphrase, ¶ms); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-2 private key\n"); - return 1; + RETURN(1); } } if (outfiletmp) { if (!move(outfiletmp, outfile)) - return 1; /* rename failed */ + RETURN(1); /* rename failed */ } break; case PUBLIC: - case PUBLICO: - { - FILE *fp; - - if (outfile) { - fp = f_open(outfilename, "w", false); - if (!fp) { - fprintf(stderr, "unable to open output file\n"); - exit(1); - } - } else { - fp = stdout; - } + case PUBLICO: { + FILE *fp; + + if (outfile) { + fp = f_open(outfilename, "w", false); + if (!fp) { + fprintf(stderr, "unable to open output file\n"); + exit(1); + } + } else { + fp = stdout; + } - if (sshver == 1) { - ssh1_write_pubkey(fp, ssh1key); - } else { - if (!ssh2blob) { - assert(ssh2key); - ssh2blob = strbuf_new(); - ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob)); - } + if (sshver == 1) { + ssh1_write_pubkey(fp, ssh1key); + } else { + if (!ssh2blob) { + assert(ssh2key); + ssh2blob = strbuf_new(); + ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob)); + } + + ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment, + ssh2blob->s, ssh2blob->len, + (outtype == PUBLIC ? + SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 : + SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)); + } - ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment, - ssh2blob->s, ssh2blob->len, - (outtype == PUBLIC ? - SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 : - SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)); - } + if (outfile) + fclose(fp); - if (outfile) - fclose(fp); - } break; + } - case FP: - { - FILE *fp; - char *fingerprint; + case FP: { + FILE *fp; + char *fingerprint; - if (sshver == 1) { - assert(ssh1key); - fingerprint = rsa_ssh1_fingerprint(ssh1key); - } else { - if (ssh2key) { - fingerprint = ssh2_fingerprint(ssh2key->key); - } else { - assert(ssh2blob); - fingerprint = ssh2_fingerprint_blob( - ptrlen_from_strbuf(ssh2blob)); - } - } - - if (outfile) { - fp = f_open(outfilename, "w", false); - if (!fp) { - fprintf(stderr, "unable to open output file\n"); - exit(1); - } - } else { - fp = stdout; - } - fprintf(fp, "%s\n", fingerprint); - if (outfile) - fclose(fp); + if (sshver == 1) { + assert(ssh1key); + fingerprint = rsa_ssh1_fingerprint(ssh1key); + } else { + if (ssh2key) { + fingerprint = ssh2_fingerprint(ssh2key->key, fptype); + } else { + assert(ssh2blob); + fingerprint = ssh2_fingerprint_blob( + ptrlen_from_strbuf(ssh2blob), fptype); + } + } - sfree(fingerprint); + if (outfile) { + fp = f_open(outfilename, "w", false); + if (!fp) { + fprintf(stderr, "unable to open output file\n"); + exit(1); + } + } else { + fp = stdout; } + fprintf(fp, "%s\n", fingerprint); + if (outfile) + fclose(fp); + + sfree(fingerprint); break; + } case OPENSSH_AUTO: case OPENSSH_NEW: @@ -1066,15 +1252,82 @@ int main(int argc, char **argv) ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to export key\n"); - return 1; + RETURN(1); } if (outfiletmp) { if (!move(outfiletmp, outfile)) - return 1; /* rename failed */ + RETURN(1); /* rename failed */ + } + break; + + case TEXT: { + key_components *kc; + if (sshver == 1) { + assert(ssh1key); + kc = rsa_components(ssh1key); + } else { + if (ssh2key) { + kc = ssh_key_components(ssh2key->key); + } else { + assert(ssh2blob); + + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(ssh2blob)); + ptrlen algname = get_string(src); + const ssh_keyalg *alg = find_pubkey_alg_len(algname); + if (!alg) { + fprintf(stderr, "puttygen: cannot extract key components " + "from public key of unknown type '%.*s'\n", + PTRLEN_PRINTF(algname)); + RETURN(1); + } + ssh_key *sk = ssh_key_new_pub( + alg, ptrlen_from_strbuf(ssh2blob)); + if (!sk) { + fprintf(stderr, "puttygen: unable to decode public key\n"); + RETURN(1); + } + kc = ssh_key_components(sk); + ssh_key_free(sk); + } } + + FILE *fp; + if (outfile) { + fp = f_open(outfilename, "w", false); + if (!fp) { + fprintf(stderr, "unable to open output file\n"); + exit(1); + } + } else { + fp = stdout; + } + + for (size_t i = 0; i < kc->ncomponents; i++) { + if (kc->components[i].is_mp_int) { + char *hex = mp_get_hex(kc->components[i].mp); + fprintf(fp, "%s=0x%s\n", kc->components[i].name, hex); + smemclr(hex, strlen(hex)); + sfree(hex); + } else { + fprintf(fp, "%s=\"", kc->components[i].name); + write_c_string_literal(fp, ptrlen_from_asciz( + kc->components[i].text)); + fputs("\"\n", fp); + } + } + + if (outfile) + fclose(fp); + key_components_free(kc); break; + } } + out: + + #undef RETURN + if (old_passphrase) { smemclr(old_passphrase, strlen(old_passphrase)); sfree(old_passphrase); @@ -1088,9 +1341,10 @@ int main(int argc, char **argv) freersakey(ssh1key); sfree(ssh1key); } - if (ssh2key) { + if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) { sfree(ssh2key->comment); - ssh_key_free(ssh2key->key); + if (ssh2key->key) + ssh_key_free(ssh2key->key); sfree(ssh2key); } if (ssh2blob) @@ -1098,588 +1352,11 @@ int main(int argc, char **argv) sfree(origcomment); if (infilename) filename_free(infilename); - filename_free(outfilename); - - return 0; -} - -#ifdef TEST_CMDGEN - -#undef main + if (infile_lf) + lf_free(infile_lf); + if (outfilename) + filename_free(outfilename); + sfree(outfiletmp); -#include - -int passes, fails; - -void setup_passphrases(char *first, ...) -{ - va_list ap; - char *next; - - nprompts = 0; - if (first) { - prompts[nprompts++] = first; - va_start(ap, first); - while ((next = va_arg(ap, char *)) != NULL) { - assert(nprompts < lenof(prompts)); - prompts[nprompts++] = next; - } - va_end(ap); - } -} - -void test(int retval, ...) -{ - va_list ap; - int i, argc, ret; - char **argv; - - argc = 0; - va_start(ap, retval); - while (va_arg(ap, char *) != NULL) - argc++; - va_end(ap); - - argv = snewn(argc+1, char *); - va_start(ap, retval); - for (i = 0; i <= argc; i++) - argv[i] = va_arg(ap, char *); - va_end(ap); - - promptsgot = 0; - if (cgtest_verbose) { - printf("run:"); - for (int i = 0; i < argc; i++) { - static const char okchars[] = - "0123456789abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ%+,-./:=[]^_"; - const char *arg = argv[i]; - - printf(" "); - if (arg[strspn(arg, okchars)]) { - printf("'"); - for (const char *c = argv[i]; *c; c++) { - if (*c == '\'') { - printf("'\\''"); - } else { - putchar(*c); - } - } - printf("'"); - } else { - fputs(arg, stdout); - } - } - printf("\n"); - } - ret = cmdgen_main(argc, argv); - random_clear(); - - if (ret != retval) { - printf("FAILED retval (exp %d got %d):", retval, ret); - for (i = 0; i < argc; i++) - printf(" %s", argv[i]); - printf("\n"); - fails++; - } else if (promptsgot != nprompts) { - printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot); - for (i = 0; i < argc; i++) - printf(" %s", argv[i]); - printf("\n"); - fails++; - } else { - passes++; - } - - sfree(argv); -} - -void filecmp(char *file1, char *file2, char *fmt, ...) -{ - /* - * Ideally I should do file comparison myself, to maximise the - * portability of this test suite once this application begins - * running on non-Unix platforms. For the moment, though, - * calling Unix diff is perfectly adequate. - */ - char *buf; - int ret; - - buf = dupprintf("diff -q '%s' '%s'", file1, file2); - ret = system(buf); - sfree(buf); - - if (ret) { - va_list ap; - - printf("FAILED diff (ret=%d): ", ret); - - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - - printf("\n"); - - fails++; - } else - passes++; + return exit_status; } - -char *cleanup_fp(char *s) -{ - ptrlen pl = ptrlen_from_asciz(s); - static const char separators[] = " \n\t"; - - /* Skip initial key type word if we find one */ - if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL)) - ptrlen_get_word(&pl, separators); - - /* Expect two words giving the key length and the hash */ - ptrlen bits = ptrlen_get_word(&pl, separators); - ptrlen hash = ptrlen_get_word(&pl, separators); - - /* Strip "MD5:" prefix if it's present, and do nothing if it isn't */ - ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash); - - return dupprintf("%.*s %.*s", PTRLEN_PRINTF(bits), PTRLEN_PRINTF(hash)); -} - -char *get_fp(char *filename) -{ - FILE *fp; - char buf[256], *ret; - - fp = fopen(filename, "r"); - if (!fp) - return NULL; - ret = fgets(buf, sizeof(buf), fp); - fclose(fp); - if (!ret) - return NULL; - return cleanup_fp(buf); -} - -void check_fp(char *filename, char *fp, char *fmt, ...) -{ - char *newfp; - - if (!fp) - return; - - newfp = get_fp(filename); - - if (!strcmp(fp, newfp)) { - passes++; - } else { - va_list ap; - - printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp); - - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); - - printf("\n"); - - fails++; - } - - sfree(newfp); -} - -int main(int argc, char **argv) -{ - int i; - static char *const keytypes[] = { "rsa1", "dsa", "rsa" }; - - if (getenv("CGTEST_VERBOSE")) - cgtest_verbose = true; - - /* - * Even when this thing is compiled for automatic test mode, - * it's helpful to be able to invoke it with command-line - * options for _manual_ tests. - */ - if (argc > 1) - return cmdgen_main(argc, argv); - - passes = fails = 0; - - for (i = 0; i < lenof(keytypes); i++) { - char filename[128], osfilename[128], scfilename[128]; - char pubfilename[128], tmpfilename1[128], tmpfilename2[128]; - char *fp; - - sprintf(filename, "test-%s.ppk", keytypes[i]); - sprintf(pubfilename, "test-%s.pub", keytypes[i]); - sprintf(osfilename, "test-%s.os", keytypes[i]); - sprintf(scfilename, "test-%s.sc", keytypes[i]); - sprintf(tmpfilename1, "test-%s.tmp1", keytypes[i]); - sprintf(tmpfilename2, "test-%s.tmp2", keytypes[i]); - - /* - * Create an encrypted key. - */ - setup_passphrases("sponge", "sponge", NULL); - test(0, "puttygen", "-t", keytypes[i], "-o", filename, NULL); - - /* - * List the public key in OpenSSH format. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL); - { - char *cmdbuf; - fp = NULL; - cmdbuf = dupprintf("ssh-keygen -E md5 -l -f '%s' > '%s'", - pubfilename, tmpfilename1); - if (system(cmdbuf) || - (fp = get_fp(tmpfilename1)) == NULL) { - printf("UNABLE to test fingerprint matching against OpenSSH"); - } - sfree(cmdbuf); - } - - /* - * List the public key in IETF/ssh.com format. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-p", filename, NULL); - - /* - * List the fingerprint of the key. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-l", filename, "-o", tmpfilename1, NULL); - if (!fp) { - /* - * If we can't test fingerprints against OpenSSH, we - * can at the very least test equality of all the - * fingerprints we generate of this key throughout - * testing. - */ - fp = get_fp(tmpfilename1); - } else { - check_fp(tmpfilename1, fp, "%s initial fp", keytypes[i]); - } - - /* - * Change the comment of the key; this _does_ require a - * passphrase owing to the tamperproofing. - * - * NOTE: In SSH-1, this only requires a passphrase because - * of inadequacies of the loading and saving mechanisms. In - * _principle_, it should be perfectly possible to modify - * the comment on an SSH-1 key without requiring a - * passphrase; the only reason I can't do it is because my - * loading and saving mechanisms don't include a method of - * loading all the key data without also trying to decrypt - * the private section. - * - * I don't consider this to be a problem worth solving, - * because (a) to fix it would probably end up bloating - * PuTTY proper, and (b) SSH-1 is on the way out anyway so - * it shouldn't be highly significant. If it seriously - * bothers anyone then perhaps I _might_ be persuadable. - */ - setup_passphrases("sponge", NULL); - test(0, "puttygen", "-C", "new-comment", filename, NULL); - - /* - * Change the passphrase to nothing. - */ - setup_passphrases("sponge", "", "", NULL); - test(0, "puttygen", "-P", filename, NULL); - - /* - * Change the comment of the key again; this time we expect no - * passphrase to be required. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-C", "new-comment-2", filename, NULL); - - /* - * Export the private key into OpenSSH format; no passphrase - * should be required since the key is currently unencrypted. - * For RSA1 keys, this should give an error. - */ - setup_passphrases(NULL); - test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename, - filename, NULL); - - if (i) { - /* - * List the fingerprint of the OpenSSH-formatted key. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL); - check_fp(tmpfilename1, fp, "%s openssh clear fp", keytypes[i]); - - /* - * List the public half of the OpenSSH-formatted key in - * OpenSSH format. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-L", osfilename, NULL); - - /* - * List the public half of the OpenSSH-formatted key in - * IETF/ssh.com format. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-p", osfilename, NULL); - } - - /* - * Export the private key into ssh.com format; no passphrase - * should be required since the key is currently unencrypted. - * For RSA1 keys, this should give an error. - */ - setup_passphrases(NULL); - test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename, - filename, NULL); - - if (i) { - /* - * List the fingerprint of the ssh.com-formatted key. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL); - check_fp(tmpfilename1, fp, "%s ssh.com clear fp", keytypes[i]); - - /* - * List the public half of the ssh.com-formatted key in - * OpenSSH format. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-L", scfilename, NULL); - - /* - * List the public half of the ssh.com-formatted key in - * IETF/ssh.com format. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-p", scfilename, NULL); - } - - if (i) { - /* - * Convert from OpenSSH into ssh.com. - */ - setup_passphrases(NULL); - test(0, "puttygen", osfilename, "-o", tmpfilename1, - "-O", "private-sshcom", NULL); - - /* - * Convert from ssh.com back into a PuTTY key, - * supplying the same comment as we had before we - * started to ensure the comparison works. - */ - setup_passphrases(NULL); - test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", - "-o", tmpfilename2, NULL); - - /* - * See if the PuTTY key thus generated is the same as - * the original. - */ - filecmp(filename, tmpfilename2, - "p->o->s->p clear %s", keytypes[i]); - - /* - * Convert from ssh.com to OpenSSH. - */ - setup_passphrases(NULL); - test(0, "puttygen", scfilename, "-o", tmpfilename1, - "-O", "private-openssh", NULL); - - /* - * Convert from OpenSSH back into a PuTTY key, - * supplying the same comment as we had before we - * started to ensure the comparison works. - */ - setup_passphrases(NULL); - test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", - "-o", tmpfilename2, NULL); - - /* - * See if the PuTTY key thus generated is the same as - * the original. - */ - filecmp(filename, tmpfilename2, - "p->s->o->p clear %s", keytypes[i]); - - /* - * Finally, do a round-trip conversion between PuTTY - * and ssh.com without involving OpenSSH, to test that - * the key comment is preserved in that case. - */ - setup_passphrases(NULL); - test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1, - filename, NULL); - setup_passphrases(NULL); - test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL); - filecmp(filename, tmpfilename2, - "p->s->p clear %s", keytypes[i]); - } - - /* - * Check that mismatched passphrases cause an error. - */ - setup_passphrases("sponge2", "sponge3", NULL); - test(1, "puttygen", "-P", filename, NULL); - - /* - * Put a passphrase back on. - */ - setup_passphrases("sponge2", "sponge2", NULL); - test(0, "puttygen", "-P", filename, NULL); - - /* - * Export the private key into OpenSSH format, this time - * while encrypted. For RSA1 keys, this should give an - * error. - */ - if (i == 0) - setup_passphrases(NULL); /* error, hence no passphrase read */ - else - setup_passphrases("sponge2", NULL); - test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename, - filename, NULL); - - if (i) { - /* - * List the fingerprint of the OpenSSH-formatted key. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL); - check_fp(tmpfilename1, fp, "%s openssh encrypted fp", keytypes[i]); - - /* - * List the public half of the OpenSSH-formatted key in - * OpenSSH format. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-L", osfilename, NULL); - - /* - * List the public half of the OpenSSH-formatted key in - * IETF/ssh.com format. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-p", osfilename, NULL); - } - - /* - * Export the private key into ssh.com format, this time - * while encrypted. For RSA1 keys, this should give an - * error. - */ - if (i == 0) - setup_passphrases(NULL); /* error, hence no passphrase read */ - else - setup_passphrases("sponge2", NULL); - test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename, - filename, NULL); - - if (i) { - /* - * List the fingerprint of the ssh.com-formatted key. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL); - check_fp(tmpfilename1, fp, "%s ssh.com encrypted fp", keytypes[i]); - - /* - * List the public half of the ssh.com-formatted key in - * OpenSSH format. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-L", scfilename, NULL); - - /* - * List the public half of the ssh.com-formatted key in - * IETF/ssh.com format. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-p", scfilename, NULL); - } - - if (i) { - /* - * Convert from OpenSSH into ssh.com. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", osfilename, "-o", tmpfilename1, - "-O", "private-sshcom", NULL); - - /* - * Convert from ssh.com back into a PuTTY key, - * supplying the same comment as we had before we - * started to ensure the comparison works. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", - "-o", tmpfilename2, NULL); - - /* - * See if the PuTTY key thus generated is the same as - * the original. - */ - filecmp(filename, tmpfilename2, - "p->o->s->p encrypted %s", keytypes[i]); - - /* - * Convert from ssh.com to OpenSSH. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", scfilename, "-o", tmpfilename1, - "-O", "private-openssh", NULL); - - /* - * Convert from OpenSSH back into a PuTTY key, - * supplying the same comment as we had before we - * started to ensure the comparison works. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", - "-o", tmpfilename2, NULL); - - /* - * See if the PuTTY key thus generated is the same as - * the original. - */ - filecmp(filename, tmpfilename2, - "p->s->o->p encrypted %s", keytypes[i]); - - /* - * Finally, do a round-trip conversion between PuTTY - * and ssh.com without involving OpenSSH, to test that - * the key comment is preserved in that case. - */ - setup_passphrases("sponge2", NULL); - test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1, - filename, NULL); - setup_passphrases("sponge2", NULL); - test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL); - filecmp(filename, tmpfilename2, - "p->s->p encrypted %s", keytypes[i]); - } - - /* - * Load with the wrong passphrase. - */ - setup_passphrases("sponge8", NULL); - test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL); - - /* - * Load a totally bogus file. - */ - setup_passphrases(NULL); - test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL); - } - printf("%d passes, %d fails\n", passes, fails); - return 0; -} - -#endif diff --git a/cmdline.c b/cmdline.c index b9d068f..62d65e1 100644 --- a/cmdline.c +++ b/cmdline.c @@ -109,18 +109,6 @@ int cmdline_get_passwd_input(prompts_t *p) return 1; } -/* - * Here we have a flags word which describes the capabilities of - * the particular tool on whose behalf we're running. We will - * refuse certain command-line options if a particular tool - * inherently can't do anything sensible. For example, the file - * transfer tools (psftp, pscp) can't do a great deal with protocol - * selections (ever tried running scp over telnet?) or with port - * forwarding (even if it wasn't a hideously bad idea, they don't - * have the select/poll infrastructure to make them work). - */ -int cmdline_tooltype = 0; - static bool cmdline_check_unavailable(int flag, const char *p) { if (cmdline_tooltype & flag) { @@ -157,6 +145,24 @@ static bool cmdline_check_unavailable(int flag, const char *p) static bool seen_hostname_argument = false; static bool seen_port_argument = false; +static bool seen_verbose_option = false; +static bool loaded_session = false; +bool cmdline_verbose(void) { return seen_verbose_option; } +bool cmdline_seat_verbose(Seat *seat) { return cmdline_verbose(); } +bool cmdline_lp_verbose(LogPolicy *lp) { return cmdline_verbose(); } +bool cmdline_loaded_session(void) { return loaded_session; } + +static void set_protocol(Conf *conf, int protocol) +{ + settings_set_default_protocol(protocol); + conf_set_int(conf, CONF_protocol, protocol); +} + +static void set_port(Conf *conf, int port) +{ + settings_set_default_port(port); + conf_set_int(conf, CONF_port, port); +} int cmdline_process_param(const char *p, char *value, int need_save, Conf *conf) @@ -275,9 +281,7 @@ int cmdline_process_param(const char *p, char *value, backend_vt_from_name(prefix); if (vt) { - default_protocol = vt->protocol; - conf_set_int(conf, CONF_protocol, - default_protocol); + set_protocol(conf, vt->protocol); port_override = vt->default_port; } else { cmdline_error("unrecognised protocol prefix '%s'", @@ -399,60 +403,30 @@ int cmdline_process_param(const char *p, char *value, * saved. */ do_defaults(value, conf); loaded_session = true; - cmdline_session_name = dupstr(value); return 2; } - if (!strcmp(p, "-ssh")) { - RETURN(1); - UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); - default_protocol = PROT_SSH; - default_port = 22; - conf_set_int(conf, CONF_protocol, default_protocol); - conf_set_int(conf, CONF_port, default_port); - return 1; - } - if (!strcmp(p, "-telnet")) { - RETURN(1); - UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); - default_protocol = PROT_TELNET; - default_port = 23; - conf_set_int(conf, CONF_protocol, default_protocol); - conf_set_int(conf, CONF_port, default_port); - return 1; - } - if (!strcmp(p, "-rlogin")) { - RETURN(1); - UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); - default_protocol = PROT_RLOGIN; - default_port = 513; - conf_set_int(conf, CONF_protocol, default_protocol); - conf_set_int(conf, CONF_port, default_port); - return 1; - } - if (!strcmp(p, "-raw")) { - RETURN(1); - UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); - default_protocol = PROT_RAW; - conf_set_int(conf, CONF_protocol, default_protocol); - } - if (!strcmp(p, "-serial")) { - RETURN(1); - /* Serial is not NONNETWORK in an odd sense of the word */ - UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); - SAVEABLE(0); - default_protocol = PROT_SERIAL; - conf_set_int(conf, CONF_protocol, default_protocol); - /* The host parameter will already be loaded into CONF_host, - * so copy it across */ - conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host)); + for (size_t i = 0; backends[i]; i++) { + if (p[0] == '-' && !strcmp(p+1, backends[i]->id)) { + RETURN(1); + UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); + set_protocol(conf, backends[i]->protocol); + if (backends[i]->default_port) + set_port(conf, backends[i]->default_port); + if (backends[i]->protocol == PROT_SERIAL) { + /* Special handling: the 'where to connect to' argument will + * have been placed into CONF_host, but for this protocol, it + * needs to be in CONF_serline */ + conf_set_str(conf, CONF_serline, + conf_get_str(conf, CONF_host)); + } + return 1; + } } if (!strcmp(p, "-v")) { RETURN(1); - flags |= FLAG_VERBOSE; + UNAVAILABLE_IN(TOOLTYPE_NO_VERBOSE_OPTION); + seen_verbose_option = true; } if (!strcmp(p, "-l")) { RETURN(2); @@ -589,7 +563,7 @@ int cmdline_process_param(const char *p, char *value, if (!strcmp(p, "-P")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); - SAVEABLE(1); /* lower priority than -ssh,-telnet */ + SAVEABLE(1); /* lower priority than -ssh, -telnet, etc */ conf_set_int(conf, CONF_port, atoi(value)); } if (!strcmp(p, "-pw")) { @@ -624,6 +598,14 @@ int cmdline_process_param(const char *p, char *value, SAVEABLE(0); conf_set_bool(conf, CONF_tryagent, false); } + + if (!strcmp(p, "-no-trivial-auth")) { + RETURN(1); + UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); + conf_set_bool(conf, CONF_ssh_no_trivial_userauth, true); + } + if (!strcmp(p, "-share")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); @@ -833,6 +815,20 @@ int cmdline_process_param(const char *p, char *value, filename_free(fn); } + if (!strcmp(p, "-logoverwrite")) { + RETURN(1); + UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); + conf_set_int(conf, CONF_logxfovr, LGXF_OVR); + } + + if (!strcmp(p, "-logappend")) { + RETURN(1); + UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); + SAVEABLE(0); + conf_set_int(conf, CONF_logxfovr, LGXF_APN); + } + if (!strcmp(p, "-proxycmd")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); @@ -849,7 +845,6 @@ int cmdline_process_param(const char *p, char *value, !strcmp(p, "-restrictacl")) { RETURN(1); restrict_process_acl(); - restricted_acl = true; } #endif @@ -858,9 +853,8 @@ int cmdline_process_param(const char *p, char *value, void cmdline_run_saved(Conf *conf) { - int pri, i; - for (pri = 0; pri < NPRIORITIES; pri++) { - for (i = 0; i < saves[pri].nsaved; i++) { + for (size_t pri = 0; pri < NPRIORITIES; pri++) { + for (size_t i = 0; i < saves[pri].nsaved; i++) { cmdline_process_param(saves[pri].params[i].p, saves[pri].params[i].value, 0, conf); sfree(saves[pri].params[i].p); diff --git a/compile b/compile index a85b723..99e5052 100755 --- a/compile +++ b/compile @@ -1,9 +1,9 @@ #! /bin/sh # Wrapper for compilers which do not understand '-c -o'. -scriptversion=2012-10-14.11; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2014 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ scriptversion=2012-10-14.11; # UTC # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -255,7 +255,8 @@ EOF echo "compile $scriptversion" exit $? ;; - cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ + icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac @@ -339,9 +340,9 @@ exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/config.c b/config.c index a265dda..b834853 100644 --- a/config.c +++ b/config.c @@ -257,65 +257,167 @@ static void config_port_handler(union control *ctrl, dlgparam *dlg, } struct hostport { - union control *host, *port; + union control *host, *port, *protradio, *protlist; + bool mid_refresh; }; /* - * We export this function so that platform-specific config - * routines can use it to conveniently identify the protocol radio - * buttons in order to add to them. + * Shared handler for protocol radio-button and drop-list controls. + * Handles the interaction of those two controls, and also changes + * the setting of the port box to match the protocol if necessary, + * and refreshes both host and port boxes when switching to/from the + * serial backend. */ -void config_protocolbuttons_handler(union control *ctrl, dlgparam *dlg, - void *data, int event) +static void config_protocols_handler(union control *ctrl, dlgparam *dlg, + void *data, int event) { - int button; Conf *conf = (Conf *)data; - struct hostport *hp = (struct hostport *)ctrl->radio.context.p; + int curproto = conf_get_int(conf, CONF_protocol); + struct hostport *hp = (struct hostport *)ctrl->generic.context.p; - /* - * This function works just like the standard radio-button - * handler, except that it also has to change the setting of - * the port box, and refresh both host and port boxes when. We - * expect the context parameter to point at a hostport - * structure giving the `union control's for both. - */ if (event == EVENT_REFRESH) { - int protocol = conf_get_int(conf, CONF_protocol); - for (button = 0; button < ctrl->radio.nbuttons; button++) - if (protocol == ctrl->radio.buttondata[button].i) - break; - /* We expected that `break' to happen, in all circumstances. */ - assert(button < ctrl->radio.nbuttons); - dlg_radiobutton_set(ctrl, dlg, button); - } else if (event == EVENT_VALCHANGE) { - int oldproto = conf_get_int(conf, CONF_protocol); - int newproto, port; + /* + * Refresh the states of the controls from Conf. + * + * When refreshing these controls, we have to watch out for + * re-entrancy: because there are two controls involved, the + * refresh is not atomic, so the VALCHANGE and/or SELCHANGE + * callbacks resulting from our updates here might cause other + * settings here to change unwantedly. (E.g. setting the list + * selection shouldn't trigger the SELCHANGE side effect of + * selecting the Other radio button; setting the radio button + * to Other here shouldn't have the side effect of selecting + * whatever protocol is _currently_ selected in the list box, + * if we haven't selected the right one yet.) + */ + hp->mid_refresh = true; + + if (ctrl == hp->protradio) { + /* Available buttons were set up when control was created. + * Just select one of them, possibly. */ + for (int button = 0; button < ctrl->radio.nbuttons; button++) + /* The final button is "Other:". If we reach that one, the + * current protocol must be in the drop list, so we should + * select the "Other:" button. */ + if (curproto == ctrl->radio.buttondata[button].i || + button == ctrl->radio.nbuttons-1) { + dlg_radiobutton_set(ctrl, dlg, button); + break; + } + } else if (ctrl == hp->protlist) { + int curentry = -1; + dlg_update_start(ctrl, dlg); + dlg_listbox_clear(ctrl, dlg); + assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT); + for (size_t i = n_ui_backends; + i < PROTOCOL_LIMIT && backends[i]; i++) { + dlg_listbox_addwithid(ctrl, dlg, + backends[i]->displayname, + backends[i]->protocol); + if (backends[i]->protocol == curproto) + curentry = i - n_ui_backends; + } + if (curentry > 0) { + /* + * The currently configured protocol is one of the + * list-box ones, so select it in protlist. + * + * (The corresponding refresh event for protradio + * should have selected the "Other:" radio button, to + * keep things consistent.) + */ + dlg_listbox_select(ctrl, dlg, curentry); + } else { + /* + * If the currently configured protocol is one of the + * radio buttons, we must still ensure *something* is + * selected in the list box. The sensible default is + * the first list element, which be_*.c ought to have + * arranged to be the 'runner-up' in protocol + * popularity out of the ones relegated to the list + * box. + * + * We don't make much effort to retain the state of + * the list box when it doesn't correspond to an + * actual protocol. So it's easy for this case to be + * reached as a side effect of other actions, e.g. + * loading a saved session that has a radio-button + * protocol configured. + */ + dlg_listbox_select(ctrl, dlg, 0); + } + dlg_update_done(ctrl, dlg); + } - button = dlg_radiobutton_get(ctrl, dlg); - assert(button >= 0 && button < ctrl->radio.nbuttons); - newproto = ctrl->radio.buttondata[button].i; - conf_set_int(conf, CONF_protocol, newproto); + hp->mid_refresh = false; + } else if (!hp->mid_refresh) { + /* + * Potentially update Conf from the states of the controls. + */ + int newproto = curproto; - if (oldproto != newproto) { - const struct BackendVtable *ovt = backend_vt_from_proto(oldproto); + if (event == EVENT_VALCHANGE && ctrl == hp->protradio) { + int button = dlg_radiobutton_get(ctrl, dlg); + assert(button >= 0 && button < ctrl->radio.nbuttons); + if (ctrl->radio.buttondata[button].i == -1) { + /* + * The 'Other' radio button was selected, which means we + * have to set CONF_protocol based on the currently + * selected list box entry. + * + * (We conditionalise this on there _being_ a selected + * list box entry. I hope the case where nothing is + * selected can't actually come up except during + * initialisation, and I also hope that hp->mid_session + * will prevent that case from getting here. But as a + * last-ditch fallback, this if statement should at least + * guarantee that we don't pass a nonsense value to + * dlg_listbox_getid.) + */ + int i = dlg_listbox_index(hp->protlist, dlg); + if (i >= 0) + newproto = dlg_listbox_getid(hp->protlist, dlg, i); + } else { + newproto = ctrl->radio.buttondata[button].i; + } + } else if (event == EVENT_SELCHANGE && ctrl == hp->protlist) { + int i = dlg_listbox_index(ctrl, dlg); + if (i >= 0) { + newproto = dlg_listbox_getid(ctrl, dlg, i); + /* Select the "Other" radio button, too */ + dlg_radiobutton_set(hp->protradio, dlg, + hp->protradio->radio.nbuttons-1); + } + } + + if (newproto != curproto) { + conf_set_int(conf, CONF_protocol, newproto); + + const struct BackendVtable *cvt = backend_vt_from_proto(curproto); const struct BackendVtable *nvt = backend_vt_from_proto(newproto); - assert(ovt); + assert(cvt); assert(nvt); - /* Iff the user hasn't changed the port from the old protocol's - * default, update it with the new protocol's default. - * (This includes a "default" of 0, implying that there is no - * sensible default for that protocol; in this case it's - * displayed as a blank.) + /* + * Iff the user hasn't changed the port from the old + * protocol's default, update it with the new protocol's + * default. + * + * (This includes a "default" of 0, implying that there is + * no sensible default for that protocol; in this case + * it's displayed as a blank.) + * * This helps with the common case of tabbing through the * controls in order and setting a non-default port before * getting to the protocol; we want that non-default port - * to be preserved. */ - port = conf_get_int(conf, CONF_port); - if (port == ovt->default_port) + * to be preserved. + */ + int port = conf_get_int(conf, CONF_port); + if (port == cvt->default_port) conf_set_int(conf, CONF_port, nvt->default_port); + + dlg_refresh(hp->host, dlg); + dlg_refresh(hp->port, dlg); } - dlg_refresh(hp->host, dlg); - dlg_refresh(hp->port, dlg); } } @@ -505,6 +607,7 @@ static void hklist_handler(union control *ctrl, dlgparam *dlg, static const struct { const char *s; int k; } hks[] = { { "Ed25519", HK_ED25519 }, + { "Ed448", HK_ED448 }, { "ECDSA", HK_ECDSA }, { "DSA", HK_DSA }, { "RSA", HK_RSA }, @@ -858,18 +961,12 @@ struct colour_data { union control *listbox, *redit, *gedit, *bedit, *button; }; +/* Array of the user-visible colour names defined in the list macro in + * putty.h */ static const char *const colours[] = { - "Default Foreground", "Default Bold Foreground", - "Default Background", "Default Bold Background", - "Cursor Text", "Cursor Colour", - "ANSI Black", "ANSI Black Bold", - "ANSI Red", "ANSI Red Bold", - "ANSI Green", "ANSI Green Bold", - "ANSI Yellow", "ANSI Yellow Bold", - "ANSI Blue", "ANSI Blue Bold", - "ANSI Magenta", "ANSI Magenta Bold", - "ANSI Cyan", "ANSI Cyan Bold", - "ANSI White", "ANSI White Bold" + #define CONF_COLOUR_NAME_DECL(id,name) name, + CONF_COLOUR_LIST(CONF_COLOUR_NAME_DECL) + #undef CONF_COLOUR_NAME_DECL }; static void colour_handler(union control *ctrl, dlgparam *dlg, @@ -1104,7 +1201,7 @@ static void environ_handler(union control *ctrl, dlgparam *dlg, return; } conf_set_str_str(conf, CONF_environmt, key, val); - str = dupcat(key, "\t", val, NULL); + str = dupcat(key, "\t", val); dlg_editbox_set(ed->varbox, dlg, ""); dlg_editbox_set(ed->valbox, dlg, ""); sfree(str); @@ -1235,7 +1332,7 @@ static void portfwd_handler(union control *ctrl, dlgparam *dlg, val = dupstr("D"); /* special case */ } - key = dupcat(family, type, src, NULL); + key = dupcat(family, type, src); sfree(src); if (conf_get_str_str_opt(conf, CONF_portfwd, key)) { @@ -1402,7 +1499,7 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg, if (!strcmp(sval, options[i].name)) break; /* needs escaping */ if (i < lenof(options) || sval[0] == '=') { - char *escaped = dupcat("=", sval, (const char *)NULL); + char *escaped = dupcat("=", sval); dlg_editbox_set(ctrl, dlg, escaped); sfree(escaped); } else { @@ -1429,7 +1526,7 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg, #endif ) { #ifdef NAMED_CLIPBOARDS - const char *sval = dlg_editbox_get(ctrl, dlg); + char *sval = dlg_editbox_get(ctrl, dlg); int i; for (i = 0; i < lenof(options); i++) @@ -1444,6 +1541,8 @@ static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg, sval++; conf_set_str(conf, strsetting, sval); } + + sfree(sval); #else int index = dlg_listbox_index(ctrl, dlg); if (index >= 0) { @@ -1468,9 +1567,118 @@ static void clipboard_control(struct controlset *s, const char *label, #endif } +static void serial_parity_handler(union control *ctrl, dlgparam *dlg, + void *data, int event) +{ + static const struct { + const char *name; + int val; + } parities[] = { + {"None", SER_PAR_NONE}, + {"Odd", SER_PAR_ODD}, + {"Even", SER_PAR_EVEN}, + {"Mark", SER_PAR_MARK}, + {"Space", SER_PAR_SPACE}, + }; + int mask = ctrl->listbox.context.i; + int i, j; + Conf *conf = (Conf *)data; + + if (event == EVENT_REFRESH) { + /* Fetching this once at the start of the function ensures we + * remember what the right value is supposed to be when + * operations below cause reentrant calls to this function. */ + int oldparity = conf_get_int(conf, CONF_serparity); + + dlg_update_start(ctrl, dlg); + dlg_listbox_clear(ctrl, dlg); + for (i = 0; i < lenof(parities); i++) { + if (mask & (1 << parities[i].val)) + dlg_listbox_addwithid(ctrl, dlg, parities[i].name, + parities[i].val); + } + for (i = j = 0; i < lenof(parities); i++) { + if (mask & (1 << parities[i].val)) { + if (oldparity == parities[i].val) { + dlg_listbox_select(ctrl, dlg, j); + break; + } + j++; + } + } + if (i == lenof(parities)) { /* an unsupported setting was chosen */ + dlg_listbox_select(ctrl, dlg, 0); + oldparity = SER_PAR_NONE; + } + dlg_update_done(ctrl, dlg); + conf_set_int(conf, CONF_serparity, oldparity); /* restore */ + } else if (event == EVENT_SELCHANGE) { + int i = dlg_listbox_index(ctrl, dlg); + if (i < 0) + i = SER_PAR_NONE; + else + i = dlg_listbox_getid(ctrl, dlg, i); + conf_set_int(conf, CONF_serparity, i); + } +} + +static void serial_flow_handler(union control *ctrl, dlgparam *dlg, + void *data, int event) +{ + static const struct { + const char *name; + int val; + } flows[] = { + {"None", SER_FLOW_NONE}, + {"XON/XOFF", SER_FLOW_XONXOFF}, + {"RTS/CTS", SER_FLOW_RTSCTS}, + {"DSR/DTR", SER_FLOW_DSRDTR}, + }; + int mask = ctrl->listbox.context.i; + int i, j; + Conf *conf = (Conf *)data; + + if (event == EVENT_REFRESH) { + /* Fetching this once at the start of the function ensures we + * remember what the right value is supposed to be when + * operations below cause reentrant calls to this function. */ + int oldflow = conf_get_int(conf, CONF_serflow); + + dlg_update_start(ctrl, dlg); + dlg_listbox_clear(ctrl, dlg); + for (i = 0; i < lenof(flows); i++) { + if (mask & (1 << flows[i].val)) + dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val); + } + for (i = j = 0; i < lenof(flows); i++) { + if (mask & (1 << flows[i].val)) { + if (oldflow == flows[i].val) { + dlg_listbox_select(ctrl, dlg, j); + break; + } + j++; + } + } + if (i == lenof(flows)) { /* an unsupported setting was chosen */ + dlg_listbox_select(ctrl, dlg, 0); + oldflow = SER_FLOW_NONE; + } + dlg_update_done(ctrl, dlg); + conf_set_int(conf, CONF_serflow, oldflow);/* restore */ + } else if (event == EVENT_SELCHANGE) { + int i = dlg_listbox_index(ctrl, dlg); + if (i < 0) + i = SER_FLOW_NONE; + else + i = dlg_listbox_getid(ctrl, dlg, i); + conf_set_int(conf, CONF_serflow, i); + } +} + void setup_config_box(struct controlbox *b, bool midsession, int protocol, int protcfginfo) { + const struct BackendVtable *backvt; struct controlset *s; struct sessionsaver_data *ssd; struct charclass_data *ccd; @@ -1480,6 +1688,7 @@ void setup_config_box(struct controlbox *b, bool midsession, struct portfwd_data *pfd; struct manual_hostkey_data *mh; union control *c; + bool resize_forbidden = false; char *str; ssd = (struct sessionsaver_data *) @@ -1519,6 +1728,7 @@ void setup_config_box(struct controlbox *b, bool midsession, if (!midsession) { struct hostport *hp = (struct hostport *) ctrl_alloc(b, sizeof(struct hostport)); + memset(hp, 0, sizeof(*hp)); s = ctrl_getset(b, "Session", "hostport", "Specify the destination you want to connect to"); @@ -1533,26 +1743,54 @@ void setup_config_box(struct controlbox *b, bool midsession, config_port_handler, I(0), I(0)); c->generic.column = 1; hp->port = c; - ctrl_columns(s, 1, 100); - if (!backend_vt_from_proto(PROT_SSH)) { - ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 3, - HELPCTX(session_hostname), - config_protocolbuttons_handler, P(hp), - "Raw", 'w', I(PROT_RAW), - "Telnet", 't', I(PROT_TELNET), - "Rlogin", 'i', I(PROT_RLOGIN), - NULL); - } else { - ctrl_radiobuttons(s, "Connection type:", NO_SHORTCUT, 4, + ctrl_columns(s, 1, 100); + c = ctrl_text(s, "Connection type:", HELPCTX(session_hostname)); + ctrl_columns(s, 2, 62, 38); + c = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(session_hostname), - config_protocolbuttons_handler, P(hp), - "Raw", 'w', I(PROT_RAW), - "Telnet", 't', I(PROT_TELNET), - "Rlogin", 'i', I(PROT_RLOGIN), - "SSH", 's', I(PROT_SSH), - NULL); + config_protocols_handler, P(hp), NULL); + c->generic.column = 0; + hp->protradio = c; + c->radio.buttons = sresize(c->radio.buttons, PROTOCOL_LIMIT, char *); + c->radio.shortcuts = sresize(c->radio.shortcuts, PROTOCOL_LIMIT, char); + c->radio.buttondata = sresize(c->radio.buttondata, PROTOCOL_LIMIT, + intorptr); + assert(c->radio.nbuttons == 0); + /* UI design assumes there exists at least one 'real' radio button */ + assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT); + for (size_t i = 0; i < n_ui_backends; i++) { + assert(backends[i]); + c->radio.buttons[c->radio.nbuttons] = + dupstr(backends[i]->displayname); + c->radio.shortcuts[c->radio.nbuttons] = + (backends[i]->protocol == PROT_SSH ? 's' : + backends[i]->protocol == PROT_SERIAL ? 'r' : + backends[i]->protocol == PROT_RAW ? 'w' : /* FIXME unused */ + NO_SHORTCUT); + c->radio.buttondata[c->radio.nbuttons] = + I(backends[i]->protocol); + c->radio.nbuttons++; } + /* UI design assumes there exists at least one droplist entry */ + assert(backends[c->radio.nbuttons]); + + c->radio.buttons[c->radio.nbuttons] = dupstr("Other:"); + c->radio.shortcuts[c->radio.nbuttons] = 't'; + c->radio.buttondata[c->radio.nbuttons] = I(-1); + c->radio.nbuttons++; + + c = ctrl_droplist(s, NULL, NO_SHORTCUT, 100, + HELPCTX(session_hostname), + config_protocols_handler, P(hp)); + hp->protlist = c; + /* droplist is populated in config_protocols_handler */ + c->generic.column = 1; + + /* Vertically centre the two protocol controls w.r.t. each other */ + hp->protlist->generic.align_next_to = hp->protradio; + + ctrl_columns(s, 1, 100); } /* @@ -1852,17 +2090,23 @@ void setup_config_box(struct controlbox *b, bool midsession, ctrl_settitle(b, "Window", str); sfree(str); - s = ctrl_getset(b, "Window", "size", "Set the size of the window"); - ctrl_columns(s, 2, 50, 50); - c = ctrl_editbox(s, "Columns", 'm', 100, - HELPCTX(window_size), - conf_editbox_handler, I(CONF_width), I(-1)); - c->generic.column = 0; - c = ctrl_editbox(s, "Rows", 'r', 100, - HELPCTX(window_size), - conf_editbox_handler, I(CONF_height),I(-1)); - c->generic.column = 1; - ctrl_columns(s, 1, 100); + backvt = backend_vt_from_proto(protocol); + if (backvt) + resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN); + + if (!resize_forbidden || !midsession) { + s = ctrl_getset(b, "Window", "size", "Set the size of the window"); + ctrl_columns(s, 2, 50, 50); + c = ctrl_editbox(s, "Columns", 'm', 100, + HELPCTX(window_size), + conf_editbox_handler, I(CONF_width), I(-1)); + c->generic.column = 0; + c = ctrl_editbox(s, "Rows", 'r', 100, + HELPCTX(window_size), + conf_editbox_handler, I(CONF_height),I(-1)); + c->generic.column = 1; + ctrl_columns(s, 1, 100); + } s = ctrl_getset(b, "Window", "scrollback", "Control the scrollback in the window"); @@ -2296,67 +2540,23 @@ void setup_config_box(struct controlbox *b, bool midsession, } /* - * The Telnet panel exists in the base config box, and in a - * mid-session reconfig box _if_ we're using Telnet. - */ - if (!midsession || protocol == PROT_TELNET) { - /* - * The Connection/Telnet panel. - */ - ctrl_settitle(b, "Connection/Telnet", - "Options controlling Telnet connections"); - - s = ctrl_getset(b, "Connection/Telnet", "protocol", - "Telnet protocol adjustments"); - - if (!midsession) { - ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:", - NO_SHORTCUT, 2, - HELPCTX(telnet_oldenviron), - conf_radiobutton_bool_handler, - I(CONF_rfc_environ), - "BSD (commonplace)", 'b', I(false), - "RFC 1408 (unusual)", 'f', I(true), NULL); - ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, - HELPCTX(telnet_passive), - conf_radiobutton_bool_handler, - I(CONF_passive_telnet), - "Passive", I(true), "Active", I(false), NULL); - } - ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k', - HELPCTX(telnet_specialkeys), - conf_checkbox_handler, - I(CONF_telnet_keyboard)); - ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M", - 'm', HELPCTX(telnet_newline), - conf_checkbox_handler, - I(CONF_telnet_newline)); - } - - if (!midsession) { - - /* - * The Connection/Rlogin panel. - */ - ctrl_settitle(b, "Connection/Rlogin", - "Options controlling Rlogin connections"); - - s = ctrl_getset(b, "Connection/Rlogin", "data", - "Data to send to the server"); - ctrl_editbox(s, "Local username:", 'l', 50, - HELPCTX(rlogin_localuser), - conf_editbox_handler, I(CONF_localusername), I(1)); - - } - - /* - * All the SSH stuff is omitted in PuTTYtel, or in a reconfig - * when we're not doing SSH. + * Each per-protocol configuration GUI panel is conditionally + * displayed. We don't display it if this binary doesn't contain a + * backend for its protocol at all; we don't display it if we're + * already in mid-session with a different protocol selected; and + * even if we _do_ have this protocol selected, we don't display + * the panel if the protocol doesn't permit any mid-session + * reconfiguration anyway. */ - if (backend_vt_from_proto(PROT_SSH) && - (!midsession || protocol == PROT_SSH)) { +#define DISPLAY_RECONFIGURABLE_PROTOCOL(which_proto) \ + (backend_vt_from_proto(which_proto) && \ + (!midsession || protocol == (which_proto))) +#define DISPLAY_NON_RECONFIGURABLE_PROTOCOL(which_proto) \ + (backend_vt_from_proto(which_proto) && !midsession) + if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSH) || + DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSHCONN)) { /* * The Connection/SSH panel. */ @@ -2486,6 +2686,10 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_hklist), hklist_handler, P(NULL)); c->listbox.height = 5; + + ctrl_checkbox(s, "Prefer algorithms for which a host key is known", + 'p', HELPCTX(ssh_hk_known), conf_checkbox_handler, + I(CONF_ssh_prefer_known_hostkeys)); } /* @@ -2568,6 +2772,10 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_auth_bypass), conf_checkbox_handler, I(CONF_ssh_no_userauth)); + ctrl_checkbox(s, "Disconnect if authentication succeeds trivially", + 'n', HELPCTX(ssh_no_trivial_userauth), + conf_checkbox_handler, + I(CONF_ssh_no_trivial_userauth)); s = ctrl_getset(b, "Connection/SSH/Auth", "methods", "Authentication methods"); @@ -2708,6 +2916,7 @@ void setup_config_box(struct controlbox *b, bool midsession, HELPCTX(ssh_ttymodes), ttymodes_handler, P(td), P(NULL)); td->valbox->generic.column = 1; + td->valbox->generic.align_next_to = td->valradio; ctrl_tabdelay(s, td->setbutton); } @@ -2861,4 +3070,129 @@ void setup_config_box(struct controlbox *b, bool midsession, sshbug_handler, I(CONF_sshbug_rsa1)); } } + + if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SERIAL)) { + const BackendVtable *ser_vt = backend_vt_from_proto(PROT_SERIAL); + + /* + * The Connection/Serial panel. + */ + ctrl_settitle(b, "Connection/Serial", + "Options controlling local serial lines"); + + if (!midsession) { + /* + * We don't permit switching to a different serial port in + * midflight, although we do allow all other + * reconfiguration. + */ + s = ctrl_getset(b, "Connection/Serial", "serline", + "Select a serial line"); + ctrl_editbox(s, "Serial line to connect to", 'l', 40, + HELPCTX(serial_line), + conf_editbox_handler, I(CONF_serline), I(1)); + } + + s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line"); + ctrl_editbox(s, "Speed (baud)", 's', 40, + HELPCTX(serial_speed), + conf_editbox_handler, I(CONF_serspeed), I(-1)); + ctrl_editbox(s, "Data bits", 'b', 40, + HELPCTX(serial_databits), + conf_editbox_handler, I(CONF_serdatabits), I(-1)); + /* + * Stop bits come in units of one half. + */ + ctrl_editbox(s, "Stop bits", 't', 40, + HELPCTX(serial_stopbits), + conf_editbox_handler, I(CONF_serstopbits), I(-2)); + ctrl_droplist(s, "Parity", 'p', 40, + HELPCTX(serial_parity), serial_parity_handler, + I(ser_vt->serial_parity_mask)); + ctrl_droplist(s, "Flow control", 'f', 40, + HELPCTX(serial_flow), serial_flow_handler, + I(ser_vt->serial_flow_mask)); + } + + if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_TELNET)) { + /* + * The Connection/Telnet panel. + */ + ctrl_settitle(b, "Connection/Telnet", + "Options controlling Telnet connections"); + + s = ctrl_getset(b, "Connection/Telnet", "protocol", + "Telnet protocol adjustments"); + + if (!midsession) { + ctrl_radiobuttons(s, "Handling of OLD_ENVIRON ambiguity:", + NO_SHORTCUT, 2, + HELPCTX(telnet_oldenviron), + conf_radiobutton_bool_handler, + I(CONF_rfc_environ), + "BSD (commonplace)", 'b', I(false), + "RFC 1408 (unusual)", 'f', I(true), NULL); + ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, + HELPCTX(telnet_passive), + conf_radiobutton_bool_handler, + I(CONF_passive_telnet), + "Passive", I(true), "Active", I(false), NULL); + } + ctrl_checkbox(s, "Keyboard sends Telnet special commands", 'k', + HELPCTX(telnet_specialkeys), + conf_checkbox_handler, + I(CONF_telnet_keyboard)); + ctrl_checkbox(s, "Return key sends Telnet New Line instead of ^M", + 'm', HELPCTX(telnet_newline), + conf_checkbox_handler, + I(CONF_telnet_newline)); + } + + if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_RLOGIN)) { + /* + * The Connection/Rlogin panel. + */ + ctrl_settitle(b, "Connection/Rlogin", + "Options controlling Rlogin connections"); + + s = ctrl_getset(b, "Connection/Rlogin", "data", + "Data to send to the server"); + ctrl_editbox(s, "Local username:", 'l', 50, + HELPCTX(rlogin_localuser), + conf_editbox_handler, I(CONF_localusername), I(1)); + + } + + if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_SUPDUP)) { + /* + * The Connection/SUPDUP panel. + */ + ctrl_settitle(b, "Connection/SUPDUP", + "Options controlling SUPDUP connections"); + + s = ctrl_getset(b, "Connection/SUPDUP", "main", NULL); + + ctrl_editbox(s, "Location string", 'l', 70, + HELPCTX(supdup_location), + conf_editbox_handler, I(CONF_supdup_location), + I(1)); + + ctrl_radiobuttons(s, "Extended ASCII Character set:", 'e', 4, + HELPCTX(supdup_ascii), + conf_radiobutton_handler, + I(CONF_supdup_ascii_set), + "None", I(SUPDUP_CHARSET_ASCII), + "ITS", I(SUPDUP_CHARSET_ITS), + "WAITS", I(SUPDUP_CHARSET_WAITS), NULL); + + ctrl_checkbox(s, "**MORE** processing", 'm', + HELPCTX(supdup_more), + conf_checkbox_handler, + I(CONF_supdup_more)); + + ctrl_checkbox(s, "Terminal scrolling", 's', + HELPCTX(supdup_scroll), + conf_checkbox_handler, + I(CONF_supdup_scroll)); + } } diff --git a/configure b/configure index a86baec..5b258b3 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for putty 0.73. +# Generated by GNU Autoconf 2.69 for putty 0.76. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -577,8 +577,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='putty' PACKAGE_TARNAME='putty' -PACKAGE_VERSION='0.73' -PACKAGE_STRING='putty 0.73' +PACKAGE_VERSION='0.76' +PACKAGE_STRING='putty 0.76' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -649,7 +649,6 @@ am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE -am__quote am__include DEPDIR OBJEXT @@ -727,7 +726,8 @@ PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR -SHELL' +SHELL +am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking @@ -1303,7 +1303,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures putty 0.73 to adapt to many kinds of systems. +\`configure' configures putty 0.76 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1370,7 +1370,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of putty 0.73:";; + short | recursive ) echo "Configuration of putty 0.76:";; esac cat <<\_ACEOF @@ -1478,7 +1478,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -putty configure 0.73 +putty configure 0.76 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -1889,7 +1889,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by putty $as_me 0.73, which was +It was created by putty $as_me 0.76, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -2241,7 +2241,7 @@ ac_config_files="$ac_config_files Makefile" ac_config_headers="$ac_config_headers uxconfig.h:uxconfig.in" -am__api_version='1.15' +am__api_version='1.16' ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do @@ -2756,7 +2756,7 @@ fi # Define the identity of the package. PACKAGE='putty' - VERSION='0.73' + VERSION='0.76' cat >>confdefs.h <<_ACEOF @@ -2786,8 +2786,8 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: -# -# +# +# mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The @@ -2838,7 +2838,7 @@ END Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation -that behaves properly: . +that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM @@ -2947,45 +2947,45 @@ DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" - -am_make=${MAKE-make} -cat > confinc << 'END' +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' am__doit: - @echo this is the am__doit target + @echo this is the am__doit target >confinc.out .PHONY: am__doit END -# If we don't find an include directive, just comment out the code. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 -$as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= -_am_result=none -# First try GNU make style include. -echo "include confinc" > confmf -# Ignore all kinds of additional output from 'make'. -case `$am_make -s -f confmf 2> /dev/null` in #( -*the\ am__doit\ target*) - am__include=include - am__quote= - _am_result=GNU - ;; -esac -# Now try BSD make style include. -if test "$am__include" = "#"; then - echo '.include "confinc"' > confmf - case `$am_make -s -f confmf 2> /dev/null` in #( - *the\ am__doit\ target*) - am__include=.include - am__quote="\"" - _am_result=BSD +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : ;; - esac -fi - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 -$as_echo "$_am_result" >&6; } -rm -f confinc confmf +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +$as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : @@ -5860,7 +5860,7 @@ $as_echo "#define HAVE_LIBX11 /**/" >>confdefs.h fi -for ac_func in getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx fstatat dirfd futimes setpwent endpwent +for ac_func in getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx fstatat dirfd futimes setpwent endpwent getauxval elf_aux_info sysctlbyname do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" @@ -5884,7 +5884,7 @@ cat >>confdefs.h <<_ACEOF #define HAVE_DECL_CLOCK_MONOTONIC $ac_have_decl _ACEOF -for ac_header in sys/auxv.h asm/hwcap.h glob.h +for ac_header in sys/auxv.h asm/hwcap.h sys/sysctl.h sys/types.h glob.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" @@ -6004,13 +6004,70 @@ fi if test "x$GCC" = "xyes"; then : - WARNINGOPTS='-Wall -Werror -Wpointer-arith -Wvla' + WARNINGOPTS='-Wall -Wpointer-arith -Wvla' else : fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pow" >&5 +$as_echo_n "checking for library containing pow... " >&6; } +if ${ac_cv_search_pow+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pow (); +int +main () +{ +return pow (); + ; + return 0; +} +_ACEOF +for ac_lib in '' m; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_pow=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_pow+:} false; then : + break +fi +done +if ${ac_cv_search_pow+:} false; then : + +else + ac_cv_search_pow=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pow" >&5 +$as_echo "$ac_cv_search_pow" >&6; } +ac_res=$ac_cv_search_pow +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +fi + + cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -6557,7 +6614,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by putty $as_me 0.73, which was +This file was extended by putty $as_me 0.76, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6623,7 +6680,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -putty config.status 0.73 +putty config.status 0.76 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -6742,7 +6799,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # -AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _ACEOF @@ -7354,29 +7411,35 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. - case $CONFIG_FILES in - *\'*) eval set x "$CONFIG_FILES" ;; - *) set x $CONFIG_FILES ;; - esac + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac shift - for mf + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf do # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named 'Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line + am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. - if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then - dirpart=`$as_dirname -- "$mf" || -$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$mf" : 'X\(//\)[^/]' \| \ - X"$mf" : 'X\(//\)$' \| \ - X"$mf" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$mf" | + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -7394,53 +7457,48 @@ $as_echo X"$mf" | q } s/.*/./; q'` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running 'make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "$am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`$as_dirname -- "$file" || -$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$file" : 'X\(//\)[^/]' \| \ - X"$file" : 'X\(//\)$' \| \ - X"$file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } - /^X\(\/\/\)$/{ + /^X\/\(\/\/\)$/{ s//\1/ q } - /^X\(\/\).*/{ + /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` - as_dir=$dirpart/$fdir; as_fn_mkdir_p - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? done + if test $am_rc -ne 0; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. Try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk } ;; diff --git a/configure.ac b/configure.ac index 968b168..06a3766 100644 --- a/configure.ac +++ b/configure.ac @@ -7,10 +7,10 @@ # Version number is substituted by Buildscr for releases, snapshots # and custom builds out of svn; X.XX shows up in ad-hoc developer # builds, which shouldn't matter -AC_INIT(putty, 0.73) +AC_INIT(putty, 0.76) AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in]) -AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AM_INIT_AUTOMAKE([-Wall foreign]) AC_PROG_INSTALL AC_PROG_RANLIB @@ -169,9 +169,9 @@ AC_CHECK_LIB(X11, XOpenDisplay, [GTK_LIBS="-lX11 $GTK_LIBS" AC_DEFINE([HAVE_LIBX11],[],[Define if libX11.a is available])]) -AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx fstatat dirfd futimes setpwent endpwent]) +AC_CHECK_FUNCS([getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx fstatat dirfd futimes setpwent endpwent getauxval elf_aux_info sysctlbyname]) AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include ]]) -AC_CHECK_HEADERS([sys/auxv.h asm/hwcap.h glob.h]) +AC_CHECK_HEADERS([sys/auxv.h asm/hwcap.h sys/sysctl.h sys/types.h glob.h]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Define if clock_gettime() is available])]) AC_CACHE_CHECK([for SO_PEERCRED and dependencies], [x_cv_linux_so_peercred], [ @@ -198,12 +198,14 @@ AS_IF([test AS_VAR_GET(x_cv_linux_so_peercred) = yes], if test "x$GCC" = "xyes"; then : - AC_SUBST(WARNINGOPTS, ['-Wall -Werror -Wpointer-arith -Wvla']) + AC_SUBST(WARNINGOPTS, ['-Wall -Wpointer-arith -Wvla']) else : AC_SUBST(WARNINGOPTS, []) fi +AC_SEARCH_LIBS([pow], [m]) + AC_OUTPUT if test "$gtk_version_desired" = "no"; then cat < +#include + +#include "putty.h" +#include "misc.h" +#include "console.h" + +const char hk_absentmsg_common_fmt[] = + "The server's host key is not cached. You have no guarantee\n" + "that the server is the computer you think it is.\n" + "The server's %s key fingerprint is:\n" + "%s\n"; +const char hk_absentmsg_interactive_intro[] = + "If you trust this host, enter \"y\" to add the key to\n" + "PuTTY's cache and carry on connecting.\n" + "If you want to carry on connecting just once, without\n" + "adding the key to the cache, enter \"n\".\n" + "If you do not trust this host, press Return to abandon the\n" + "connection.\n"; +const char hk_absentmsg_interactive_prompt[] = + "Store key in cache? (y/n, Return cancels connection, " + "i for more info) "; + +const char hk_wrongmsg_common_fmt[] = + "WARNING - POTENTIAL SECURITY BREACH!\n" + "The server's host key does not match the one PuTTY has\n" + "cached. This means that either the server administrator\n" + "has changed the host key, or you have actually connected\n" + "to another computer pretending to be the server.\n" + "The new %s key fingerprint is:\n" + "%s\n"; +const char hk_wrongmsg_interactive_intro[] = + "If you were expecting this change and trust the new key,\n" + "enter \"y\" to update PuTTY's cache and continue connecting.\n" + "If you want to carry on connecting but without updating\n" + "the cache, enter \"n\".\n" + "If you want to abandon the connection completely, press\n" + "Return to cancel. Pressing Return is the ONLY guaranteed\n" + "safe choice.\n"; +const char hk_wrongmsg_interactive_prompt[] = + "Update cached key? (y/n, Return cancels connection, " + "i for more info) "; + +const char weakcrypto_msg_common_fmt[] = + "The first %s supported by the server is\n" + "%s, which is below the configured warning threshold.\n"; + +const char weakhk_msg_common_fmt[] = + "The first host key type we have stored for this server\n" + "is %s, which is below the configured warning threshold.\n" + "The server also provides the following types of host key\n" + "above the threshold, which we do not have stored:\n" + "%s\n"; + +const char console_continue_prompt[] = "Continue with connection? (y/n) "; +const char console_abandoned_msg[] = "Connection abandoned.\n"; + +bool console_batch_mode = false; + +/* + * Error message and/or fatal exit functions, all based on + * console_print_error_msg which the platform front end provides. + */ +void console_print_error_msg_fmt_v( + const char *prefix, const char *fmt, va_list ap) +{ + char *msg = dupvprintf(fmt, ap); + console_print_error_msg(prefix, msg); + sfree(msg); +} + +void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v(prefix, fmt, ap); + va_end(ap); +} + +void modalfatalbox(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap); + va_end(ap); + cleanup_exit(1); +} + +void nonfatal(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + console_print_error_msg_fmt_v("ERROR", fmt, ap); + va_end(ap); +} + +void console_connection_fatal(Seat *seat, const char *msg) +{ + console_print_error_msg("FATAL ERROR", msg); + cleanup_exit(1); +} + +/* + * Console front ends redo their select() or equivalent every time, so + * they don't need separate timer handling. + */ +void timer_change_notify(unsigned long next) +{ +} diff --git a/console.h b/console.h new file mode 100644 index 0000000..a8b2246 --- /dev/null +++ b/console.h @@ -0,0 +1,17 @@ +/* + * Common pieces between the platform console frontend modules. + */ + +extern const char hk_absentmsg_common_fmt[]; +extern const char hk_absentmsg_interactive_intro[]; +extern const char hk_absentmsg_interactive_prompt[]; +extern const char hk_wrongmsg_common_fmt[]; +extern const char hk_wrongmsg_interactive_intro[]; +extern const char hk_wrongmsg_interactive_prompt[]; + +extern const char weakcrypto_msg_common_fmt[]; + +extern const char weakhk_msg_common_fmt[]; + +extern const char console_continue_prompt[]; +extern const char console_abandoned_msg[]; diff --git a/contrib/encodelib.py b/contrib/encodelib.py index b9d159e..f64d963 100644 --- a/contrib/encodelib.py +++ b/contrib/encodelib.py @@ -4,14 +4,21 @@ # The idea of this is that you can use it to manually construct key # exchange sequences of interesting kinds, for testing purposes. -import struct, random +import sys +import struct +import random + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + +def tobytes(s): + return s if isinstance(s, bytes) else s.encode('ASCII') def boolean(b): - return "\1" if b else "\0" + return b"\1" if b else b"\0" def byte(b): assert 0 <= b < 0x100 - return chr(b) + return bytes([b]) def uint32(u): assert 0 <= u < 0x100000000 @@ -22,25 +29,24 @@ def uint64(u): return struct.pack(">L", u) def string(s): - return uint32(len(s)) + s + return uint32(len(s)) + tobytes(s) def mpint(m): - s = "" - lastbyte = 0 + s = [] while m > 0: - lastbyte = m & 0xFF - s = chr(lastbyte) + s + s.append(m & 0xFF) m >>= 8 - if lastbyte & 0x80: - s = "\0" + s - return string(s) + if len(s) > 0 and (s[-1] & 0x80): + s.append(0) + s.reverse() + return string(bytes(s)) def name_list(ns): - s = "" - for n in ns: - assert "," not in n - if s != "": - s += "," + s = b"" + for n in map(tobytes, ns): + assert b"," not in n + if s != b"": + s += b"," s += n return string(s) @@ -52,7 +58,7 @@ def ssh_rsa_signature_blob(signature): def greeting(string): # Greeting at the start of an SSH connection. - return string + "\r\n" + return tobytes(string) + b"\r\n" # Packet types. SSH2_MSG_DISCONNECT = 1 @@ -122,8 +128,5 @@ def decode_uint32(s): def read_clearpkt(fh): length_field = fh.read(4) s = fh.read(decode_uint32(length_field)) - import sys - padlen = ord(s[0]) - s = s[1:-padlen] - msgtype = ord(s[0]) - return msgtype, s[1:] + padlen, msgtype = s[:2] + return msgtype, s[2:-padlen] diff --git a/contrib/kh2reg.py b/contrib/kh2reg.py index 272124d..cff06c8 100755 --- a/contrib/kh2reg.py +++ b/contrib/kh2reg.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#!/usr/bin/env python3 # Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY # host keys. @@ -73,8 +73,8 @@ def compute(self, key, string): def openssh_hashed_host_match(hashed_host, try_host): if hashed_host.startswith(b'|1|'): salt, expected = hashed_host[3:].split(b'|') - salt = base64.decodestring(salt) - expected = base64.decodestring(expected) + salt = base64.decodebytes(salt) + expected = base64.decodebytes(expected) mac = HMAC(hashlib.sha1, 64) else: return False # unrecognised magic number prefix @@ -225,14 +225,14 @@ def handle_line(line, output_formatter, try_hosts): # Treat as SSH-1-type host key. # Format: hostpat bits10 exp10 mod10 comment... # (PuTTY doesn't store the number of bits.) - keyparams = map (int, fields[2:4]) + keyparams = list(map(int, fields[2:4])) keytype = "rsa" else: # Treat as SSH-2-type host key. # Format: hostpat keytype keyblob64 comment... - sshkeytype, blob = fields[1], base64.decodestring( + sshkeytype, blob = fields[1], base64.decodebytes( fields[2].encode("ASCII")) # 'blob' consists of a number of @@ -259,12 +259,12 @@ def handle_line(line, output_formatter, try_hosts): keytype = "rsa2" # The rest of the subfields we can treat as an opaque list # of bignums (same numbers and order as stored by PuTTY). - keyparams = map (strtoint, subfields[1:]) + keyparams = list(map(strtoint, subfields[1:])) elif sshkeytype == "ssh-dss": keytype = "dss" # Same again. - keyparams = map (strtoint, subfields[1:]) + keyparams = list(map(strtoint, subfields[1:])) elif sshkeytype in nist_curves: keytype = sshkeytype @@ -302,7 +302,7 @@ def handle_line(line, output_formatter, try_hosts): keyparams = [curvename, x, y] - elif sshkeytype == "ssh-ed25519": + elif sshkeytype in { "ssh-ed25519", "ssh-ed448" }: keytype = sshkeytype if len(subfields) != 2: @@ -314,12 +314,14 @@ def handle_line(line, output_formatter, try_hosts): x_parity = y >> 255 y &= ~(1 << 255) - # Standard Ed25519 parameters. - p = 2**255 - 19 - d = 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3 + # Curve parameters. + p, d, a = { + "ssh-ed25519": (2**255 - 19, 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3, -1), + "ssh-ed448": (2**448-2**224-1, -39081, +1), + }[sshkeytype] - # Recover x^2 = (y^2 - 1) / (d y^2 + 1). - xx = (y*y - 1) * invert(d*y*y + 1, p) % p + # Recover x^2 = (y^2 - 1) / (d y^2 - a). + xx = (y*y - 1) * invert(d*y*y - a, p) % p # Take the square root. x = SqrtModP.root(xx, p) diff --git a/contrib/make1305.py b/contrib/make1305.py index 58dca67..66b7881 100755 --- a/contrib/make1305.py +++ b/contrib/make1305.py @@ -1,9 +1,11 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import sys import string from collections import namedtuple +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + class Multiprecision(object): def __init__(self, target, minval, maxval, words): self.target = target @@ -89,21 +91,21 @@ def extract_bits(self, start, bits=None): for i in range(nwords): srcpos = i * self.target.bits + start maxbits = min(self.target.bits, start + bits - srcpos) - wordindex = srcpos / self.target.bits + wordindex = srcpos // self.target.bits if srcpos % self.target.bits == 0: - word = self.getword(srcpos / self.target.bits) + word = self.getword(srcpos // self.target.bits) elif (wordindex+1 >= len(self.words) or srcpos % self.target.bits + maxbits < self.target.bits): word = self.target.new_value( "(%%s) >> %d" % (srcpos % self.target.bits), - self.getword(srcpos / self.target.bits)) + self.getword(srcpos // self.target.bits)) else: word = self.target.new_value( "((%%s) >> %d) | ((%%s) << %d)" % ( srcpos % self.target.bits, self.target.bits - (srcpos % self.target.bits)), - self.getword(srcpos / self.target.bits), - self.getword(srcpos / self.target.bits + 1)) + self.getword(srcpos // self.target.bits), + self.getword(srcpos // self.target.bits + 1)) if maxbits < self.target.bits and maxbits < bits: word = self.target.new_value( "(%%s) & ((((BignumInt)1) << %d)-1)" % maxbits, @@ -127,11 +129,11 @@ def __init__(self, bits): self.valindex = 0 self.stmts = [] self.generators = {} - self.bv_words = (130 + self.bits - 1) / self.bits + self.bv_words = (130 + self.bits - 1) // self.bits self.carry_index = 0 def nwords(self, maxval): - return (maxval.bit_length() + self.bits - 1) / self.bits + return (maxval.bit_length() + self.bits - 1) // self.bits def stmt(self, stmt, needed=False): index = len(self.stmts) @@ -149,7 +151,7 @@ def new_value(self, formatstr=None, *deps): return name def bigval_input(self, name, bits): - words = (bits + self.bits - 1) / self.bits + words = (bits + self.bits - 1) // self.bits # Expect not to require an entire extra word assert words == self.bv_words diff --git a/contrib/plinkfs b/contrib/plinkfs new file mode 100755 index 0000000..e4b4580 --- /dev/null +++ b/contrib/plinkfs @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 + +# Wrapper around the FUSE 'sshfs' client, which arranges to use Plink +# as the SSH transport subcommand. +# +# This is not totally trivial because sshfs assumes slightly more of +# OpenSSH's command-line syntax than Plink supports. So we actually +# give sshfs a subcommand which is this script itself, re-invoked with +# the --helper option. + +import sys +import os +import shlex + +if sys.argv[1:2] == ["--helper"]: + # Helper mode. Strip OpenSSH-specific '-o' options from the + # command line, and invoke Plink. + plink_command = ["plink"] + + it = iter(sys.argv) + next(it) # discard command name + next(it) # discard --helper + + for arg in it: + if arg == "-o": + next(it) # discard -o option + elif arg.startswith("-o"): + pass + else: + plink_command.append(arg) + + os.execvp(plink_command[0], plink_command) + +else: + # Normal mode, invoked by the user. + sshfs_command = [ + "sshfs", "-o", "ssh_command={} --helper".format( + os.path.realpath(__file__)) + ] + sys.argv[1:] + + os.execvp(sshfs_command[0], sshfs_command) diff --git a/contrib/samplekex.py b/contrib/samplekex.py index 2332e6a..1684289 100755 --- a/contrib/samplekex.py +++ b/contrib/samplekex.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Example Python script to synthesise the server end of an SSH key exchange. @@ -27,6 +27,8 @@ import sys, random from encodelib import * +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + # A random Diffie-Hellman group, taken from an SSH server I made a # test connection to. groupgen = 5 @@ -37,7 +39,7 @@ rsamod = 0xB98FE0C0BEE1E05B35FDDF5517B3E29D8A9A6A7834378B6783A19536968968F755E341B5822CAE15B465DECB80EE4116CF8D22DB5A6C85444A68D0D45D9D42008329619BE3CAC3B192EF83DD2B75C4BB6B567E11B841073BACE92108DA7E97E543ED7F032F454F7AC3C6D3F27DB34BC9974A85C7963C546662AE300A61CBABEE274481FD041C41D0145704F5FA9C77A5A442CD7A64827BB0F21FB56FDE388B596A20D7A7D1C5F22DA96C6C2171D90A673DABC66596CD99499D75AD82FEFDBE04DEC2CC7E1414A95388F668591B3F4D58249F80489646ED2C318E77D4F4E37EE8A588E79F2960620E6D28BF53653F1C974C91845F0BABFE5D167E1CA7044EE20D # 16 bytes of random data for the start of KEXINIT. -cookie = "".join([chr(random.randint(0,255)) for i in range(16)]) +cookie = bytes(random.randint(0,255) for i in range(16)) def expect(var, expr): expected_val = eval(expr) @@ -46,12 +48,12 @@ def expect(var, expr): expr, repr(expected_val), repr(var))) sys.exit(1) -sys.stdout.write(greeting("SSH-2.0-Example KEX synthesis")) +sys.stdout.buffer.write(greeting("SSH-2.0-Example KEX synthesis")) -greeting = sys.stdin.readline() -expect(greeting[:8], '"SSH-2.0-"') +greeting = sys.stdin.buffer.readline() +expect(greeting[:8].decode("ASCII"), '"SSH-2.0-"') -sys.stdout.write( +sys.stdout.buffer.write( clearpkt(SSH2_MSG_KEXINIT, cookie, name_list(("diffie-hellman-group-exchange-sha256",)), # kex @@ -66,27 +68,27 @@ def expect(var, expr): name_list(()), # server->client languages boolean(False), # first kex packet does not follow uint32(0))) -sys.stdout.flush() +sys.stdout.buffer.flush() -intype, inpkt = read_clearpkt(sys.stdin) +intype, inpkt = read_clearpkt(sys.stdin.buffer) expect(intype, "SSH2_MSG_KEXINIT") -intype, inpkt = read_clearpkt(sys.stdin) +intype, inpkt = read_clearpkt(sys.stdin.buffer) expect(intype, "SSH2_MSG_KEX_DH_GEX_REQUEST") expect(inpkt, "uint32(0x400) + uint32(0x400) + uint32(0x2000)") -sys.stdout.write( +sys.stdout.buffer.write( clearpkt(SSH2_MSG_KEX_DH_GEX_GROUP, mpint(group), mpint(groupgen))) -sys.stdout.flush() +sys.stdout.buffer.flush() -intype, inpkt = read_clearpkt(sys.stdin) +intype, inpkt = read_clearpkt(sys.stdin.buffer) expect(intype, "SSH2_MSG_KEX_DH_GEX_INIT") -sys.stdout.write( +sys.stdout.buffer.write( clearpkt(SSH2_MSG_KEX_DH_GEX_REPLY, ssh_rsa_key_blob(rsaexp, rsamod), mpint(random.randint(2, group-2)), ssh_rsa_signature_blob(random.randint(2, rsamod-2)))) -sys.stdout.flush() +sys.stdout.buffer.flush() diff --git a/defs.h b/defs.h index afeb506..3ba43c4 100644 --- a/defs.h +++ b/defs.h @@ -13,20 +13,49 @@ #include #include +#include /* for __MINGW_PRINTF_FORMAT */ #include #if defined _MSC_VER && _MSC_VER < 1800 /* Work around lack of inttypes.h and strtoumax in older MSVC */ #define PRIx32 "x" +#define PRIu32 "u" #define PRIu64 "I64u" #define PRIdMAX "I64d" #define PRIXMAX "I64X" #define SCNu64 "I64u" +#define SIZEx "Ix" +#define SIZEu "Iu" uintmax_t strtoumax(const char *nptr, char **endptr, int base); #else #include +/* Because we still support older MSVC libraries which don't recognise the + * standard C "z" modifier for size_t-sized integers, we must use an + * inttypes.h-style macro for those */ +#define SIZEx "zx" +#define SIZEu "zu" #endif +#if defined __GNUC__ || defined __clang__ +/* + * On MinGW, the correct compiler format checking for vsnprintf() etc + * can depend on compile-time flags; these control whether you get + * ISO C or Microsoft's non-standard format strings. + * We sometimes use __attribute__ ((format)) for our own printf-like + * functions, which are ultimately interpreted by the toolchain-chosen + * printf, so we need to take that into account to get correct warnings. + */ +#ifdef __MINGW_PRINTF_FORMAT +#define PRINTF_LIKE(fmt_index, ellipsis_index) \ + __attribute__ ((format (__MINGW_PRINTF_FORMAT, fmt_index, ellipsis_index))) +#else +#define PRINTF_LIKE(fmt_index, ellipsis_index) \ + __attribute__ ((format (printf, fmt_index, ellipsis_index))) +#endif +#else /* __GNUC__ */ +#define PRINTF_LIKE(fmt_index, ellipsis_index) +#endif /* __GNUC__ */ + typedef struct conf_tag Conf; typedef struct terminal_tag Terminal; typedef struct term_utf8_decode term_utf8_decode; @@ -37,6 +66,7 @@ typedef struct FontSpec FontSpec; typedef struct bufchain_tag bufchain; typedef struct strbuf strbuf; +typedef struct LoadedFile LoadedFile; typedef struct RSAKey RSAKey; @@ -154,6 +184,8 @@ typedef struct PacketProtocolLayer PacketProtocolLayer; #if defined __GNUC__ || defined __clang__ #define NORETURN __attribute__((__noreturn__)) +#elif defined _MSC_VER +#define NORETURN __declspec(noreturn) #else #define NORETURN #endif diff --git a/depcomp b/depcomp index b39f98f..65cbf70 100755 --- a/depcomp +++ b/depcomp @@ -1,9 +1,9 @@ #! /bin/sh # depcomp - compile a program generating dependencies as side-effects -scriptversion=2016-01-11.22; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1999-2017 Free Software Foundation, Inc. +# Copyright (C) 1999-2018 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -16,7 +16,7 @@ scriptversion=2016-01-11.22; # UTC # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -783,7 +783,7 @@ exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" diff --git a/dialog.c b/dialog.c index f8004ee..7409daa 100644 --- a/dialog.c +++ b/dialog.c @@ -221,6 +221,7 @@ static union control *ctrl_new(struct controlset *s, int type, c->generic.handler = handler; c->generic.context = context; c->generic.label = NULL; + c->generic.align_next_to = NULL; return c; } diff --git a/dialog.h b/dialog.h index e1887ef..86ebfc2 100644 --- a/dialog.h +++ b/dialog.h @@ -113,7 +113,8 @@ typedef void (*handler_fn)(union control *ctrl, dlgparam *dp, int column; \ handler_fn handler; \ intorptr context; \ - intorptr helpctx + intorptr helpctx; \ + union control *align_next_to union control { /* @@ -179,6 +180,18 @@ union control { * to ensure it brings up the right piece of help text. */ intorptr helpctx; + /* + * Setting this to non-NULL coerces two controls to have their + * y-coordinates adjusted so that they can sit alongside each + * other and look nicely aligned, even if they're different + * heights. + * + * Set this field on the _second_ control of the pair (in + * terms of order in the data structure), so that when it's + * instantiated, the first one is already there to be referred + * to. + */ + union control *align_next_to; } generic; struct { STANDARD_PREFIX; diff --git a/doc/AppendixA.html b/doc/AppendixA.html index b19a12e..094d119 100644 --- a/doc/AppendixA.html +++ b/doc/AppendixA.html @@ -106,22 +106,23 @@
  • A.9 Administrative questions
  • A.10 Miscellaneous questions
      @@ -139,7 +140,7 @@

      Appendix A: PuTTY FAQA.1 Introduction

      A.1.1 What is PuTTY?

      -PuTTY is a client program for the SSH, Telnet and Rlogin network protocols. +PuTTY is a client program for the SSH, Telnet, Rlogin, and SUPDUP network protocols.

      These protocols are all used to run a remote session on a computer, over a network. PuTTY implements the client end of that session: the end at which the session is displayed, rather than the end at which it runs. @@ -192,7 +193,7 @@

      A.2.5 Does PuTTY suppor

      A.2.6 Does PuTTY support storing its settings in a disk file?

      -Not at present, although section 4.30 in the documentation gives a method of achieving the same effect. +Not at present, although section 4.32 in the documentation gives a method of achieving the same effect.

      A.2.7 Does PuTTY support full-screen mode, like a DOS box?

      @@ -533,7 +534,7 @@

      A.6.10 Should I run the The 64-bit version (first released in 0.68) will only run if you have a 64-bit processor and a 64-bit edition of Windows (both of these things are likely to be true of any recent Windows PC). It will run somewhat faster (in particular, the cryptography will be faster, especially during link setup), but it will consume slightly more memory.

      -If you need to use an external DLL for GSSAPI authentication, that DLL may only be available in a 32-bit or 64-bit form, and that will dictate the version of PuTTY you need to use. (You will probably know if you're doing this; see section 4.24.2 in the documentation.) +If you need to use an external DLL for GSSAPI authentication, that DLL may only be available in a 32-bit or 64-bit form, and that will dictate the version of PuTTY you need to use. (You will probably know if you're doing this; see section 4.22.2 in the documentation.)

      A.7 Troubleshooting

      A.7.1 Why do I see ‘Fatal: Protocol error: Expected control record’ in PSCP?

      @@ -733,7 +734,7 @@

      A.7.20 My SSH-2 session repeat key exchange once per hour, to improve session security. If your client or server machine is slow, you may experience this as a delay of anything up to thirty seconds or so.

      -These delays are inconvenient, but they are there for your protection. If they really cause you a problem, you can choose to turn off periodic rekeying using the ‘Kex’ configuration panel (see section 4.20), but be aware that you will be sacrificing security for this. (Falling back to SSH-1 would also remove the delays, but would lose a lot more security still. We do not recommend it.) +These delays are inconvenient, but they are there for your protection. If they really cause you a problem, you can choose to turn off periodic rekeying using the ‘Kex’ configuration panel (see section 4.18), but be aware that you will be sacrificing security for this. (Falling back to SSH-1 would also remove the delays, but would lose a lot more security still. We do not recommend it.)

      A.7.21 PuTTY fails to start up. Windows claims that ‘the application configuration is incorrect’.

      @@ -751,7 +752,7 @@

      A.7.23 After I upgraded PuTTY If your SSH server has started unexpectedly closing SSH connections after you enter your password, and it worked before 0.68, you may have a buggy server that objects to certain SSH protocol extensions.

      -The SSH protocol recently gained a new ‘terminal mode’, IUTF8, which PuTTY sends by default; see section 4.25.2. This is the first new terminal mode since the SSH-2 protocol was defined. While servers are supposed to ignore modes they don't know about, some buggy servers will unceremoniously close the connection if they see anything they don't recognise. SSH servers in embedded devices, network appliances, and the like seem to disproportionately have this bug. +The SSH protocol recently gained a new ‘terminal mode’, IUTF8, which PuTTY sends by default; see section 4.23.2. This is the first new terminal mode since the SSH-2 protocol was defined. While servers are supposed to ignore modes they don't know about, some buggy servers will unceremoniously close the connection if they see anything they don't recognise. SSH servers in embedded devices, network appliances, and the like seem to disproportionately have this bug.

      If you think you have such a server, from 0.69 onwards you can disable sending of the IUTF8 mode: on the SSH / TTY panel, select IUTF8 on the list, select ‘Nothing’, and press ‘Set’. (It's not possible to disable sending this mode in 0.68.) @@ -769,7 +770,7 @@

      A.8.2 What does PuTTY leave o PuTTY will leave some Registry entries, and a random seed file, on the PC (see question A.5.2). Windows 7 and up also remember some information about recently launched sessions for the ‘jump list’ feature.

      -If you are using PuTTY on a public PC, or somebody else's PC, you might want to clean this information up when you leave. You can do that automatically, by running the command putty -cleanup. See section 3.8.2 in the documentation for more detail. (Note that this only removes settings for the currently logged-in user on multi-user systems.) +If you are using PuTTY on a public PC, or somebody else's PC, you might want to clean this information up when you leave. You can do that automatically, by running the command putty -cleanup. See section 3.11.2 in the documentation for more detail. (Note that this only removes settings for the currently logged-in user on multi-user systems.)

      If PuTTY was installed from the installer package, it will also appear in ‘Add/Remove Programs’. Current versions of the installer do not offer to remove the above-mentioned items, so if you want them removed you should run putty -cleanup before uninstalling. @@ -789,18 +790,25 @@

      A.8.4 Couldn't Pageant us Unfortunately not. The VirtualLock() function in the Windows API doesn't do a proper job: it may prevent small pieces of a process's memory from being paged to disk while the process is running, but it doesn't stop the process's memory as a whole from being swapped completely out to disk when the process is long-term inactive. And Pageant spends most of its time inactive.

      A.9 Administrative questions

      -

      A.9.1 Would you like me to register you a nicer domain name?

      +

      A.9.1 Is putty.org your website?

      +

      +No, it isn't. putty.org is run by an opportunist who uses it to advertise their own commercial SSH implementation to people looking for our free one. We don't own that site, we can't control it, and we don't advise anyone to use it in preference to our own site. +

      +

      +The real PuTTY web site, run by the PuTTY team, has always been at https://www.chiark.greenend.org.uk/~sgtatham/putty/. +

      +

      A.9.2 Would you like me to register you a nicer domain name?

      No, thank you. Even if you can find one (most of them seem to have been registered already, by people who didn't ask whether we actually wanted it before they applied), we're happy with the PuTTY web site being exactly where it is. It's not hard to find (just type ‘putty’ into google.com and we're the first link returned), and we don't believe the administrative hassle of moving the site would be worth the benefit.

      In addition, if we did want a custom domain name, we would want to run it ourselves, so we knew for certain that it would continue to point where we wanted it, and wouldn't suddenly change or do strange things. Having it registered for us by a third party who we don't even know is not the best way to achieve this.

      -

      A.9.2 Would you like free web hosting for the PuTTY web site?

      +

      A.9.3 Would you like free web hosting for the PuTTY web site?

      We already have some, thanks.

      -

      A.9.3 Would you link to my web site from the PuTTY web site?

      +

      A.9.4 Would you link to my web site from the PuTTY web site?

      Only if the content of your web page is of definite direct interest to PuTTY users. If your content is unrelated, or only tangentially related, to PuTTY, then the link would simply be advertising for you.

      @@ -816,9 +824,9 @@

      A.9.3 Would you link to my web s

      If you have software based on PuTTY, or specifically designed to interoperate with PuTTY, or in some other way of genuine interest to PuTTY users, then we will probably be happy to add a link to you on our Links page. And if you're running a particularly valuable mirror of the PuTTY web site, we might be interested in linking to you from our Mirrors page.

      -

      A.9.4 Why don't you move PuTTY to SourceForge?

      +

      A.9.5 Why don't you move PuTTY to SourceForge?

      -Partly, because we don't want to move the web site location (see question A.9.1). +Partly, because we don't want to move the web site location (see question A.9.2).

      Also, security reasons. PuTTY is a security product, and as such it is particularly important to guard the code and the web site against unauthorised modifications which might introduce subtle security flaws. Therefore, we prefer that the Git repository, web site and FTP site remain where they are, under the direct control of system administrators we know and trust personally, rather than being run by a large organisation full of people we've never met and which is known to have had breakins in the past. @@ -826,18 +834,18 @@

      A.9.4 Why don't you move

      No offence to SourceForge; I think they do a wonderful job. But they're not ideal for everyone, and in particular they're not ideal for us.

      -

      A.9.5 Why can't I subscribe to the putty-bugs mailing list?

      +

      A.9.6 Why can't I subscribe to the putty-bugs mailing list?

      Because you're not a member of the PuTTY core development team. The putty-bugs mailing list is not a general newsgroup-like discussion forum; it's a contact address for the core developers, and an internal mailing list for us to discuss things among ourselves. If we opened it up for everybody to subscribe to, it would turn into something more like a newsgroup and we would be completely overwhelmed by the volume of traffic. It's hard enough to keep up with the list as it is.

      -

      A.9.6 If putty-bugs isn't a general-subscription mailing list, what is?

      +

      A.9.7 If putty-bugs isn't a general-subscription mailing list, what is?

      There isn't one, that we know of.

      If someone else wants to set up a mailing list or other forum for PuTTY users to help each other with common problems, that would be fine with us, though the PuTTY team would almost certainly not have the time to read it. It's probably better to use one of the established newsgroups for this purpose (see section B.1.2).

      -

      A.9.7 How can I donate to PuTTY development?

      +

      A.9.8 How can I donate to PuTTY development?

      Please, please don't feel you have to. PuTTY is completely free software, and not shareware. We think it's very important that everybody who wants to use PuTTY should be able to, whether they have any money or not; so the last thing we would want is for a PuTTY user to feel guilty because they haven't paid us any money. If you want to keep your money, please do keep it. We wouldn't dream of asking for any.

      @@ -847,14 +855,14 @@

      A.9.7 How can I donate to P

      Small donations (tens of dollars or tens of euros) will probably be spent on beer or curry, which helps motivate our volunteer team to continue doing this for the world. Larger donations will be spent on something that actually helps development, if we can find anything (perhaps new hardware, or a copy of Windows XP), but if we can't find anything then we'll just distribute the money among the developers. If you want to be sure your donation is going towards something worthwhile, ask us first. If you don't like these terms, feel perfectly free not to donate. We don't mind.

      -

      A.9.8 Can I have permission to put PuTTY on a cover disk / distribute it with other software / etc?

      +

      A.9.9 Can I have permission to put PuTTY on a cover disk / distribute it with other software / etc?

      Yes. For most things, you need not bother asking us explicitly for permission; our licence already grants you permission.

      See section B.8 for more details.

      -

      A.9.9 Can you sign an agreement indemnifying us against security problems in PuTTY?

      +

      A.9.10 Can you sign an agreement indemnifying us against security problems in PuTTY?

      No!

      @@ -876,24 +884,24 @@

      A.9.9 Can you sign an agree

      If you really want financial security, see if you can find a security engineer who will take financial responsibility for the correctness of their review. (This might be less likely to suffer from the everything-failing-at-once problem mentioned above, because such an engineer would probably be reviewing a lot of different products which would tend to fail independently.) Failing that, see if you can persuade an insurance company to insure you against security incidents, and if the insurer demands it as a condition then get our code reviewed by a security engineer they're happy with.

      -

      A.9.10 Can you sign this form granting us permission to use/distribute PuTTY?

      +

      A.9.11 Can you sign this form granting us permission to use/distribute PuTTY?

      -If your form contains any clause along the lines of ‘the undersigned represents and warrants’, we're not going to sign it. This is particularly true if it asks us to warrant that PuTTY is secure; see question A.9.9 for more discussion of this. But it doesn't really matter what we're supposed to be warranting: even if it's something we already believe is true, such as that we don't infringe any third-party copyright, we will not sign a document accepting any legal or financial liability. This is simply because the PuTTY development project has no income out of which to satisfy that liability, or pay legal costs, should it become necessary. We cannot afford to be sued. We are assuring you that we have done our best; if that isn't good enough for you, tough. +If your form contains any clause along the lines of ‘the undersigned represents and warrants’, we're not going to sign it. This is particularly true if it asks us to warrant that PuTTY is secure; see question A.9.10 for more discussion of this. But it doesn't really matter what we're supposed to be warranting: even if it's something we already believe is true, such as that we don't infringe any third-party copyright, we will not sign a document accepting any legal or financial liability. This is simply because the PuTTY development project has no income out of which to satisfy that liability, or pay legal costs, should it become necessary. We cannot afford to be sued. We are assuring you that we have done our best; if that isn't good enough for you, tough.

      The existing PuTTY licence document already gives you permission to use or distribute PuTTY in pretty much any way which does not involve pretending you wrote it or suing us if it goes wrong. We think that really ought to be enough for anybody.

      -See also question A.9.12 for another reason why we don't want to do this sort of thing. +See also question A.9.13 for another reason why we don't want to do this sort of thing.

      -

      A.9.11 Can you write us a formal notice of permission to use PuTTY?

      +

      A.9.12 Can you write us a formal notice of permission to use PuTTY?

      We could, in principle, but it isn't clear what use it would be. If you think there's a serious chance of one of the PuTTY copyright holders suing you (which we don't!), you would presumably want a signed notice from all of them; and we couldn't provide that even if we wanted to, because many of the copyright holders are people who contributed some code in the past and with whom we subsequently lost contact. Therefore the best we would be able to do even in theory would be to have the core development team sign the document, which wouldn't guarantee you that some other copyright holder might not sue.

      -See also question A.9.12 for another reason why we don't want to do this sort of thing. +See also question A.9.13 for another reason why we don't want to do this sort of thing.

      -

      A.9.12 Can you sign anything for us?

      +

      A.9.13 Can you sign anything for us?

      Not unless there's an incredibly good reason.

      @@ -903,7 +911,7 @@

      A.9.12 Can you si

      If your company policy requires you to have an individual agreement with the supplier of any software you use, then your company policy is simply not well suited to using popular free software, and we urge you to consider this as a flaw in your policy.

      -

      A.9.13 If you won't sign anything, can you give us some sort of assurance that you won't make PuTTY closed-source in future?

      +

      A.9.14 If you won't sign anything, can you give us some sort of assurance that you won't make PuTTY closed-source in future?

      Yes and no.

      @@ -919,14 +927,14 @@

      A.9.13 If you w

      (Finally, we can also confidently predict that if we made PuTTY closed-source and someone made an open-source fork, most people would switch to the latter. Therefore, it would be pretty stupid of us to try it.)

      -

      A.9.14 Can you provide us with export control information / FIPS certification for PuTTY?

      +

      A.9.15 Can you provide us with export control information / FIPS certification for PuTTY?

      Some people have asked us for an Export Control Classification Number (ECCN) for PuTTY. We don't know whether we have one, and as a team of free software developers based in the UK we don't have the time, money, or effort to deal with US bureaucracy to investigate any further. We believe that PuTTY falls under 5D002 on the US Commerce Control List, but that shouldn't be taken as definitive. If you need to know more you should seek professional legal advice. The same applies to any other country's legal requirements and restrictions.

      Similarly, some people have asked us for FIPS certification of the PuTTY tools. Unless someone else is prepared to do the necessary work and pay any costs, we can't provide this.

      -

      A.9.15 As one of our existing software vendors, can you just fill in this questionnaire for us?

      +

      A.9.16 As one of our existing software vendors, can you just fill in this questionnaire for us?

      We periodically receive requests like this, from organisations which have apparently sent out a form letter to everyone listed in their big spreadsheet of ‘software vendors’ requiring them all to answer some long list of questions about supported OS versions, paid support arrangements, compliance with assorted local regulations we haven't heard of, contact phone numbers, and other such administrivia. Many of the questions are obviously meaningless when applied to PuTTY (we don't provide any paid support in the first place!), most of the rest could have been answered with only a very quick look at our website, and some we are actively unwilling to answer (we are private individuals, why would we want to give out our home phone numbers to large corporations?).

      @@ -945,7 +953,7 @@

      A.9.15 As one of our existing

      If you work for an organisation which you think might be at risk of making this mistake, we urge you to reorganise your list of software suppliers so that it clearly distinguishes paid vendors who know about you from free software developers who don't have any idea who you are. Then, only send out these mass mailings to the former.

      -

      A.9.16 The sha1sums / sha256sums / etc files on your download page don't match the binaries.

      +

      A.9.17 The sha1sums / sha256sums / etc files on your download page don't match the binaries.

      People report this every so often, and usually the reason turns out to be that they've matched up the wrong checksums file with the wrong binaries.

      @@ -996,5 +1004,5 @@

      A.10.4 How do I pronounce


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/AppendixB.html b/doc/AppendixB.html index a43b5f4..1590839 100644 --- a/doc/AppendixB.html +++ b/doc/AppendixB.html @@ -61,7 +61,7 @@

      B.1.1 Sending large a If you are considering sending any kind of large data file to the PuTTY team, it's almost always a bad idea, or at the very least it would be better to ask us first whether we actually need the file. Alternatively, you could put the file on a web site and just send us the URL; that way, we don't have to download it unless we decide we actually need it, and only one of us needs to download it instead of it being automatically copied to all the developers.

      -(If the file contains confidential information, then you could encrypt it with our Secure Contact Key; see section E.1 for details.) +(If the file contains confidential information, then you could encrypt it with our Secure Contact Key; see section F.1 for details. Please only use this for information that needs to be confidential.)

      Some people like to send mail in MS Word format. Please don't send us bug reports, or any other mail, as a Word document. Word documents are roughly fifty times larger than writing the same report in plain text. In addition, most of the PuTTY team read their e-mail on Unix machines, so copying the file to a Windows box to run Word is very inconvenient. Not only that, but several of us don't even have a copy of Word! @@ -122,7 +122,7 @@

      B.2 Reporting bugs

      PuTTY is a multi-platform application; tell us what version of what OS you are running PuTTY on. (If you're running on Unix, or Windows for Arm, tell us, or we'll assume you're running on Windows for Intel as this is overwhelmingly the case.)
    • -Tell us what protocol you are connecting with: SSH, Telnet, Rlogin, or Raw mode, or a serial connection. +Tell us what protocol you are connecting with: SSH, Telnet, Rlogin, SUPDUP, or Raw mode, or a serial connection.
    • Tell us what kind of server you are connecting to; what OS, and if possible what SSH server (if you're using SSH). You can get some of this information from the PuTTY Event Log (see section 3.1.3.1 in the manual). @@ -151,7 +151,7 @@

      B.3 Reporting security vulne If you've found a security vulnerability in PuTTY, you might well want to notify us using an encrypted communications channel, to avoid disclosing information about the vulnerability before a fixed release is available.

      -For this purpose, we provide a GPG key suitable for encryption: the Secure Contact Key. See section E.1 for details of this. +For this purpose, we provide a GPG key suitable for encryption: the Secure Contact Key. See section F.1 for details of this.

      (Of course, vulnerabilities are also bugs, so please do include as much information as possible about them, the same way you would with any other bug report.) @@ -174,7 +174,7 @@

      B.4 Requesting extra feat Do as much of the design as you can. Think about ‘corner cases’; think about how your feature interacts with other existing features. Think about the user interface; if you can't come up with a simple and intuitive interface to your feature, you shouldn't be surprised if we can't either. Always imagine whether it's possible for there to be more than one, or less than one, of something you'd assumed there would be one of. (For example, if you were to want PuTTY to put an icon in the System tray rather than the Taskbar, you should think about what happens if there's more than one PuTTY active; how would the user tell which was which?)

    • -If you can program, it may be worth offering to write the feature yourself and send us a patch. However, it is likely to be helpful if you confer with us first; there may be design issues you haven't thought of, or we may be about to make big changes to the code which your patch would clash with, or something. If you check with the maintainers first, there is a better chance of your code actually being usable. Also, read the design principles listed in appendix D: if you do not conform to them, we will probably not be able to accept your patch. +If you can program, it may be worth offering to write the feature yourself and send us a patch. However, it is likely to be helpful if you confer with us first; there may be design issues you haven't thought of, or we may be about to make big changes to the code which your patch would clash with, or something. If you check with the maintainers first, there is a better chance of your code actually being usable. Also, read the design principles listed in appendix E: if you do not conform to them, we will probably not be able to accept your patch.

    B.5 Requesting features that have already been requested

    @@ -223,7 +223,7 @@

    B.7 Web server administra

    B.8 Asking permission for things

    -PuTTY is distributed under the MIT Licence (see appendix C for details). This means you can do almost anything you like with our software, our source code, and our documentation. The only things you aren't allowed to do are to remove our copyright notices or the licence text itself, or to hold us legally responsible if something goes wrong. +PuTTY is distributed under the MIT Licence (see appendix D for details). This means you can do almost anything you like with our software, our source code, and our documentation. The only things you aren't allowed to do are to remove our copyright notices or the licence text itself, or to hold us legally responsible if something goes wrong.

    So if you want permission to include PuTTY on a magazine cover disk, or as part of a collection of useful software on a CD or a web site, then permission is already granted. You don't have to mail us and ask. Just go ahead and do it. We don't mind. @@ -269,5 +269,5 @@

    B.11 E-mail address


    If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

    -[PuTTY release 0.73]
    +[PuTTY release 0.76] diff --git a/doc/AppendixC.html b/doc/AppendixC.html index e8f6fc5..aed2e40 100644 --- a/doc/AppendixC.html +++ b/doc/AppendixC.html @@ -3,7 +3,7 @@ -PuTTY Licence +PPK file format @@ -12,23 +12,343 @@

    Previous | Contents | Index | Next

    -

    Appendix C: PuTTY Licence

    + + +

    Appendix C: PPK file format

    +

    +This appendix documents the file format used by PuTTY to store private keys. +

    +

    +In this appendix, binary data structures are described using data type representations such as ‘uint32’, ‘string’ and ‘mpint’ as used in the SSH protocol standards themselves. These are defined authoritatively by RFC 4251 section 5, ‘Data Type Representations Used in the SSH Protocols’. +

    +

    C.1 Overview

    +

    +A PPK file stores a private key, and the corresponding public key. Both are contained in the same file. +

    +

    +The file format can be completely unencrypted, or it can encrypt the private key. The public key is stored in cleartext in both cases. (This enables PuTTY to send the public key to an SSH server to see whether it will accept it, and not bother prompting for the passphrase unless the server says yes.) +

    +

    +When the key file is encrypted, the encryption key is derived from a passphrase. An encrypted PPK file is also tamper-proofed using a MAC (authentication code), also derived from the same passphrase. The MAC protects the encrypted private key data, but it also covers the cleartext parts of the file. So you can't edit the public half of the key without invalidating the MAC and causing the key file as a whole to become useless. +

    +

    +This MAC protects the key file against active cryptographic attacks in which the public half of a key pair is modified in a controlled way that allows an attacker to deduce information about the private half from the resulting corrupted signatures. Any attempt to do that to a PPK file should be reliably caught by the MAC failing to validate. +

    +

    +(Such an attack would only be useful if the key file was stored in a location where the attacker could modify it without also having full access to the process that you type passphrases into. But that's not impossible; for example, if your home directory was on a network file server, then the file server's administrator could access the key file but not processes on the client machine.) +

    +

    +The MAC also covers the comment on the key. This stops an attacker from swapping keys with each other and editing the comments to disguise the fact. As a consequence, PuTTYgen cannot edit the comment on a key unless you decrypt the key with your passphrase first. +

    +

    +(The circumstances in which that attack would be useful are even more restricted. One example might be that the different keys trigger specific actions on the server you're connecting to and one of those actions is more useful to the attacker than the other. But once you have a MAC at all, it's no extra effort to make it cover as much as possible, and usually sensible.) +

    +

    C.2 Outer layer

    +

    +The outer layer of a PPK file is text-based. The PuTTY tools will always use LF line termination when writing PPK files, but will tolerate CR+LF and CR-only on input. +

    +

    +The first few lines identify it as a PPK, and give some initial data about what's stored in it and how. They look like this: +

    +
    PuTTY-User-Key-File-version: algorithm-name
    +Encryption: encryption-type
    +Comment: key-comment-string
    +
    +

    +version is a decimal number giving the version number of the file format itself. The current file format version is 3. +

    +

    +algorithm-name is the SSH protocol identifier for the public key algorithm that this key is used for (such as ‘ssh-dss’ or ‘ecdsa-sha2-nistp384’). +

    +

    +encryption-type indicates whether this key is stored encrypted, and if so, by what method. Currently the only supported encryption types are ‘aes256-cbc’ and ‘none’. +

    -PuTTY is copyright 1997-2019 Simon Tatham. +key-comment-string is a free text field giving the comment. This can contain any byte values other than 13 and 10 (CR and LF).

    -Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, and CORE SDI S.A. +The next part of the file gives the public key. This is stored unencrypted but base64-encoded (RFC 4648), and is preceded by a header line saying how many lines of base64 data are shown, looking like this:

    +
    Public-Lines: number-of-lines
    +that many lines of base64 data
    +

    -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The base64-encoded data in this blob is formatted in exactly the same way as an SSH public key sent over the wire in the SSH protocol itself. That is also the same format as the base64 data stored in OpenSSH's authorized_keys file, except that in a PPK file the base64 data is split across multiple lines. But if you remove the newlines from the middle of this section, the resulting base64 blob is in the right format to go in an authorized_keys line.

    -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +If the key is encrypted (i.e. encryption-type is not ‘none’), then the next thing that appears is a sequence of lines specifying how the keys for encrypting the file are to be derived from the passphrase: +

    +
    Key-Derivation: argon2-flavour
    +Argon2-Memory: decimal-integer
    +Argon2-Passes: decimal-integer
    +Argon2-Parallelism: decimal-integer
    +Argon2-Salt: hex-string
    +
    +

    +argon2-flavour is one of the identifiers ‘Argon2d’, ‘Argon2i’ or ‘Argon2id’, all describing variants of the Argon2 password-hashing function. +

    +

    +The three integer values are used as parameters for Argon2, which allows you to configure the amount of memory used (in Kbyte), the number of passes of the algorithm to run (to tune its running time), and the degree of parallelism required by the hash function. The salt is decoded into a sequence of binary bytes and used as an additional input to Argon2. (It is chosen randomly when the key file is written, so that a guessing attack can't be mounted in parallel against multiple key files.) +

    +

    +The next part of the file gives the private key. This is base64-encoded in the same way: +

    +
    Private-Lines: number-of-lines
    +that many lines of base64 data
    +
    +

    +The binary data represented in this base64 blob may be encrypted, depending on the encryption-type field in the key file header shown above: +

    +
    • +If encryption-type is ‘none’, then this data is stored in plain text. +
    • +
    • +If encryption-type is ‘aes256-cbc’, then this data is encrypted using AES, with a 256-bit key length, in the CBC cipher mode. The key and initialisation vector are derived from the passphrase: see section C.4. +

      +In order to encrypt the private key data with AES, it must be a multiple of 16 bytes (the AES cipher block length). This is achieved by appending random padding to the data before encrypting it. When decoding it after decryption, the random data can be ignored: the internal structure of the data is enough to tell you when you've reached the end of the meaningful part. +

      + +
    • +
    +

    +Unlike public keys, the binary encoding of private keys is not specified at all in the SSH standards. See section C.3 for details of the private key format for each key type supported by PuTTY. +

    +

    +The final thing in the key file is the MAC: +

    +
    Private-MAC: hex-mac-data
    +
    +

    +hex-mac-data is a hexadecimal-encoded value, 64 digits long (i.e. 32 bytes), generated using the HMAC-SHA-256 algorithm with the following binary data as input: +

    +
    • +string: the algorithm-name header field. +
    • +
    • +string: the encryption-type header field. +
    • +
    • +string: the key-comment-string header field. +
    • +
    • +string: the binary public key data, as decoded from the base64 lines after the ‘Public-Lines’ header. +
    • +
    • +string: the plaintext of the binary private key data, as decoded from the base64 lines after the ‘Private-Lines’ header. If that data was stored encrypted, then the decrypted version of it is used in this MAC preimage, including the random padding mentioned above. +
    • +
    +

    +The MAC key is derived from the passphrase: see section C.4. +

    +

    C.3 Private key encodings

    +

    +This section describes the private key format for each key type supported by PuTTY. +

    +

    +Because the PPK format also contains the public key (and both public and private key are protected by the same MAC to ensure they can't be made inconsistent), there is no need for the private key section of the file to repeat data from the public section. So some of these formats are very short. +

    +

    +In all cases, a decoding application can begin reading from the start of the decrypted private key data, and know when it has read all that it needs. This allows random padding after the meaningful data to be safely ignored. +

    +

    C.3.1 RSA

    +

    +RSA keys are stored using an algorithm-name of ‘ssh-rsa’. (Keys stored like this are also used by the updated RSA signature schemes that use hashes other than SHA-1.) +

    +

    +The public key data has already provided the key modulus and the public encoding exponent. The private data stores: +

    +
    • +mpint: the private decoding exponent of the key. +
    • +
    • +mpint: one prime factor p of the key. +
    • +
    • +mpint: the other prime factor q of the key. (RSA keys stored in this format are expected to have exactly two prime factors.) +
    • +
    • +mpint: the multiplicative inverse of q modulo p. +
    • +
    +

    C.3.2 DSA

    +

    +DSA keys are stored using an algorithm-name of ‘ssh-dss’. +

    +

    +The public key data has already provided the key parameters (the large prime p, the small prime q and the group generator g), and the public key y. The private key stores: +

    +
    • +mpint: the private key x, which is the discrete logarithm of y in the group generated by g mod p. +
    • +
    +

    C.3.3 NIST elliptic-curve keys

    +

    +NIST elliptic-curve keys are stored using one of the following algorithm-name values, each corresponding to a different elliptic curve and key size: +

    +
    • +‘ecdsa-sha2-nistp256’ +
    • +
    • +‘ecdsa-sha2-nistp384’ +
    • +
    • +‘ecdsa-sha2-nistp521’ +
    • +
    +

    +The public key data has already provided the public elliptic curve point. The private key stores: +

    +
    • +mpint: the private exponent, which is the discrete log of the public point. +
    • +
    +

    C.3.4 EdDSA elliptic-curve keys (Ed25519 and Ed448)

    +

    +EdDSA elliptic-curve keys are stored using one of the following algorithm-name values, each corresponding to a different elliptic curve and key size: +

    +
    • +‘ssh-ed25519’ +
    • +
    • +‘ssh-ed448’ +
    • +
    +

    +The public key data has already provided the public elliptic curve point. The private key stores: +

    +
    • +mpint: the private exponent, which is the discrete log of the public point. +
    • +
    +

    C.4 Key derivation

    +

    +When a key file is encrypted, there are three pieces of key material that need to be computed from the passphrase: +

    +
    • +the key for the symmetric cipher used to encrypt the private key +
    • +
    • +the initialisation vector for that cipher encryption +
    • +
    • +the key for the MAC. +
    • +
    +

    +If encryption-type is ‘aes256-cbc’, then the symmetric cipher key is 32 bytes long, and the initialisation vector is 16 bytes (one cipher block). The length of the MAC key is also chosen to be 32 bytes. +

    +

    +If encryption-type is ‘none’, then all three of these pieces of data have zero length. (The MAC is still generated and checked in the key file format, but it has a zero-length key.) +

    +

    +If the amount of key material required is not zero, then the passphrase is fed to the Argon2 key derivation function, in whichever mode is described in the ‘Key-Derivation’ header in the key file, with parameters derived from the various ‘Argon2-Parameter:’ headers. +

    +

    +(If the key is unencrypted, then all those headers are omitted, and Argon2 is not run at all.) +

    +

    +Argon2 takes two extra string inputs in addition to the passphrase and the salt: a secret key, and some ‘associated data’. In PPK's use of Argon2, these are both set to the empty string. +

    +

    +The ‘tag length’ parameter to Argon2 (i.e. the amount of data it is asked to output) is set to the sum of the lengths of all of the data items required, i.e. (cipher key length + IV length + MAC key length). The output data is interpreted as the concatenation of the cipher key, the IV and the MAC key, in that order. +

    +

    +So, for ‘aes256-cbc’, the tag length will be 32+16+32 = 80 bytes; of the 80 bytes of output data, the first 32 bytes are used as the 256-bit AES key, the next 16 as the CBC IV, and the final 32 bytes as the HMAC-SHA-256 key. +

    +

    C.5 Older versions of the PPK format

    +

    C.5.1 Version 2

    +

    +PPK version 2 was used by PuTTY 0.52 to 0.74 inclusive. +

    +

    +In PPK version 2, the MAC algorithm used was HMAC-SHA-1 (so the Private-MAC line contained only 40 hex digits). +

    +

    +The ‘Key-Derivation:’ header and all the ‘Argon2-Parameter:’ headers were absent. Instead of using Argon2, the key material for encrypting the private blob was derived from the passphrase in a totally different way, as follows. +

    +

    +The cipher key for ‘aes256-cbc’ was constructed by generating two SHA-1 hashes, concatenating them, and taking the first 32 bytes of the result. (So you'd get all 20 bytes of the first hash output, and the first 12 of the second). Each hash preimage was as follows: +

    +
    • +uint32: a sequence number. This is 0 in the first hash, and 1 in the second. (The idea was to extend this mechanism to further hashes by continuing to increment the sequence number, if future changes required even longer keys.) +
    • +
    • +the passphrase, without any prefix length field. +
    • +
    +

    +In PPK v2, the CBC initialisation vector was all zeroes. +

    +

    +The MAC key was 20 bytes long, and was a single SHA-1 hash of the following data: +

    +
    • +the fixed string ‘putty-private-key-file-mac-key’, without any prefix length field. +
    • +
    • +the passphrase, without any prefix length field. (If the key is stored unencrypted, the passphrase was taken to be the empty string for these purposes.) +
    • +
    +

    C.5.2 Version 1

    +

    +PPK version 1 was a badly designed format, only used during initial development, and not recommended for production use. +

    +

    +PPK version 1 was never used by a released version of PuTTY. It was only emitted by some early development snapshots between version 0.51 (which did not support SSH-2 public keys at all) and 0.52 (which already used version 2 of this file format). I hope there are no PPK v1 files in use anywhere. But just in case, the old badly designed format is documented here anyway. +

    +

    +In PPK version 1, the input to the MAC does not include any of the header fields or the public key. It is simply the private key data (still in plaintext and including random padding), all by itself (without a wrapping string). +

    +

    +PPK version 1 keys must therefore be rigorously validated after loading, to ensure that the public and private parts of the key were consistent with each other. +

    +

    +PPK version 1 only supported the RSA and DSA key types. For RSA, this validation can be done using only the provided data (since the private key blob contains enough information to reconstruct the public values anyway). But for DSA, that isn't quite enough. +

    +

    +Hence, PPK version 1 DSA keys extended the private data so that immediately after x was stored an extra value: +

    +
    • +string: a SHA-1 hash of the public key data, whose preimage consists of +
      • +string: the large prime p +
      • +
      • +string: the small prime q +
      • +
      • +string: the group generator g +
      • +
      + +
    • +
    +

    +The idea was that checking this hash would verify that the key parameters had not been tampered with, and then the loading application could directly verify that g^x = y.

    -THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +In an unencrypted version 1 key file, the MAC is replaced by a plain SHA-1 hash of the private key data. This is indicated by the ‘Private-MAC:’ header being replaced with ‘Private-Hash:’ instead.


    If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

    -[PuTTY release 0.73]
    +[PuTTY release 0.76] diff --git a/doc/AppendixD.html b/doc/AppendixD.html index b2c00cb..a742dd0 100644 --- a/doc/AppendixD.html +++ b/doc/AppendixD.html @@ -3,7 +3,7 @@ -PuTTY hacking guide +PuTTY Licence @@ -12,253 +12,23 @@

    Previous | Contents | Index | Next

    - - -

    Appendix D: PuTTY hacking guide

    -

    -This appendix lists a selection of the design principles applying to the PuTTY source code. If you are planning to send code contributions, you should read this first. -

    -

    D.1 Cross-OS portability

    -

    -Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac port is in progress; more ports may or may not happen at a later date. -

    -

    -Therefore, embedding Windows-specific code in core modules such as ssh.c is not acceptable. We went to great lengths to remove all the Windows-specific stuff from our core modules, and to shift it out into Windows-specific modules. Adding large amounts of Windows-specific stuff in parts of the code that should be portable is almost guaranteed to make us reject a contribution. -

    -

    -The PuTTY source base is divided into platform-specific modules and platform-generic modules. The Unix-specific modules are all in the unix subdirectory; the Windows-specific modules are in the windows subdirectory. -

    -

    -All the modules in the main source directory - notably all of the code for the various back ends - are platform-generic. We want to keep them that way. -

    -

    -This also means you should stick to the C semantics guaranteed by the C standard: try not to make assumptions about the precise size of basic types such as int and long int; don't use pointer casts to do endianness-dependent operations, and so on. -

    -

    -(Even within a platform front end you should still be careful of some of these portability issues. The Windows front end compiles on both 32- and 64-bit x86 and also Arm.) -

    -

    -Our current choice of C standards version is mostly C99. With a couple of exceptions, you can assume that C99 features are available (in particular <stdint.h>, <stdbool.h> and inline), but you shouldn't use things that are new in C11 (such as <uchar.h> or _Generic). -

    -

    -The exceptions to that rule are due to the need for Visual Studio compatibility: -

    -
    • -Don't use variable-length arrays. Visual Studio doesn't support them even now that it's adopted the rest of C99. We use -Wvla when building with gcc and clang, to make it easier to avoid accidentally breaking that rule. -
    • -
    • -For historical reasons, we still build with one older VS version which lacks <inttypes.h>. So that file is included centrally in defs.h, and has a set of workaround definitions for the PRIx64-type macros we use. If you need to use another one of those macros, you need to add a workaround definition in defs.h, and don't casually re-include <inttypes.h> anywhere else in the source file. -
    • -
    -

    -Here are a few portability assumptions that we do currently allow (because we'd already have to thoroughly vet the existing code if they ever needed to change, and it doesn't seem worth doing that unless we really have to): -

    -
    • -You can assume int is at least 32 bits wide. (We've never tried to port PuTTY to a platform with 16-bit int, and it doesn't look likely to be necessary in future.) -
    • -
    • -Similarly, you can assume char is exactly 8 bits. (Exceptions to that are even less likely to be relevant to us than short int.) -
    • -
    • -You can assume that using memset to write zero bytes over a whole structure will have the effect of setting all its pointer fields to NULL. (The standard itself guarantees this for integer fields, but not for pointers.) -
    • -
    • -You can assume that time_t has POSIX semantics, i.e. that it represents an integer number of non-leap seconds since 1970-01-01 00:00:00 UTC. (Times in this format are used in X authorisation, but we could work around that by carefully distinguishing local time_t from time values used in the wire protocol; but these semantics of time_t are also baked into the shared library API used by the GSSAPI authentication code, which would be much harder to change.) -
    • -
    • -You can assume that the execution character encoding is a superset of the printable characters of ASCII. (In particular, it's fine to do arithmetic on a char value representing a Latin alphabetic character, without bothering to allow for EBCDIC or other non-consecutive encodings of the alphabet.) -
    • -
    -

    -On the other hand, here are some particular things not to assume: -

    -
    • -Don't assume anything about the signedness of char. In particular, you must cast char values to unsigned char before passing them to any <ctype.h> function (because those expect a non-negative character value, or EOF). If you need a particular signedness, explicitly specify signed char or unsigned char, or use C99 int8_t or uint8_t. -
    • -
    • -From past experience with MacOS, we're still a bit nervous about '\n' and '\r' potentially having unusual meanings on a given platform. So it's fine to say \n in a string you're passing to printf, but in any context where those characters appear in a standardised wire protocol or a binary file format, they should be spelled '\012' and '\015' respectively. -
    • -
    -

    D.2 Multiple backends treated equally

    -

    -PuTTY is not an SSH client with some other stuff tacked on the side. PuTTY is a generic, multiple-backend, remote VT-terminal client which happens to support one backend which is larger, more popular and more useful than the rest. Any extra feature which can possibly be general across all backends should be so: localising features unnecessarily into the SSH back end is a design error. (For example, we had several code submissions for proxy support which worked by hacking ssh.c. Clearly this is completely wrong: the network.h abstraction is the place to put it, so that it will apply to all back ends equally, and indeed we eventually put it there after another contributor sent a better patch.) -

    -

    -The rest of PuTTY should try to avoid knowing anything about specific back ends if at all possible. To support a feature which is only available in one network protocol, for example, the back end interface should be extended in a general manner such that any back end which is able to provide that feature can do so. If it so happens that only one back end actually does, that's just the way it is, but it shouldn't be relied upon by any code. -

    -

    D.3 Multiple sessions per process on some platforms

    -

    -Some ports of PuTTY - notably the in-progress Mac port - are constrained by the operating system to run as a single process potentially managing multiple sessions. -

    -

    -Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular login session: flags defines properties that are expected to apply equally to all the sessions run by a single PuTTY process, the random number state in sshrand.c and the timer list in timing.c serve all sessions equally, and so on. But most data is specific to a particular network session, and is therefore stored in dynamically allocated data structures, and pointers to these structures are passed around between functions. -

    -

    -Platform-specific code can reverse this decision if it likes. The Windows code, for historical reasons, stores most of its data as global variables. That's OK, because on Windows we know there is only one session per PuTTY process, so it's safe to do that. But changes to the platform-independent code should avoid introducing global variables, unless they are genuinely cross-session. -

    -

    D.4 C, not C++

    -

    -PuTTY is written entirely in C, not in C++. -

    -

    -We have made some effort to make it easy to compile our code using a C++ compiler: notably, our snew, snewn and sresize macros explicitly cast the return values of malloc and realloc to the target type. (This has type checking advantages even in C: it means you never accidentally allocate the wrong size piece of memory for the pointer type you're assigning it to. C++ friendliness is really a side benefit.) -

    -

    -We want PuTTY to continue being pure C, at least in the platform-independent parts and the currently existing ports. Patches which switch the Makefiles to compile it as C++ and start using classes will not be accepted. Also, in particular, we disapprove of // comments, at least for the moment. (Perhaps once C99 becomes genuinely widespread we might be more lenient.) -

    -

    -The one exception: a port to a new platform may use languages other than C if they are necessary to code on that platform. If your favourite PDA has a GUI with a C++ API, then there's no way you can do a port of PuTTY without using C++, so go ahead and use it. But keep the C++ restricted to that platform's subdirectory; if your changes force the Unix or Windows ports to be compiled as C++, they will be unacceptable to us. -

    -

    D.5 Security-conscious coding

    -

    -PuTTY is a network application and a security application. Assume your code will end up being fed deliberately malicious data by attackers, and try to code in a way that makes it unlikely to be a security risk. -

    -

    -In particular, try not to use fixed-size buffers for variable-size data such as strings received from the network (or even the user). We provide functions such as dupcat and dupprintf, which dynamically allocate buffers of the right size for the string they construct. Use these wherever possible. -

    -

    D.6 Independence of specific compiler

    -

    -Windows PuTTY can currently be compiled with any of three Windows compilers: MS Visual C, the Cygwin / mingw32 GNU tools, and clang (in MS compatibility mode). -

    -

    -This is a really useful property of PuTTY, because it means people who want to contribute to the coding don't depend on having a specific compiler; so they don't have to fork out money for MSVC if they don't already have it, but on the other hand if they do have it they also don't have to spend effort installing gcc alongside it. They can use whichever compiler they happen to have available, or install whichever is cheapest and easiest if they don't have one. -

    -

    -Therefore, we don't want PuTTY to start depending on which compiler you're using. Using GNU extensions to the C language, for example, would ruin this useful property (not that anyone's ever tried it!); and more realistically, depending on an MS-specific library function supplied by the MSVC C library (_snprintf, for example) is a mistake, because that function won't be available under the other compilers. Any function supplied in an official Windows DLL as part of the Windows API is fine, and anything defined in the C library standard is also fine, because those should be available irrespective of compilation environment. But things in between, available as non-standard library and language extensions in only one compiler, are disallowed. -

    -

    -(_snprintf in particular should be unnecessary, since we provide dupprintf; see section D.5.) -

    -

    -Compiler independence should apply on all platforms, of course, not just on Windows. -

    -

    D.7 Small code size

    -

    -PuTTY is tiny, compared to many other Windows applications. And it's easy to install: it depends on no DLLs, no other applications, no service packs or system upgrades. It's just one executable. You install that executable wherever you want to, and run it. -

    -

    -We want to keep both these properties - the small size, and the ease of installation - if at all possible. So code contributions that depend critically on external DLLs, or that add a huge amount to the code size for a feature which is only useful to a small minority of users, are likely to be thrown out immediately. -

    -

    -We do vaguely intend to introduce a DLL plugin interface for PuTTY, whereby seriously large extra features can be implemented in plugin modules. The important thing, though, is that those DLLs will be optional; if PuTTY can't find them on startup, it should run perfectly happily and just won't provide those particular features. A full installation of PuTTY might one day contain ten or twenty little DLL plugins, which would cut down a little on the ease of installation - but if you really needed ease of installation you could still just install the one PuTTY binary, or just the DLLs you really needed, and it would still work fine. -

    -

    -Depending on external DLLs is something we'd like to avoid if at all possible (though for some purposes, such as complex SSH authentication mechanisms, it may be unavoidable). If it can't be avoided, the important thing is to follow the same principle of graceful degradation: if a DLL can't be found, then PuTTY should run happily and just not supply the feature that depended on it. -

    -

    D.8 Single-threaded code

    -

    -PuTTY and its supporting tools, or at least the vast majority of them, run in only one OS thread. -

    -

    -This means that if you're devising some piece of internal mechanism, there's no need to use locks to make sure it doesn't get called by two threads at once. The only way code can be called re-entrantly is by recursion. -

    -

    -That said, most of Windows PuTTY's network handling is triggered off Windows messages requested by WSAAsyncSelect(), so if you call MessageBox() deep within some network event handling code you should be aware that you might be re-entered if a network event comes in and is passed on to our window procedure by the MessageBox() message loop. -

    -

    -Also, the front ends (in particular Windows Plink) can use multiple threads if they like. However, Windows Plink keeps very tight control of its auxiliary threads, and uses them pretty much exclusively as a form of select(). Pretty much all the code outside windows/winplink.c is only ever called from the one primary thread; the others just loop round blocking on file handles and send messages to the main thread when some real work needs doing. This is not considered a portability hazard because that bit of windows/winplink.c will need rewriting on other platforms in any case. -

    -

    -One important consequence of this: PuTTY has only one thread in which to do everything. That ‘everything’ may include managing more than one login session (section D.3), managing multiple data channels within an SSH session, responding to GUI events even when nothing is happening on the network, and responding to network requests from the server (such as repeat key exchange) even when the program is dealing with complex user interaction such as the re-configuration dialog box. This means that almost none of the PuTTY code can safely block. -

    -

    D.9 Keystrokes sent to the server wherever possible

    -

    -In almost all cases, PuTTY sends keystrokes to the server. Even weird keystrokes that you think should be hot keys controlling PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a well-defined escape sequence that it could usefully be sending to the server, then it should do so, or at the very least it should be configurably able to do so. -

    -

    -To unconditionally turn a key combination into a hot key to control PuTTY is almost always a design error. If a hot key is really truly required, then try to find a key combination for it which isn't already used in existing PuTTYs (either it sends nothing to the server, or it sends the same thing as some other combination). Even then, be prepared for the possibility that one day that key combination might end up being needed to send something to the server - so make sure that there's an alternative way to invoke whatever PuTTY feature it controls. -

    -

    D.10 640×480 friendliness in configuration panels

    -

    -There's a reason we have lots of tiny configuration panels instead of a few huge ones, and that reason is that not everyone has a 1600×1200 desktop. 640×480 is still a viable resolution for running Windows (and indeed it's still the default if you start up in safe mode), so it's still a resolution we care about. -

    -

    -Accordingly, the PuTTY configuration box, and the PuTTYgen control window, are deliberately kept just small enough to fit comfortably on a 640×480 display. If you're adding controls to either of these boxes and you find yourself wanting to increase the size of the whole box, don't. Split it into more panels instead. -

    -

    D.11 Automatically generated Makefiles

    -

    -PuTTY is intended to compile on multiple platforms, and with multiple compilers. It would be horrifying to try to maintain a single Makefile which handled all possible situations, and just as painful to try to directly maintain a set of matching Makefiles for each different compilation environment. -

    -

    -Therefore, we have moved the problem up by one level. In the PuTTY source archive is a file called Recipe, which lists which source files combine to produce which binaries; and there is also a script called mkfiles.pl, which reads Recipe and writes out the real Makefiles. (The script also reads all the source files and analyses their dependencies on header files, so we get an extra benefit from doing it this way, which is that we can supply correct dependency information even in environments where it's difficult to set up an automated make depend phase.) -

    -

    -You should never edit any of the PuTTY Makefiles directly. They are not stored in our source repository at all. They are automatically generated by mkfiles.pl from the file Recipe. -

    -

    -If you need to add a new object file to a particular binary, the right thing to do is to edit Recipe and re-run mkfiles.pl. This will cause the new object file to be added in every tool that requires it, on every platform where it matters, in every Makefile to which it is relevant, and to get all the dependency data right. -

    -

    -If you send us a patch that modifies one of the Makefiles, you just waste our time, because we will have to convert it into a change to Recipe. If you send us a patch that modifies all of the Makefiles, you will have wasted a lot of your time as well! -

    -

    -(There is a comment at the top of every Makefile in the PuTTY source archive saying this, but many people don't seem to read it, so it's worth repeating here.) -

    -

    D.12 Coroutines in the SSH code

    -

    -Large parts of the code in the various SSH modules (in fact most of the protocol layers) are structured using a set of macros that implement (something close to) Donald Knuth's ‘coroutines’ concept in C. -

    -

    -Essentially, the purpose of these macros are to arrange that a function can call crReturn() to return to its caller, and the next time it is called control will resume from just after that crReturn statement. -

    -

    -This means that any local (automatic) variables declared in such a function will be corrupted every time you call crReturn. If you need a variable to persist for longer than that, you must make it a field in some appropriate structure containing the persistent state of the coroutine – typically the main state structure for an SSH protocol layer. -

    -

    -See https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for a more in-depth discussion of what these macros are for and how they work. -

    -

    -Another caveat: most of these coroutines are not guaranteed to run to completion, because the SSH connection (or whatever) that they're part of might be interrupted at any time by an unexpected network event or user action. So whenever a coroutine-managed variable refers to a resource that needs releasing, you should also ensure that the cleanup function for its containing state structure can reliably release it even if the coroutine is aborted at an arbitrary point. -

    -

    -For example, if an SSH packet protocol layer has to have a field that sometimes points to a piece of allocated memory, then you should ensure that when you free that memory you reset the pointer field to NULL. Then, no matter when the protocol layer's cleanup function is called, it can reliably free the memory if there is any, and not crash if there isn't. -

    -

    D.13 Single compilation of each source file

    -

    -The PuTTY build system for any given platform works on the following very simple model: -

    -
    • -Each source file is compiled precisely once, to produce a single object file. -
    • -
    • -Each binary is created by linking together some combination of those object files. -
    • -
    +

    Appendix D: PuTTY Licence

    -Therefore, if you need to introduce functionality to a particular module which is only available in some of the tool binaries (for example, a cryptographic proxy authentication mechanism which needs to be left out of PuTTYtel to maintain its usability in crypto-hostile jurisdictions), the wrong way to do it is by adding #ifdefs in (say) proxy.c. This would require separate compilation of proxy.c for PuTTY and PuTTYtel, which means that the entire Makefile-generation architecture (see section D.11) would have to be significantly redesigned. Unless you are prepared to do that redesign yourself, and guarantee that it will still port to any future platforms we might decide to run on, you should not attempt this! +PuTTY is copyright 1997-2021 Simon Tatham.

    -The right way to introduce a feature like this is to put the new code in a separate source file, and (if necessary) introduce a second new source file defining the same set of functions, but defining them as stubs which don't provide the feature. Then the module whose behaviour needs to vary (proxy.c in this example) can call the functions defined in these two modules, and it will either provide the new feature or not provide it according to which of your new modules it is linked with. +Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A.

    -Of course, object files are never shared between platforms; so it is allowable to use #ifdef to select between platforms. This happens in puttyps.h (choosing which of the platform-specific include files to use), and also in misc.c (the Windows-specific ‘Minefield’ memory diagnostic system). It should be used sparingly, though, if at all. +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    -

    D.14 Do as we say, not as we do

    -The current PuTTY code probably does not conform strictly to all of the principles listed above. There may be the occasional SSH-specific piece of code in what should be a backend-independent module, or the occasional dependence on a non-standard X library function under Unix. +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    -This should not be taken as a licence to go ahead and violate the rules. Where we violate them ourselves, we're not happy about it, and we would welcome patches that fix any existing problems. Please try to help us make our code better, not worse! +THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


    If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

    -[PuTTY release 0.73]
    +[PuTTY release 0.76] diff --git a/doc/AppendixE.html b/doc/AppendixE.html index 092b503..593f441 100644 --- a/doc/AppendixE.html +++ b/doc/AppendixE.html @@ -3,7 +3,7 @@ -PuTTY download keys and signatures +PuTTY hacking guide @@ -14,242 +14,449 @@

    Previous | Contents | Index | Next

    -

    Appendix E: PuTTY download keys and signatures

    -

    -We create GPG signatures for all the PuTTY files distributed from our web site, so that users can be confident that the files have not been tampered with. Here we identify our public keys, and explain our signature policy so you can have an accurate idea of what each signature guarantees. This description is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual. -

    -

    -As of release 0.58, all of the PuTTY executables contain fingerprint material (usually accessed via the -pgpfp command-line option), such that if you have an executable you trust, you can use it to establish a trust path, for instance to a newer version downloaded from the Internet. -

    +

    Appendix E: PuTTY hacking guide

    -As of release 0.67, the Windows executables and installer also contain built-in signatures that are automatically verified by Windows' own mechanism (‘Authenticode’). The keys used for that are different, and are not covered here. +This appendix lists a selection of the design principles applying to the PuTTY source code. If you are planning to send code contributions, you should read this first.

    +

    E.1 Cross-OS portability

    -(Note that none of the keys, signatures, etc mentioned here have anything to do with keys used with SSH - they are purely for verifying the origin of files distributed by the PuTTY team.) +Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac port is in progress; more ports may or may not happen at a later date.

    -

    E.1 Public keys

    -We maintain multiple keys, stored with different levels of security due to being used in different ways. See section E.2 below for details. +Therefore, embedding Windows-specific code in core modules such as ssh.c is not acceptable. We went to great lengths to remove all the Windows-specific stuff from our core modules, and to shift it out into Windows-specific modules. Adding large amounts of Windows-specific stuff in parts of the code that should be portable is almost guaranteed to make us reject a contribution.

    -The keys we provide are: +The PuTTY source base is divided into platform-specific modules and platform-generic modules. The Unix-specific modules are all in the unix subdirectory; the Windows-specific modules are in the windows subdirectory.

    -
    -Snapshot Key -
    -
    -Used to sign routine development builds of PuTTY: nightly snapshots, pre-releases, and sometimes also custom diagnostic builds we send to particular users. -
    -
    -Release Key -
    -
    -Used to sign manually released versions of PuTTY. -
    -
    -Secure Contact Key -
    -
    -An encryption-capable key suitable for people to send confidential messages to the PuTTY team, e.g. reports of vulnerabilities. -
    -
    -Master Key -
    -
    -Used to tie all the above keys into the GPG web of trust. The Master Key signs all the other keys, and other GPG users have signed it in turn. -
    -

    -The current issue of those keys are available for download from the PuTTY website, and are also available on PGP keyservers using the key IDs listed below. +All the modules in the main source directory - notably all of the code for the various back ends - are platform-generic. We want to keep them that way.

    -
    -Master Key (2018) -
    -
    -RSA, 4096-bit. Key ID: 76BC7FE4EBFD2D9E. Fingerprint: 24E1 B1C5 75EA 3C9F F752  A922 76BC 7FE4 EBFD 2D9E -
    -
    -Release Key (2018) -
    -
    -RSA, 3072-bit. Key ID: 6289A25F4AE8DA82. Fingerprint: E273 94AC A3F9 D904 9522  E054 6289 A25F 4AE8 DA82 -
    -
    -Snapshot Key (2018) -
    -
    -RSA, 3072-bit. Key ID: 38BA7229B7588FD1. Fingerprint: C92B 52E9 9AB6 1DDA 33DB  2B7A 38BA 7229 B758 8FD1 -
    -
    -Secure Contact Key (2018) -
    -
    -RSA, 3072-bit. Key ID: 657D487977F95C98. Fingerprint: A680 0082 2998 6E46 22CA  0E43 657D 4879 77F9 5C98 -
    -
    -

    E.2 Security details

    -The various keys have various different security levels. This section explains what those security levels are, and how far you can expect to trust each key. +This also means you should stick to the C semantics guaranteed by the C standard: try not to make assumptions about the precise size of basic types such as int and long int; don't use pointer casts to do endianness-dependent operations, and so on.

    -

    E.2.1 The Development Snapshots key

    -The Development Snapshots private key is stored without a passphrase. This is necessary, because the snapshots are generated every night without human intervention, so nobody would be able to type a passphrase. +(Even within a platform front end you should still be careful of some of these portability issues. The Windows front end compiles on both 32- and 64-bit x86 and also Arm.)

    -The snapshots are built and signed on a team member's home computers, before being uploaded to the web server from which you download them. +Our current choice of C standards version is mostly C99. With a couple of exceptions, you can assume that C99 features are available (in particular <stdint.h>, <stdbool.h> and inline), but you shouldn't use things that are new in C11 (such as <uchar.h> or _Generic).

    -Therefore, a signature from the Development Snapshots key DOES protect you against: +The exceptions to that rule are due to the need for Visual Studio compatibility:

    • -People tampering with the PuTTY binaries between the PuTTY web site and you. +Don't use variable-length arrays. Visual Studio doesn't support them even now that it's adopted the rest of C99. We use -Wvla when building with gcc and clang, to make it easier to avoid accidentally breaking that rule.
    • -The maintainers of our web server attempting to abuse their root privilege to tamper with the binaries. +For historical reasons, we still build with one older VS version which lacks <inttypes.h>. So that file is included centrally in defs.h, and has a set of workaround definitions for the PRIx64-type macros we use. If you need to use another one of those macros, you need to add a workaround definition in defs.h, and don't casually re-include <inttypes.h> anywhere else in the source file.

    -But it DOES NOT protect you against: +Here are a few portability assumptions that we do currently allow (because we'd already have to thoroughly vet the existing code if they ever needed to change, and it doesn't seem worth doing that unless we really have to):

    • -People tampering with the binaries before they are uploaded to our download servers. +You can assume int is at least 32 bits wide. (We've never tried to port PuTTY to a platform with 16-bit int, and it doesn't look likely to be necessary in future.)
    • -People tampering with the build machines so that the next set of binaries they build will be malicious in some way. +Similarly, you can assume char is exactly 8 bits. (Exceptions to that are even less likely to be relevant to us than short int.)
    • -People stealing the unencrypted private key from the build machine it lives on. +You can assume that using memset to write zero bytes over a whole structure will have the effect of setting all its pointer fields to NULL. (The standard itself guarantees this for integer fields, but not for pointers.) +
    • +
    • +You can assume that time_t has POSIX semantics, i.e. that it represents an integer number of non-leap seconds since 1970-01-01 00:00:00 UTC. (Times in this format are used in X authorisation, but we could work around that by carefully distinguishing local time_t from time values used in the wire protocol; but these semantics of time_t are also baked into the shared library API used by the GSSAPI authentication code, which would be much harder to change.) +
    • +
    • +You can assume that the execution character encoding is a superset of the printable characters of ASCII. (In particular, it's fine to do arithmetic on a char value representing a Latin alphabetic character, without bothering to allow for EBCDIC or other non-consecutive encodings of the alphabet.)

    -Of course, we take all reasonable precautions to guard the build machines. But when you see a signature, you should always be certain of precisely what it guarantees and precisely what it does not. +On the other hand, here are some particular things not to assume: +

    +
    • +Don't assume anything about the signedness of char. In particular, you must cast char values to unsigned char before passing them to any <ctype.h> function (because those expect a non-negative character value, or EOF). If you need a particular signedness, explicitly specify signed char or unsigned char, or use C99 int8_t or uint8_t. +
    • +
    • +From past experience with MacOS, we're still a bit nervous about '\n' and '\r' potentially having unusual meanings on a given platform. So it's fine to say \n in a string you're passing to printf, but in any context where those characters appear in a standardised wire protocol or a binary file format, they should be spelled '\012' and '\015' respectively. +
    • +
    +

    E.2 Multiple backends treated equally

    +

    +PuTTY is not an SSH client with some other stuff tacked on the side. PuTTY is a generic, multiple-backend, remote VT-terminal client which happens to support one backend which is larger, more popular and more useful than the rest. Any extra feature which can possibly be general across all backends should be so: localising features unnecessarily into the SSH back end is a design error. (For example, we had several code submissions for proxy support which worked by hacking ssh.c. Clearly this is completely wrong: the network.h abstraction is the place to put it, so that it will apply to all back ends equally, and indeed we eventually put it there after another contributor sent a better patch.) +

    +

    +The rest of PuTTY should try to avoid knowing anything about specific back ends if at all possible. To support a feature which is only available in one network protocol, for example, the back end interface should be extended in a general manner such that any back end which is able to provide that feature can do so. If it so happens that only one back end actually does, that's just the way it is, but it shouldn't be relied upon by any code. +

    +

    E.3 Multiple sessions per process on some platforms

    +

    +Some ports of PuTTY - notably the in-progress Mac port - are constrained by the operating system to run as a single process potentially managing multiple sessions. +

    +

    +Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular login session. The random number state in sshrand.c, the timer list in timing.c and the queue of top-level callbacks in callback.c serve all sessions equally. But most data is specific to a particular network session, and is therefore stored in dynamically allocated data structures, and pointers to these structures are passed around between functions. +

    +

    +Platform-specific code can reverse this decision if it likes. The Windows code, for historical reasons, stores most of its data as global variables. That's OK, because on Windows we know there is only one session per PuTTY process, so it's safe to do that. But changes to the platform-independent code should avoid introducing global variables, unless they are genuinely cross-session. +

    +

    E.4 C, not C++

    +

    +PuTTY is written entirely in C, not in C++. +

    +

    +We have made some effort to make it easy to compile our code using a C++ compiler: notably, our snew, snewn and sresize macros explicitly cast the return values of malloc and realloc to the target type. (This has type checking advantages even in C: it means you never accidentally allocate the wrong size piece of memory for the pointer type you're assigning it to. C++ friendliness is really a side benefit.) +

    +

    +We want PuTTY to continue being pure C, at least in the platform-independent parts and the currently existing ports. Patches which switch the Makefiles to compile it as C++ and start using classes will not be accepted. Also, in particular, we disapprove of // comments, at least for the moment. (Perhaps once C99 becomes genuinely widespread we might be more lenient.) +

    +

    +The one exception: a port to a new platform may use languages other than C if they are necessary to code on that platform. If your favourite PDA has a GUI with a C++ API, then there's no way you can do a port of PuTTY without using C++, so go ahead and use it. But keep the C++ restricted to that platform's subdirectory; if your changes force the Unix or Windows ports to be compiled as C++, they will be unacceptable to us. +

    +

    E.5 Security-conscious coding

    +

    +PuTTY is a network application and a security application. Assume your code will end up being fed deliberately malicious data by attackers, and try to code in a way that makes it unlikely to be a security risk. +

    +

    +In particular, try not to use fixed-size buffers for variable-size data such as strings received from the network (or even the user). We provide functions such as dupcat and dupprintf, which dynamically allocate buffers of the right size for the string they construct. Use these wherever possible. +

    +

    E.6 Independence of specific compiler

    +

    +Windows PuTTY can currently be compiled with any of three Windows compilers: MS Visual C, the Cygwin / mingw32 GNU tools, and clang (in MS compatibility mode). +

    +

    +This is a really useful property of PuTTY, because it means people who want to contribute to the coding don't depend on having a specific compiler; so they don't have to fork out money for MSVC if they don't already have it, but on the other hand if they do have it they also don't have to spend effort installing gcc alongside it. They can use whichever compiler they happen to have available, or install whichever is cheapest and easiest if they don't have one. +

    +

    +Therefore, we don't want PuTTY to start depending on which compiler you're using. Using GNU extensions to the C language, for example, would ruin this useful property (not that anyone's ever tried it!); and more realistically, depending on an MS-specific library function supplied by the MSVC C library (_snprintf, for example) is a mistake, because that function won't be available under the other compilers. Any function supplied in an official Windows DLL as part of the Windows API is fine, and anything defined in the C library standard is also fine, because those should be available irrespective of compilation environment. But things in between, available as non-standard library and language extensions in only one compiler, are disallowed. +

    +

    +(_snprintf in particular should be unnecessary, since we provide dupprintf; see section E.5.) +

    +

    +Compiler independence should apply on all platforms, of course, not just on Windows. +

    +

    E.7 Small code size

    +

    +PuTTY is tiny, compared to many other Windows applications. And it's easy to install: it depends on no DLLs, no other applications, no service packs or system upgrades. It's just one executable. You install that executable wherever you want to, and run it. +

    +

    +We want to keep both these properties - the small size, and the ease of installation - if at all possible. So code contributions that depend critically on external DLLs, or that add a huge amount to the code size for a feature which is only useful to a small minority of users, are likely to be thrown out immediately. +

    +

    +We do vaguely intend to introduce a DLL plugin interface for PuTTY, whereby seriously large extra features can be implemented in plugin modules. The important thing, though, is that those DLLs will be optional; if PuTTY can't find them on startup, it should run perfectly happily and just won't provide those particular features. A full installation of PuTTY might one day contain ten or twenty little DLL plugins, which would cut down a little on the ease of installation - but if you really needed ease of installation you could still just install the one PuTTY binary, or just the DLLs you really needed, and it would still work fine. +

    +

    +Depending on external DLLs is something we'd like to avoid if at all possible (though for some purposes, such as complex SSH authentication mechanisms, it may be unavoidable). If it can't be avoided, the important thing is to follow the same principle of graceful degradation: if a DLL can't be found, then PuTTY should run happily and just not supply the feature that depended on it. +

    +

    E.8 Single-threaded code

    +

    +PuTTY and its supporting tools, or at least the vast majority of them, run in only one OS thread. +

    +

    +This means that if you're devising some piece of internal mechanism, there's no need to use locks to make sure it doesn't get called by two threads at once. The only way code can be called re-entrantly is by recursion. +

    +

    +That said, most of Windows PuTTY's network handling is triggered off Windows messages requested by WSAAsyncSelect(), so if you call MessageBox() deep within some network event handling code you should be aware that you might be re-entered if a network event comes in and is passed on to our window procedure by the MessageBox() message loop. +

    +

    +Also, the front ends (in particular Windows Plink) can use multiple threads if they like. However, Windows Plink keeps very tight control of its auxiliary threads, and uses them pretty much exclusively as a form of select(). Pretty much all the code outside windows/winplink.c is only ever called from the one primary thread; the others just loop round blocking on file handles and send messages to the main thread when some real work needs doing. This is not considered a portability hazard because that bit of windows/winplink.c will need rewriting on other platforms in any case. +

    +

    +One important consequence of this: PuTTY has only one thread in which to do everything. That ‘everything’ may include managing more than one login session (section E.3), managing multiple data channels within an SSH session, responding to GUI events even when nothing is happening on the network, and responding to network requests from the server (such as repeat key exchange) even when the program is dealing with complex user interaction such as the re-configuration dialog box. This means that almost none of the PuTTY code can safely block. +

    +

    E.9 Keystrokes sent to the server wherever possible

    +

    +In almost all cases, PuTTY sends keystrokes to the server. Even weird keystrokes that you think should be hot keys controlling PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a well-defined escape sequence that it could usefully be sending to the server, then it should do so, or at the very least it should be configurably able to do so. +

    +

    +To unconditionally turn a key combination into a hot key to control PuTTY is almost always a design error. If a hot key is really truly required, then try to find a key combination for it which isn't already used in existing PuTTYs (either it sends nothing to the server, or it sends the same thing as some other combination). Even then, be prepared for the possibility that one day that key combination might end up being needed to send something to the server - so make sure that there's an alternative way to invoke whatever PuTTY feature it controls. +

    +

    E.10 640×480 friendliness in configuration panels

    +

    +There's a reason we have lots of tiny configuration panels instead of a few huge ones, and that reason is that not everyone has a 1600×1200 desktop. 640×480 is still a viable resolution for running Windows (and indeed it's still the default if you start up in safe mode), so it's still a resolution we care about. +

    +

    +Accordingly, the PuTTY configuration box, and the PuTTYgen control window, are deliberately kept just small enough to fit comfortably on a 640×480 display. If you're adding controls to either of these boxes and you find yourself wanting to increase the size of the whole box, don't. Split it into more panels instead. +

    +

    E.11 Automatically generated Makefiles

    +

    +PuTTY is intended to compile on multiple platforms, and with multiple compilers. It would be horrifying to try to maintain a single Makefile which handled all possible situations, and just as painful to try to directly maintain a set of matching Makefiles for each different compilation environment. +

    +

    +Therefore, we have moved the problem up by one level. In the PuTTY source archive is a file called Recipe, which lists which source files combine to produce which binaries; and there is also a script called mkfiles.pl, which reads Recipe and writes out the real Makefiles. (The script also reads all the source files and analyses their dependencies on header files, so we get an extra benefit from doing it this way, which is that we can supply correct dependency information even in environments where it's difficult to set up an automated make depend phase.) +

    +

    +You should never edit any of the PuTTY Makefiles directly. They are not stored in our source repository at all. They are automatically generated by mkfiles.pl from the file Recipe. +

    +

    +If you need to add a new object file to a particular binary, the right thing to do is to edit Recipe and re-run mkfiles.pl. This will cause the new object file to be added in every tool that requires it, on every platform where it matters, in every Makefile to which it is relevant, and to get all the dependency data right. +

    +

    +If you send us a patch that modifies one of the Makefiles, you just waste our time, because we will have to convert it into a change to Recipe. If you send us a patch that modifies all of the Makefiles, you will have wasted a lot of your time as well! +

    +

    +(There is a comment at the top of every Makefile in the PuTTY source archive saying this, but many people don't seem to read it, so it's worth repeating here.) +

    +

    E.12 Coroutines in the SSH code

    +

    +Large parts of the code in the various SSH modules (in fact most of the protocol layers) are structured using a set of macros that implement (something close to) Donald Knuth's ‘coroutines’ concept in C. +

    +

    +Essentially, the purpose of these macros are to arrange that a function can call crReturn() to return to its caller, and the next time it is called control will resume from just after that crReturn statement.

    -

    E.2.2 The Releases key

    -The Releases key is more secure: because it is only used at release time, to sign each release by hand, we can store it encrypted. +This means that any local (automatic) variables declared in such a function will be corrupted every time you call crReturn. If you need a variable to persist for longer than that, you must make it a field in some appropriate structure containing the persistent state of the coroutine – typically the main state structure for an SSH protocol layer.

    -The Releases private key is kept encrypted on the developers' own local machines. So an attacker wanting to steal it would have to also steal the passphrase. +See https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for a more in-depth discussion of what these macros are for and how they work.

    -

    E.2.3 The Secure Contact Key

    -The Secure Contact Key is stored with a similar level of security to the Release Key: it is stored with a passphrase, and no automated script has access to it. +Another caveat: most of these coroutines are not guaranteed to run to completion, because the SSH connection (or whatever) that they're part of might be interrupted at any time by an unexpected network event or user action. So whenever a coroutine-managed variable refers to a resource that needs releasing, you should also ensure that the cleanup function for its containing state structure can reliably release it even if the coroutine is aborted at an arbitrary point.

    -

    E.2.4 The Master Keys

    -The Master Key signs almost nothing. Its purpose is to bind the other keys together and certify that they are all owned by the same people and part of the same integrated setup. The only signatures produced by the Master Key, ever, should be the signatures on the other keys. +For example, if an SSH packet protocol layer has to have a field that sometimes points to a piece of allocated memory, then you should ensure that when you free that memory you reset the pointer field to NULL. Then, no matter when the protocol layer's cleanup function is called, it can reliably free the memory if there is any, and not crash if there isn't.

    +

    E.13 Explicit vtable structures to implement traits

    -The Master Key is especially long, and its private key and passphrase are stored with special care. +A lot of PuTTY's code is written in a style that looks structurally rather like an object-oriented language, in spite of PuTTY being a pure C program.

    -We have collected some third-party signatures on the Master Key, in order to increase the chances that you can find a suitable trust path to them. +For example, there's a single data type called ssh_hash, which is an abstraction of a secure hash function, and a bunch of functions called things like ssh_hash_foo that do things with those data types. But in fact, PuTTY supports many different hash functions, and each one has to provide its own implementation of those functions.

    -We have uploaded our various keys to public keyservers, so that even if you don't know any of the people who have signed our keys, you can still be reasonably confident that an attacker would find it hard to substitute fake keys on all the public keyservers at once. +In C++ terms, this is rather like having a single abstract base class, and multiple concrete subclasses of it, each of which fills in all the pure virtual methods in a way that's compatible with the data fields of the subclass. The implementation is more or less the same, as well: in C, we do explicitly in the source code what the C++ compiler will be doing behind the scenes at compile time.

    -

    E.3 Key rollover

    -Our current keys were generated in August 2018. +But perhaps a closer analogy in functional terms is the Rust concept of a ‘trait’, or the Java idea of an ‘interface’. C++ supports a multi-level hierarchy of inheritance, whereas PuTTY's system – like traits or interfaces – has only two levels, one describing a generic object of a type (e.g. a hash function) and another describing a specific implementation of that type (e.g. SHA-256).

    -Each new Master Key is signed with the old one, to show that it really is owned by the same people and not substituted by an attacker. +The PuTTY code base has a standard idiom for doing this in C, as follows.

    -Each new Master Key also signs the previous Release Keys, in case you're trying to verify the signatures on a release prior to the rollover and can find a chain of trust to those keys from any of the people who have signed our new Master Key. +Firstly, we define two struct types for our trait. One of them describes a particular kind of implementation of that trait, and it's full of (mostly) function pointers. The other describes a specific instance of an implementation of that trait, and it will contain a pointer to a const instance of the first type. For example:

    +
    typedef struct MyAbstraction MyAbstraction;
    +typedef struct MyAbstractionVtable MyAbstractionVtable;
    +
    +struct MyAbstractionVtable {
    +    MyAbstraction *(*new)(const MyAbstractionVtable *vt);
    +    void (*free)(MyAbstraction *);
    +    void (*modify)(MyAbstraction *, unsigned some_parameter);
    +    unsigned (*query)(MyAbstraction *, unsigned some_parameter);
    +};
    +
    +struct MyAbstraction {
    +    const MyAbstractionVtable *vt;
    +};
    +
    +

    +Here, we imagine that MyAbstraction might be some kind of object that contains mutable state. The associated vtable structure shows what operations you can perform on a MyAbstraction: you can create one (dynamically allocated), free one you already have, or call the example methods ‘modify’ (to change the state of the object in some way) and ‘query’ (to return some value derived from the object's current state). +

    +

    +(In most cases, the vtable structure has a name ending in ‘vtable’. But for historical reasons a lot of the crypto primitives that use this scheme – ciphers, hash functions, public key methods and so on – instead have names ending in ‘alg’, on the basis that the primitives they implement are often referred to as ‘encryption algorithms’, ‘hash algorithms’ and so forth.) +

    +

    +Now, to define a concrete instance of this trait, you'd define a struct that contains a MyAbstraction field, plus any other data it might need: +

    +
    struct MyImplementation {
    +    unsigned internal_data[16];
    +    SomeOtherType *dynamic_subthing;
    +
    +    MyAbstraction myabs;
    +};
    +
    +

    +Next, you'd implement all the necessary methods for that implementation of the trait, in this kind of style: +

    +
    static MyAbstraction *myimpl_new(const MyAbstractionVtable *vt)
    +{
    +    MyImplementation *impl = snew(MyImplementation);
    +    memset(impl, 0, sizeof(*impl));
    +    impl->dynamic_subthing = allocate_some_other_type();
    +    impl->myabs.vt = vt;
    +    return &impl->myabs;
    +}
    +
    +static void myimpl_free(MyAbstraction *myabs)
    +{
    +    MyImplementation *impl = container_of(myabs, MyImplementation, myabs);
    +    free_other_type(impl->dynamic_subthing);
    +    sfree(impl);
    +}
    +
    +static void myimpl_modify(MyAbstraction *myabs, unsigned param)
    +{
    +    MyImplementation *impl = container_of(myabs, MyImplementation, myabs);
    +    impl->internal_data[param] += do_something_with(impl->dynamic_subthing);
    +}
    +
    +static unsigned myimpl_query(MyAbstraction *myabs, unsigned param)
    +{
    +    MyImplementation *impl = container_of(myabs, MyImplementation, myabs);
    +    return impl->internal_data[param];
    +}
    +
    +

    +Having defined those methods, now we can define a const instance of the vtable structure containing pointers to them: +

    +
    const MyAbstractionVtable MyImplementation_vt = {
    +    .new = myimpl_new,
    +    .free = myimpl_free,
    +    .modify = myimpl_modify,
    +    .query = myimpl_query,
    +};
    +
    +

    +In principle, this is all you need. Client code can construct a new instance of a particular implementation of MyAbstraction by digging out the new method from the vtable and calling it (with the vtable itself as a parameter), which returns a MyAbstraction * pointer that identifies a newly created instance, in which the vt field will contain a pointer to the same vtable structure you passed in. And once you have an instance object, say MyAbstraction *myabs, you can dig out one of the other method pointers from the vtable it points to, and call that, passing the object itself as a parameter. +

    +

    +But in fact, we don't do that, because it looks pretty ugly at all the call sites. Instead, what we generally do in this code base is to write a set of static inline wrapper functions in the same header file that defined the MyAbstraction structure types, like this: +

    +
    static MyAbstraction *myabs_new(const MyAbstractionVtable *vt)
    +{ return vt->new(vt); }
    +static void myabs_free(MyAbstraction *myabs)
    +{ myabs->vt->free(myabs); }
    +static void myimpl_modify(MyAbstraction *myabs, unsigned param)
    +{ myabs->vt->modify(myabs, param); }
    +static unsigned myimpl_query(MyAbstraction *myabs, unsigned param)
    +{ return myabs->vt->query(myabs, param); }
    +
    +

    +And now call sites can use those reasonably clean-looking wrapper functions, and shouldn't ever have to directly refer to the vt field inside any myabs object they're holding. For example, you might write something like this: +

    +
    MyAbstraction *myabs = myabs_new(&MyImplementation_vtable);
    +myabs_update(myabs, 10);
    +unsigned output = myabs_query(myabs, 2);
    +myabs_free(myabs);
    +
    +

    +and then all this code can use a different implementation of the same abstraction by just changing which vtable pointer it passed in in the first line. +

    +

    +Some things to note about this system: +

    +
    • +The implementation instance type (here ‘MyImplementation’ contains the abstraction type (‘MyAbstraction’) as one of its fields. But that field is not necessarily at the start of the structure. So you can't just cast pointers back and forth between the two types. Instead: +
      • +You ‘up-cast’ from implementation to abstraction by taking the address of the MyAbstraction field. You can see the example new method above doing this, returning &impl->myabs. All new methods do this on return. +
      • +
      • +Going in the other direction, each method that was passed a generic MyAbstraction *myabs parameter has to recover a pointer to the specific implementation type MyImplementation *impl. The idiom for doing that is to use the ‘container_of’ macro, also seen in the Linux kernel code. Generally, container_of(p, Type, field) says: ‘I'm confident that the pointer value ‘p’ is pointing to the field called ‘field’ within a larger struct of type Type. Please return me the pointer to the containing structure.’ So in this case, we take the ‘myabs’ pointer passed to the function, and ‘down-cast’ it into a pointer to the larger and more specific structure type MyImplementation, by adjusting the pointer value based on the offset within that structure of the field called ‘myabs’. +
      • +
      +

      +This system is flexible enough to permit ‘multiple inheritance’, or rather, multiple implementation: having one object type implement more than one trait. For example, the Proxy type implements both the Socket trait and the Plug trait that connects to it, because it has to act as an adapter between another instance of each of those types. +

      +

      +It's also perfectly possible to have the same object implement the same trait in two different ways. At the time of writing this I can't think of any case where we actually do this, but a theoretical example might be if you needed to support a trait like Comparable in two ways that sorted by different criteria. There would be no difficulty doing this in the PuTTY system: simply have your implementation struct contain two (or more) fields of the same abstraction type. The fields will have different names, which makes it easy to explicitly specify which one you're returning a pointer to during up-casting, or which one you're down-casting from using container_of. And then both sets of implementation methods can recover a pointer to the same containing structure. +

      + +
    • +
    • +Unlike in C++, all objects in PuTTY that use this system are dynamically allocated. The ‘constructor’ functions (whether they're virtualised across the whole abstraction or specific to each implementation) always allocate memory and return a pointer to it. The ‘free’ method (our analogue of a destructor) always expects the input pointer to be dynamically allocated, and frees it. As a result, client code doesn't need to know how large the implementing object type is, because it will never need to allocate it (on the stack or anywhere else). +
    • +
    • +Unlike in C++, the abstraction's ‘vtable’ structure does not only hold methods that you can call on an instance object. It can also hold several other kinds of thing: +
      • +Methods that you can call without an instance object, given only the vtable structure identifying a particular implementation of the trait. You might think of these as ‘static methods’, as in C++, except that they're virtual – the same code can call the static method of a different ‘class’ given a different vtable pointer. So they're more like ‘virtual static methods’, which is a concept C++ doesn't have. An example is the pubkey_bits method in ssh_keyalg. +
      • +
      • +The most important case of a ‘virtual static method’ is the new method that allocates and returns a new object. You can think of it as a ‘virtual constructor’ – another concept C++ doesn't have. (However, not all types need one of these: see below.) +
      • +
      • +The vtable can also contain constant data relevant to the class as a whole – ‘virtual constant data’. For example, a cryptographic hash function will contain an integer field giving the length of the output hash, and most crypto primitives will contain a string field giving the identifier used in the SSH protocol that describes that primitive. +
      • +
      +

      +The effect of all of this is that you can make other pieces of code able to use any instance of one of these types, by passing it an actual vtable as a parameter. For example, the hash_simple function takes an ssh_hashalg vtable pointer specifying any hash algorithm you like, and internally, it creates an object of that type, uses it, and frees it. In C++, you'd probably do this using a template, which would mean you had multiple specialisations of hash_simple – and then it would be much more difficult to decide at run time which one you needed to use. Here, hash_simple is still just one function, and you can decide as late as you like which vtable to pass to it. +

      + +
    • +
    • +The abstract instance structure can also contain publicly visible data fields (this time, usually treated as mutable) which are common to all implementations of the trait. For example, BinaryPacketProtocol has lots of these. +
    • +
    • +Not all abstractions of this kind want virtual constructors. It depends on how different the implementations are. +

      +With a crypto primitive like a hash algorithm, the constructor call looks the same for every implementing type, so it makes sense to have a standardised virtual constructor in the vtable and a ssh_hash_new wrapper function which can make an instance of whatever vtable you pass it. And then you make all the vtable objects themselves globally visible throughout the source code, so that any module can call (for example) ssh_hash_new(&ssh_sha256). +

      +

      +But with other kinds of object, the constructor for each implementing type has to take a different set of parameters. For example, implementations of Socket are not generally interchangeable at construction time, because constructing different kinds of socket require totally different kinds of address parameter. In that situation, it makes more sense to keep the vtable structure itself private to the implementing source file, and instead, publish an ordinary constructing function that allocates and returns an instance of that particular subtype, taking whatever parameters are appropriate to that subtype. +

      + +
    • +
    • +If you do have virtual constructors, you can choose whether they take a vtable pointer as a parameter (as shown above), or an existing instance object. In the latter case, they can refer to the object itself as well as the vtable. For example, you could have a trait come with a virtual constructor called ‘clone’, meaning ‘Make a copy of this object, no matter which implementation it is.’ +
    • +
    • +Sometimes, a single vtable structure type can be shared between two completely different object types, and contain all the methods for both. For example, ssh_compression_alg contains methods to create, use and free ssh_compressor and ssh_decompressor objects, which are not interchangeable – but putting their methods in the same vtable means that it's easy to create a matching pair of objects that are compatible with each other. +
    • +
    • +Passing the vtable itself as an argument to the new method is not compulsory: if a given new implementation is only used by a single vtable, then that function can simply hard-code the vtable pointer that it writes into the object it constructs. But passing the vtable is more flexible, because it allows a single constructor function to be shared between multiple slightly different object types. For example, SHA-384 and SHA-512 share the same new method and the same implementation data type, because they're very nearly the same hash algorithm – but a couple of the other methods in their vtables are different, because the ‘reset’ function has to set up the initial algorithm state differently, and the ‘digest’ method has to write out a different amount of data. +

      +One practical advantage of having the myabs_foo family of inline wrapper functions in the header file is that if you change your mind later about whether the vtable needs to be passed to new, you only have to update the myabs_new wrapper, and then the existing call sites won't need changing. +

      + +
    • +
    • +Another piece of ‘stunt object orientation’ made possible by this scheme is that you can write two vtables that both use the same structure layout for the implementation object, and have an object transform from one to the other part way through its lifetime, by overwriting its own vtable pointer field. For example, the sesschan type that handles the server side of an SSH terminal session will sometimes transform in mid-lifetime into an SCP or SFTP file-transfer channel in this way, at the point where the client sends an ‘exec’ or ‘subsystem’ request that indicates that that's what it wants to do with the channel. +

      +This concept would be difficult to arrange in C++. In Rust, it wouldn't even make sense, because in Rust, objects implementing a trait don't even contain a vtable pointer at all – instead, the ‘trait object’ type (identifying a specific instance of some implementation of a given trait) consists of a pair of pointers, one to the object itself and one to the vtable. In that model, the only way you could make an existing object turn into a different trait would be to know where all the pointers to it were stored elsewhere in the program, and persuade all their owners to rewrite them. +

      + +
    • +
    • +Another stunt you can do is to have a vtable that doesn't have a corresponding implementation structure at all, because the only methods implemented in it are the constructors, and they always end up returning an implementation of some other vtable. For example, some of PuTTY's crypto primitives have a hardware-accelerated version and a pure software version, and decide at run time which one to use (based on whether the CPU they're running on supports the necessary acceleration instructions). So, for example, there are vtables for ssh_sha256_sw and ssh_sha256_hw, each of which has its own data layout and its own implementations of all the methods; and then there's a top-level vtable ssh_sha256, which only provides the ‘new’ method, and implements it by calling the ‘new’ method on one or other of the subtypes depending on what it finds out about the machine it's running on. That top-level selector vtable is nearly always the one used by client code. (Except for the test suite, which has to instantiate both of the subtypes in order to make sure they both pass the tests.) +

      +As a result, the top-level selector vtable ssh_sha256 doesn't need to implement any method that takes an ssh_cipher * parameter, because no ssh_cipher object is ever constructed whose vt field points to &ssh_sha256: they all point to one of the other two full implementation vtables. +

      + +
    • +
    +

    E.14 Single compilation of each source file

    +

    +The PuTTY build system for any given platform works on the following very simple model: +

    +
    • +Each source file is compiled precisely once, to produce a single object file. +
    • +
    • +Each binary is created by linking together some combination of those object files. +
    • +

    -Each release is signed with the Release Key that was current at the time of release. We don't go back and re-sign old releases with newly generated keys. +Therefore, if you need to introduce functionality to a particular module which is only available in some of the tool binaries (for example, a cryptographic proxy authentication mechanism which needs to be left out of PuTTYtel to maintain its usability in crypto-hostile jurisdictions), the wrong way to do it is by adding #ifdefs in (say) proxy.c. This would require separate compilation of proxy.c for PuTTY and PuTTYtel, which means that the entire Makefile-generation architecture (see section E.11) would have to be significantly redesigned. Unless you are prepared to do that redesign yourself, and guarantee that it will still port to any future platforms we might decide to run on, you should not attempt this!

    -The details of all previous keys are given here. +The right way to introduce a feature like this is to put the new code in a separate source file, and (if necessary) introduce a second new source file defining the same set of functions, but defining them as stubs which don't provide the feature. Then the module whose behaviour needs to vary (proxy.c in this example) can call the functions defined in these two modules, and it will either provide the new feature or not provide it according to which of your new modules it is linked with.

    -Key generated in 2016 (when we first introduced the Secure Contact Key) +Of course, object files are never shared between platforms; so it is allowable to use #ifdef to select between platforms. This happens in puttyps.h (choosing which of the platform-specific include files to use), and also in misc.c (the Windows-specific ‘Minefield’ memory diagnostic system). It should be used sparingly, though, if at all.

    -
    -Secure Contact Key (2016) -
    -
    -RSA, 2048-bit. Main key ID: 2048R/8A0AF00B (long version: 2048R/C4FCAAD08A0AF00B). Encryption subkey ID: 2048R/50C2CF5C (long version: 2048R/9EB39CC150C2CF5C). Fingerprint: 8A26 250E 763F E359 75F3  118F C4FC AAD0 8A0A F00B -
    -
    +

    E.15 Do as we say, not as we do

    -Keys generated in the 2015 rollover +The current PuTTY code probably does not conform strictly to all of the principles listed above. There may be the occasional SSH-specific piece of code in what should be a backend-independent module, or the occasional dependence on a non-standard X library function under Unix.

    -
    -Master Key (2015) -
    -
    -RSA, 4096-bit. Key ID: 4096R/04676F7C (long version: 4096R/AB585DC604676F7C). Fingerprint: 440D E3B5 B7A1 CA85 B3CC  1718 AB58 5DC6 0467 6F7C -
    -
    -Release Key (2015) -
    -
    -RSA, 2048-bit. Key ID: 2048R/B43434E4 (long version: 2048R/9DFE2648B43434E4). Fingerprint: 0054 DDAA 8ADA 15D2 768A  6DE7 9DFE 2648 B434 34E4 -
    -
    -Snapshot Key (2015) -
    -
    -RSA, 2048-bit. Key ID: 2048R/D15F7E8A (long version: 2048R/EEF20295D15F7E8A). Fingerprint: 0A3B 0048 FE49 9B67 A234  FEB6 EEF2 0295 D15F 7E8A -
    -

    -Original keys generated in 2000 (two sets, RSA and DSA) +This should not be taken as a licence to go ahead and violate the rules. Where we violate them ourselves, we're not happy about it, and we would welcome patches that fix any existing problems. Please try to help us make our code better, not worse!

    -
    -Master Key (original RSA) -
    -
    -RSA, 1024-bit. Key ID: 1024R/1E34AC41 (long version: 1024R/9D5877BF1E34AC41). Fingerprint: 8F 15 97 DA 25 30 AB 0D  88 D1 92 54 11 CF 0C 4C -
    -
    -Master Key (original DSA) -
    -
    -DSA, 1024-bit. Key ID: 1024D/6A93B34E (long version: 1024D/4F5E6DF56A93B34E). Fingerprint: 313C 3E76 4B74 C2C5 F2AE  83A8 4F5E 6DF5 6A93 B34E -
    -
    -Release Key (original RSA) -
    -
    -RSA, 1024-bit. Key ID: 1024R/B41CAE29 (long version: 1024R/EF39CCC0B41CAE29). Fingerprint: AE 65 D3 F7 85 D3 18 E0  3B 0C 9B 02 FF 3A 81 FE -
    -
    -Release Key (original DSA) -
    -
    -DSA, 1024-bit. Key ID: 1024D/08B0A90B (long version: 1024D/FECD6F3F08B0A90B). Fingerprint: 00B1 1009 38E6 9800 6518  F0AB FECD 6F3F 08B0 A90B -
    -
    -Snapshot Key (original RSA) -
    -
    -RSA, 1024-bit. Key ID: 1024R/32B903A9 (long version: 1024R/FAAED21532B903A9). Fingerprint: 86 8B 1F 79 9C F4 7F BD  8B 1B D7 8E C6 4E 4C 03 -
    -
    -Snapshot Key (original DSA) -
    -
    -DSA, 1024-bit. Key ID: 1024D/7D3E4A00 (long version: 1024D/165E56F77D3E4A00). Fingerprint: 63DD 8EF8 32F5 D777 9FF0  2947 165E 56F7 7D3E 4A00 -
    -

    If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

    -[PuTTY release 0.73]
    +[PuTTY release 0.76] diff --git a/doc/AppendixF.html b/doc/AppendixF.html index 63d15d3..1374e4c 100644 --- a/doc/AppendixF.html +++ b/doc/AppendixF.html @@ -3,99 +3,253 @@ -SSH-2 names specified for PuTTY +PuTTY download keys and signatures - + -

    Previous | Contents | Index | Next

    +

    Previous | Contents | Index | Next

    -

    Appendix F: SSH-2 names specified for PuTTY

    +

    Appendix F: PuTTY download keys and signatures

    +

    +We create GPG signatures for all the PuTTY files distributed from our web site, so that users can be confident that the files have not been tampered with. Here we identify our public keys, and explain our signature policy so you can have an accurate idea of what each signature guarantees. This description is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual. +

    -There are various parts of the SSH-2 protocol where things are specified using a textual name. Names ending in @putty.projects.tartarus.org are reserved for allocation by the PuTTY team. Allocated names are documented here. +As of release 0.58, all of the PuTTY executables contain fingerprint material (usually accessed via the -pgpfp command-line option), such that if you have an executable you trust, you can use it to establish a trust path, for instance to a newer version downloaded from the Internet.

    -

    F.1 Connection protocol channel request names

    -These names can be sent in a SSH_MSG_CHANNEL_REQUEST message. +As of release 0.67, the Windows executables and installer also contain built-in signatures that are automatically verified by Windows' own mechanism (‘Authenticode’). The keys used for that are different, and are not covered here. +

    +

    +(Note that none of the keys, signatures, etc mentioned here have anything to do with keys used with SSH - they are purely for verifying the origin of files distributed by the PuTTY team.) +

    +

    F.1 Public keys

    +

    +We maintain multiple keys, stored with different levels of security due to being used in different ways. See section F.2 below for details. +

    +

    +The keys we provide are:

    -simple@putty.projects.tartarus.org +Snapshot Key
    -This is sent by a client to announce that it will not have more than one channel open at a time in the current connection (that one being the one the request is sent on). The intention is that the server, knowing this, can set the window on that one channel to something very large, and leave flow control to TCP. There is no message-specific data. +Used to sign routine development builds of PuTTY: nightly snapshots, pre-releases, and sometimes also custom diagnostic builds we send to particular users.
    -winadj@putty.projects.tartarus.org +Release Key
    -PuTTY sends this request along with some SSH_MSG_CHANNEL_WINDOW_ADJUST messages as part of its window-size tuning. It can be sent on any type of channel. There is no message-specific data. Servers MUST treat it as an unrecognised request and respond with SSH_MSG_CHANNEL_FAILURE. -

    -(Some SSH servers get confused by this message, so there is a bug-compatibility mode for disabling it. See section 4.28.3.) -

    - +Used to sign manually released versions of PuTTY. +
    +
    +Secure Contact Key +
    +
    +An encryption-capable key suitable for people to send confidential messages to the PuTTY team, e.g. reports of vulnerabilities. +
    +
    +Master Key +
    +
    +Used to tie all the above keys into the GPG web of trust. The Master Key signs all the other keys, and other GPG users have signed it in turn.
    -

    F.2 Key exchange method names

    +

    +The current issue of those keys are available for download from the PuTTY website, and are also available on PGP keyservers using the key IDs listed below. +

    -rsa-sha1-draft-00@putty.projects.tartarus.org +Master Key (2018)
    +
    +RSA, 4096-bit. Key ID: 76BC7FE4EBFD2D9E. Fingerprint: 24E1 B1C5 75EA 3C9F F752  A922 76BC 7FE4 EBFD 2D9E +
    -rsa-sha256-draft-00@putty.projects.tartarus.org +Release Key (2018)
    +
    +RSA, 3072-bit. Key ID: 6289A25F4AE8DA82. Fingerprint: E273 94AC A3F9 D904 9522  E054 6289 A25F 4AE8 DA82 +
    -rsa1024-sha1-draft-01@putty.projects.tartarus.org +Snapshot Key (2018)
    +
    +RSA, 3072-bit. Key ID: 38BA7229B7588FD1. Fingerprint: C92B 52E9 9AB6 1DDA 33DB  2B7A 38BA 7229 B758 8FD1 +
    -rsa1024-sha256-draft-01@putty.projects.tartarus.org +Secure Contact Key (2018)
    -
    -rsa2048-sha256-draft-01@putty.projects.tartarus.org +
    +RSA, 3072-bit. Key ID: 657D487977F95C98. Fingerprint: A680 0082 2998 6E46 22CA  0E43 657D 4879 77F9 5C98 +
    +
    +

    F.2 Security details

    +

    +The various keys have various different security levels. This section explains what those security levels are, and how far you can expect to trust each key. +

    +

    F.2.1 The Development Snapshots key

    +

    +The Development Snapshots private key is stored without a passphrase. This is necessary, because the snapshots are generated every night without human intervention, so nobody would be able to type a passphrase. +

    +

    +The snapshots are built and signed on a team member's home computers, before being uploaded to the web server from which you download them. +

    +

    +Therefore, a signature from the Development Snapshots key DOES protect you against: +

    +
    • +People tampering with the PuTTY binaries between the PuTTY web site and you. +
    • +
    • +The maintainers of our web server attempting to abuse their root privilege to tamper with the binaries. +
    • +
    +

    +But it DOES NOT protect you against: +

    +
    • +People tampering with the binaries before they are uploaded to our download servers. +
    • +
    • +People tampering with the build machines so that the next set of binaries they build will be malicious in some way. +
    • +
    • +People stealing the unencrypted private key from the build machine it lives on. +
    • +
    +

    +Of course, we take all reasonable precautions to guard the build machines. But when you see a signature, you should always be certain of precisely what it guarantees and precisely what it does not. +

    +

    F.2.2 The Releases key

    +

    +The Releases key is more secure: because it is only used at release time, to sign each release by hand, we can store it encrypted. +

    +

    +The Releases private key is kept encrypted on the developers' own local machines. So an attacker wanting to steal it would have to also steal the passphrase. +

    +

    F.2.3 The Secure Contact Key

    +

    +The Secure Contact Key is stored with a similar level of security to the Release Key: it is stored with a passphrase, and no automated script has access to it. +

    +

    F.2.4 The Master Keys

    +

    +The Master Key signs almost nothing. Its purpose is to bind the other keys together and certify that they are all owned by the same people and part of the same integrated setup. The only signatures produced by the Master Key, ever, should be the signatures on the other keys. +

    +

    +The Master Key is especially long, and its private key and passphrase are stored with special care. +

    +

    +We have collected some third-party signatures on the Master Key, in order to increase the chances that you can find a suitable trust path to them. +

    +

    +We have uploaded our various keys to public keyservers, so that even if you don't know any of the people who have signed our keys, you can still be reasonably confident that an attacker would find it hard to substitute fake keys on all the public keyservers at once. +

    +

    F.3 Key rollover

    +

    +Our current keys were generated in August 2018. +

    +

    +Each new Master Key is signed with the old one, to show that it really is owned by the same people and not substituted by an attacker. +

    +

    +Each new Master Key also signs the previous Release Keys, in case you're trying to verify the signatures on a release prior to the rollover and can find a chain of trust to those keys from any of the people who have signed our new Master Key. +

    +

    +Each release is signed with the Release Key that was current at the time of release. We don't go back and re-sign old releases with newly generated keys. +

    +

    +The details of all previous keys are given here. +

    +

    +Key generated in 2016 (when we first introduced the Secure Contact Key) +

    +
    +Secure Contact Key (2016)
    -
    -rsa1024-sha1-draft-02@putty.projects.tartarus.org +
    +RSA, 2048-bit. Main key ID: 2048R/8A0AF00B (long version: 2048R/C4FCAAD08A0AF00B). Encryption subkey ID: 2048R/50C2CF5C (long version: 2048R/9EB39CC150C2CF5C). Fingerprint: 8A26 250E 763F E359 75F3  118F C4FC AAD0 8A0A F00B +
    +
    +

    +Keys generated in the 2015 rollover +

    +
    +Master Key (2015)
    +
    +RSA, 4096-bit. Key ID: 4096R/04676F7C (long version: 4096R/AB585DC604676F7C). Fingerprint: 440D E3B5 B7A1 CA85 B3CC  1718 AB58 5DC6 0467 6F7C +
    -rsa2048-sha512-draft-02@putty.projects.tartarus.org +Release Key (2015)
    +
    +RSA, 2048-bit. Key ID: 2048R/B43434E4 (long version: 2048R/9DFE2648B43434E4). Fingerprint: 0054 DDAA 8ADA 15D2 768A  6DE7 9DFE 2648 B434 34E4 +
    -rsa1024-sha1-draft-03@putty.projects.tartarus.org +Snapshot Key (2015)
    +
    +RSA, 2048-bit. Key ID: 2048R/D15F7E8A (long version: 2048R/EEF20295D15F7E8A). Fingerprint: 0A3B 0048 FE49 9B67 A234  FEB6 EEF2 0295 D15F 7E8A +
    +
    +

    +Original keys generated in 2000 (two sets, RSA and DSA) +

    +
    +Master Key (original RSA) +
    +
    +RSA, 1024-bit. Key ID: 1024R/1E34AC41 (long version: 1024R/9D5877BF1E34AC41). Fingerprint: 8F 15 97 DA 25 30 AB 0D  88 D1 92 54 11 CF 0C 4C +
    -rsa2048-sha256-draft-03@putty.projects.tartarus.org +Master Key (original DSA)
    +
    +DSA, 1024-bit. Key ID: 1024D/6A93B34E (long version: 1024D/4F5E6DF56A93B34E). Fingerprint: 313C 3E76 4B74 C2C5 F2AE  83A8 4F5E 6DF5 6A93 B34E +
    -rsa1024-sha1-draft-04@putty.projects.tartarus.org +Release Key (original RSA)
    +
    +RSA, 1024-bit. Key ID: 1024R/B41CAE29 (long version: 1024R/EF39CCC0B41CAE29). Fingerprint: AE 65 D3 F7 85 D3 18 E0  3B 0C 9B 02 FF 3A 81 FE +
    -rsa2048-sha256-draft-04@putty.projects.tartarus.org +Release Key (original DSA)
    -These appeared in various drafts of what eventually became RFC 4432. They have been superseded by rsa1024-sha1 and rsa2048-sha256. +DSA, 1024-bit. Key ID: 1024D/08B0A90B (long version: 1024D/FECD6F3F08B0A90B). Fingerprint: 00B1 1009 38E6 9800 6518  F0AB FECD 6F3F 08B0 A90B
    -
    -

    F.3 Encryption algorithm names

    -
    -arcfour128-draft-00@putty.projects.tartarus.org +
    +Snapshot Key (original RSA)
    +
    +RSA, 1024-bit. Key ID: 1024R/32B903A9 (long version: 1024R/FAAED21532B903A9). Fingerprint: 86 8B 1F 79 9C F4 7F BD  8B 1B D7 8E C6 4E 4C 03 +
    -arcfour256-draft-00@putty.projects.tartarus.org +Snapshot Key (original DSA)
    -These were used in drafts of what eventually became RFC 4345. They have been superseded by arcfour128 and arcfour256. +DSA, 1024-bit. Key ID: 1024D/7D3E4A00 (long version: 1024D/165E56F77D3E4A00). Fingerprint: 63DD 8EF8 32F5 D777 9FF0  2947 165E 56F7 7D3E 4A00

    If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

    -[PuTTY release 0.73]
    +[PuTTY release 0.76] diff --git a/doc/Chapter1.html b/doc/Chapter1.html index 208a4ce..601feb1 100644 --- a/doc/Chapter1.html +++ b/doc/Chapter1.html @@ -16,38 +16,38 @@

    Chapter 1: Introduction to PuTTY

    -PuTTY is a free SSH, Telnet and Rlogin client for Windows systems. +PuTTY is a free SSH, Telnet, Rlogin, and SUPDUP client for Windows systems.

    -

    1.1 What are SSH, Telnet and Rlogin?

    +

    1.1 What are SSH, Telnet, Rlogin, and SUPDUP?

    -If you already know what SSH, Telnet and Rlogin are, you can safely skip on to the next section. +If you already know what SSH, Telnet, Rlogin, and SUPDUP are, you can safely skip on to the next section.

    -SSH, Telnet and Rlogin are three ways of doing the same thing: logging in to a multi-user computer from another computer, over a network. +SSH, Telnet, Rlogin, and SUPDUP are four ways of doing the same thing: logging in to a multi-user computer from another computer, over a network.

    -Multi-user operating systems, such as Unix and VMS, usually present a command-line interface to the user, much like the ‘Command Prompt’ or ‘MS-DOS Prompt’ in Windows. The system prints a prompt, and you type commands which the system will obey. +Multi-user operating systems, typically of the Unix family (such as Linux, MacOS, and the BSD family), usually present a command-line interface to the user, much like the ‘Command Prompt’ or ‘MS-DOS Prompt’ in Windows. The system prints a prompt, and you type commands which the system will obey.

    Using this type of interface, there is no need for you to be sitting at the same machine you are typing commands to. The commands, and responses, can be sent over a network, so you can sit at one computer and give commands to another one, or even to more than one.

    -SSH, Telnet and Rlogin are network protocols that allow you to do this. On the computer you sit at, you run a client, which makes a network connection to the other computer (the server). The network connection carries your keystrokes and commands from the client to the server, and carries the server's responses back to you. +SSH, Telnet, Rlogin, and SUPDUP are network protocols that allow you to do this. On the computer you sit at, you run a client, which makes a network connection to the other computer (the server). The network connection carries your keystrokes and commands from the client to the server, and carries the server's responses back to you.

    These protocols can also be used for other types of keyboard-based interactive session. In particular, there are a lot of bulletin boards, talker systems and MUDs (Multi-User Dungeons) which support access using Telnet. There are even a few that support SSH.

    -You might want to use SSH, Telnet or Rlogin if: +You might want to use SSH, Telnet, Rlogin, or SUPDUP if:

    • -you have an account on a Unix or VMS system which you want to be able to access from somewhere else +you have an account on a Unix system (or some other multi-user OS such as VMS or ITS) which you want to be able to access from somewhere else
    • your Internet Service Provider provides you with a login account on a web server. (This might also be known as a shell account. A shell is the program that runs on the server and interprets your commands for you.) @@ -57,18 +57,18 @@

      1.1 What are SSH, Telnet and Rlogi

    -You probably do not want to use SSH, Telnet or Rlogin if: +You probably do not want to use SSH, Telnet, Rlogin, or SUPDUP if:

    • you only use Windows. Windows computers have their own ways of networking between themselves, and unless you are doing something fairly unusual, you will not need to use any of these remote login protocols.
    -

    1.2 How do SSH, Telnet and Rlogin differ?

    +

    1.2 How do SSH, Telnet, Rlogin, and SUPDUP differ?

    -This list summarises some of the differences between SSH, Telnet and Rlogin. +This list summarises some of the differences between SSH, Telnet, Rlogin, and SUPDUP.

    • -SSH (which stands for ‘secure shell’) is a recently designed, high-security protocol. It uses strong cryptography to protect your connection against eavesdropping, hijacking and other attacks. Telnet and Rlogin are both older protocols offering minimal security. +SSH (which stands for ‘secure shell’) is a recently designed, high-security protocol. It uses strong cryptography to protect your connection against eavesdropping, hijacking and other attacks. Telnet, Rlogin, and SUPDUP are all older protocols offering minimal security.
    • SSH and Rlogin both allow you to log in to the server without having to type a password. (Rlogin's method of doing this is insecure, and can allow an attacker to access your account on the server. SSH's method is much more secure, and typically breaking the security requires the attacker to have gained access to your actual client machine.) @@ -81,9 +81,9 @@

      1.2 How do SSH, Telnet and Rlogin The Internet is a hostile environment and security is everybody's responsibility. If you are connecting across the open Internet, then we recommend you use SSH. If the server you want to connect to doesn't support SSH, it might be worth trying to persuade the administrator to install it.

      -If your client and server are both behind the same (good) firewall, it is more likely to be safe to use Telnet or Rlogin, but we still recommend you use SSH. +If your client and server are both behind the same (good) firewall, it is more likely to be safe to use Telnet, Rlogin, or SUPDUP, but we still recommend you use SSH.


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/Chapter10.html b/doc/Chapter10.html index ebcacba..917f775 100644 --- a/doc/Chapter10.html +++ b/doc/Chapter10.html @@ -75,7 +75,7 @@

      10.3 ‘SSH protoc By default, PuTTY only supports connecting to SSH servers that implement SSH protocol version 2. If you see this message, the server you're trying to connect to only supports the older SSH-1 protocol.

      -If the server genuinely only supports SSH-1, then you need to either change the ‘SSH protocol version’ setting (see section 4.19.4), or use the -1 command-line option; in any case, you should not treat the resulting connection as secure. +If the server genuinely only supports SSH-1, then you need to either change the ‘SSH protocol version’ setting (see section 4.17.4), or use the -1 command-line option; in any case, you should not treat the resulting connection as secure.

      You might start seeing this message with new versions of PuTTY (from 0.68 onwards) where you didn't before, because it used to be possible to configure PuTTY to automatically fall back from SSH-2 to SSH-1. This is no longer supported, to prevent the possibility of a downgrade attack. @@ -85,7 +85,7 @@

      10.4 ‘The firs This occurs when the SSH server does not offer any ciphers which you have configured PuTTY to consider strong enough. By default, PuTTY puts up this warning only for Blowfish, single-DES, and Arcfour encryption.

      -See section 4.22 for more information on this message. +See section 4.20 for more information on this message.

      (There are similar messages for other cryptographic primitives, such as host key algorithms.) @@ -95,7 +95,7 @@

      10.5 ‘Remote side This message is produced by an OpenSSH (or Sun SSH) server if it receives more failed authentication attempts than it is willing to tolerate.

      -This can easily happen if you are using Pageant and have a large number of keys loaded into it, since these servers count each offer of a public key as an authentication attempt. This can be worked around by specifying the key that's required for the authentication in the PuTTY configuration (see section 4.23.8); PuTTY will ignore any other keys Pageant may have, but will ask Pageant to do the authentication, so that you don't have to type your passphrase. +This can easily happen if you are using Pageant and have a large number of keys loaded into it, since these servers count each offer of a public key as an authentication attempt. This can be worked around by specifying the key that's required for the authentication in the PuTTY configuration (see section 4.21.9); PuTTY will ignore any other keys Pageant may have, but will ask Pageant to do the authentication, so that you don't have to type your passphrase.

      On the server, this can be worked around by disabling public-key authentication or (for Sun SSH only) by increasing MaxAuthTries in sshd_config. @@ -128,7 +128,7 @@

      10.8 ‘Unable to If you see one of these messages, it often indicates that you've tried to load a key of an inappropriate type into PuTTY, Plink, PSCP, PSFTP, or Pageant.

      -You may have tried to load an SSH-2 key in a ‘foreign’ format (OpenSSH or ssh.com) directly into one of the PuTTY tools, in which case you need to import it into PuTTY's native format (*.PPK) using PuTTYgen – see section 8.2.12. +You may have tried to load an SSH-2 key in a ‘foreign’ format (OpenSSH or ssh.com) directly into one of the PuTTY tools, in which case you need to import it into PuTTY's native format (*.PPK) using PuTTYgen – see section 8.2.14.

      Alternatively, you may have specified a key that's inappropriate for the connection you're making. The SSH-2 and the old SSH-1 protocols require different private key formats, and a SSH-1 key can't be used for a SSH-2 connection (or vice versa). @@ -157,11 +157,11 @@

      10.10 ‘Access It may be worth checking the Event Log for diagnostic messages from the server giving more detail.

      -This error can be caused by buggy SSH-1 servers that fail to cope with the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in section 4.28.11 and possibly section 4.28.12. +This error can be caused by buggy SSH-1 servers that fail to cope with the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in section 4.26.11 and possibly section 4.26.12.

      10.11 ‘No supported authentication methods available’

      -This error indicates that PuTTY has run out of ways to authenticate you to an SSH server. This may be because PuTTY has TIS or keyboard-interactive authentication disabled, in which case see section 4.23.4 and section 4.23.5. +This error indicates that PuTTY has run out of ways to authenticate you to an SSH server. This may be because PuTTY has TIS or keyboard-interactive authentication disabled, in which case see section 4.21.5 and section 4.21.6.

      10.12 ‘Incorrect MAC received on packet’ or ‘Incorrect CRC received on packet’

      @@ -171,7 +171,7 @@

      10.12 ‘Incorrect

      -Occasionally this has been caused by server bugs. An example is the bug described at section 4.28.8, although you're very unlikely to encounter that one these days. +Occasionally this has been caused by server bugs. An example is the bug described at section 4.26.8, although you're very unlikely to encounter that one these days.

      In this context MAC stands for Message Authentication Code. It's a cryptographic term, and it has nothing at all to do with Ethernet MAC (Media Access Control) addresses, or with the Apple computer. @@ -181,7 +181,7 @@

      10.13 ‘Incoming pack This error occurs when PuTTY decrypts an SSH packet and the decrypted data makes no sense. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between.

      -If you get this error, one thing you could try would be to fiddle with the setting of ‘Miscomputes SSH-2 encryption keys’ (see section 4.28.10) or ‘Ignores SSH-2 maximum packet size’ (see section 4.28.5) on the Bugs panel. +If you get this error, one thing you could try would be to fiddle with the setting of ‘Miscomputes SSH-2 encryption keys’ (see section 4.26.10) or ‘Ignores SSH-2 maximum packet size’ (see section 4.26.5) on the Bugs panel.

      10.14 ‘PuTTY X11 proxy: various errors

      @@ -201,7 +201,7 @@

      10.15 ‘Network e This is a generic error produced by the Windows network code when it kills an established connection for some reason. For example, it might happen if you pull the network cable out of the back of an Ethernet-connected computer, or if Windows has any other similar reason to believe the entire network has become unreachable.

      -Windows also generates this error if it has given up on the machine at the other end of the connection ever responding to it. If the network between your client and server goes down and your client then tries to send some data, Windows will make several attempts to send the data and will then give up and kill the connection. In particular, this can occur even if you didn't type anything, if you are using SSH-2 and PuTTY attempts a key re-exchange. (See section 4.20.2 for more about key re-exchange.) +Windows also generates this error if it has given up on the machine at the other end of the connection ever responding to it. If the network between your client and server goes down and your client then tries to send some data, Windows will make several attempts to send the data and will then give up and kill the connection. In particular, this can occur even if you didn't type anything, if you are using SSH-2 and PuTTY attempts a key re-exchange. (See section 4.18.2 for more about key re-exchange.)

      (It can also occur if you are using keepalives in your connection. Other people have reported that keepalives fix this error for them. See section 4.14.1 for a discussion of the pros and cons of keepalives.) @@ -224,7 +224,7 @@

      10.17 ‘Network e This error means that the network connection PuTTY tried to make to your server was rejected by the server. Usually this happens because the server does not provide the service which PuTTY is trying to access.

      -Check that you are connecting with the correct protocol (SSH, Telnet or Rlogin), and check that the port number is correct. If that fails, consult the administrator of your server. +Check that you are connecting with the correct protocol (SSH, Telnet, etc), and check that the port number is correct. If that fails, consult the administrator of your server.

      10.18 ‘Network error: Connection timed out’

      @@ -234,7 +234,7 @@

      10.18 ‘Network Check that you have correctly entered the host name or IP address of your server machine. If that fails, consult the administrator of your server.

      -Unix also generates this error when it tries to send data down a connection and contact with the server has been completely lost during a connection. (There is a delay of minutes before Unix gives up on receiving a reply from the server.) This can occur if you type things into PuTTY while the network is down, but it can also occur if PuTTY decides of its own accord to send data: due to a repeat key exchange in SSH-2 (see section 4.20.2) or due to keepalives (section 4.14.1). +Unix also generates this error when it tries to send data down a connection and contact with the server has been completely lost during a connection. (There is a delay of minutes before Unix gives up on receiving a reply from the server.) This can occur if you type things into PuTTY while the network is down, but it can also occur if PuTTY decides of its own accord to send data: due to a repeat key exchange in SSH-2 (see section 4.18.2) or due to keepalives (section 4.14.1).

      10.19 ‘Network error: Cannot assign requested address’

      @@ -245,5 +245,5 @@

      10.19 ‘N


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/Chapter2.html b/doc/Chapter2.html index de7c724..6f33796 100644 --- a/doc/Chapter2.html +++ b/doc/Chapter2.html @@ -38,82 +38,86 @@

      2.1 Starting a In the ‘Host Name’ box, enter the Internet host name of the server you want to connect to. You should have been told this by the provider of your login account.

      -Now select a login protocol to use, from the ‘Connection type’ buttons. For a login session, you should select Telnet, Rlogin or SSH. See section 1.2 for a description of the differences between the three protocols, and advice on which one to use. The fourth protocol, Raw, is not used for interactive login sessions; you would usually use this for debugging other Internet services (see section 3.6). The fifth option, Serial, is used for connecting to a local serial line, and works somewhat differently: see section 3.7 for more information on this. +Now select a login protocol to use, from the ‘Connection type’ controls. For a login session, you should select SSH, Telnet, Rlogin, or SUPDUP. See section 1.2 for a description of the differences between these protocols, and advice on which one to use. The Raw protocol is not used for interactive login sessions; you would usually use this for debugging other Internet services (see section 3.7). The Serial option is used for connecting to a local serial line, and works somewhat differently: see section 3.6 for more information on this.

      -When you change the selected protocol, the number in the ‘Port’ box will change. This is normal: it happens because the various login services are usually provided on different network ports by the server machine. Most servers will use the standard port numbers, so you will not need to change the port setting. If your server provides login services on a non-standard port, your system administrator should have told you which one. (For example, many MUDs run Telnet service on a port other than 23.) +When you change the selected protocol, the number in the ‘Port’ box will change. This is normal: it happens because the various login services are usually provided on different network ports by the server machine. Most servers will use the standard port numbers, so you will not need to change the port setting. If your server provides login services on a non-standard port, your system administrator should have told you which one. (For example, many MUDs run Telnet service on a port other than 23.)

      -Once you have filled in the ‘Host Name’, ‘Protocol’, and possibly ‘Port’ settings, you are ready to connect. Press the ‘Open’ button at the bottom of the dialog box, and PuTTY will begin trying to connect you to the server. +Once you have filled in the ‘Host Name’, ‘Connection type’, and possibly ‘Port’ settings, you are ready to connect. Press the ‘Open’ button at the bottom of the dialog box, and PuTTY will begin trying to connect you to the server.

      -

      2.2 Verifying the host key (SSH only)

      +

      2.2 Verifying the host key (SSH only)

      -If you are not using the SSH protocol, you can skip this section. +If you are not using the SSH protocol, you can skip this section.

      If you are using SSH to connect to a server for the first time, you will probably see a message looking something like this:

      -
      The server's host key is not cached in the registry. You
      -have no guarantee that the server is the computer you
      -think it is.
      -The server's rsa2 key fingerprint is:
      -ssh-rsa 1024 7b:e5:6f:a7:f4:f9:81:62:5c:e3:1f:bf:8b:57:6c:5a
      -If you trust this host, hit Yes to add the key to
      -PuTTY's cache and carry on connecting.
      -If you want to carry on connecting just once, without
      -adding the key to the cache, hit No.
      -If you do not trust this host, hit Cancel to abandon the
      -connection.
      +
      The server's host key is not cached in the registry. You have no
      +guarantee that the server is the computer you think it is.
      +The server's ssh-ed25519 key fingerprint is:
      + ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w
      +If you trust this host, press "Accept" to add the key to PuTTY's
      +cache and carry on connecting.
      +If you want to carry on connecting just once, without adding the key
      +to the cache, press "Connect Once".
      +If you do not trust this host, press "Cancel" to abandon the connection.
       

      -This is a feature of the SSH protocol. It is designed to protect you against a network attack known as spoofing: secretly redirecting your connection to a different computer, so that you send your password to the wrong machine. Using this technique, an attacker would be able to learn the password that guards your login account, and could then log in as if they were you and use the account for their own purposes. +This is a feature of the SSH protocol. It is designed to protect you against a network attack known as spoofing: secretly redirecting your connection to a different computer, so that you send your password to the wrong machine. Using this technique, an attacker would be able to learn the password that guards your login account, and could then log in as if they were you and use the account for their own purposes.

      To prevent this attack, each server has a unique identifying code, called a host key. These keys are created in a way that prevents one server from forging another server's key. So if you connect to a server and it sends you a different host key from the one you were expecting, PuTTY can warn you that the server may have been switched and that a spoofing attack might be in progress.

      -PuTTY records the host key for each server you connect to, in the Windows Registry. Every time you connect to a server, it checks that the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you type any private information (such as a password) into it. +PuTTY records the host key for each server you connect to, in the Windows Registry. Every time you connect to a server, it checks that the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you type any private information (such as a password) into it. (See section 10.2 for what that looks like.)

      -However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the right one or not. So it gives the warning shown above, and asks you whether you want to trust this host key or not. +However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the right one or not. So it gives the warning shown above, and asks you whether you want to trust this host key or not.

      -Whether or not to trust the host key is your choice. If you are connecting within a company network, you might feel that all the network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the Internet), you should check with your system administrator, perhaps by telephone or in person. (Many servers have more than one host key. If the system administrator sends you more than one fingerprint, you should make sure the one PuTTY shows you is on the list, but it doesn't matter which one it is.) +Whether or not to trust the host key is your choice. If you are connecting within a company network, you might feel that all the network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the Internet), you should check with your system administrator, perhaps by telephone or in person. (When verifying the fingerprint, be careful with letters and numbers that can be confused with each other: 0/O, 1/I/l, and so on.)

      -See section 4.21 for advanced options for managing host keys. +Many servers have more than one host key. If the system administrator sends you more than one fingerprint, you should make sure the one PuTTY shows you is on the list, but it doesn't matter which one it is.

      -

      2.3 Logging in

      -After you have connected, and perhaps verified the server's host key, you will be asked to log in, probably using a username and a password. Your system administrator should have provided you with these. (If, instead, your system administrator has asked you to provide, or provided you with, a ‘public key’ or ‘key file’, see chapter 8.) +If you don't have any fingerprints that look like the example (SHA256: followed by a long string of characters), but instead have pairs of characters separated by colons like a4:db:96:a7:..., try pressing the ‘More info...’ button and see if you have a fingerprint matching the ‘MD5 fingerprint’ there. This is an older and less secure way to summarise the same underlying host key; it's possible for an attacker to create their own host key with the same fingerprint; so you should avoid relying on this fingerprint format unless you have no choice. The ‘More info...’ dialog box also shows the full host public key, in case that is easier to compare than a fingerprint.

      -PuTTY will display a text window (the ‘terminal window’ – it will have a black background unless you've changed the defaults), and prompt you to type your username and password into that window. (These prompts will include the PuTTY icon, to distinguish them from any text sent by the server in the same window.) +See section 4.19 for advanced options for managing host keys.

      +

      2.3 Logging in

      -Enter the username and the password, and the server should grant you access and begin your session. If you have mistyped your password, most servers will give you several chances to get it right. +After you have connected, and perhaps verified the server's host key, you will be asked to log in, probably using a username and a password. Your system administrator should have provided you with these. (If, instead, your system administrator has asked you to provide, or provided you with, a ‘public key’ or ‘key file’, see chapter 8.) +

      +

      +PuTTY will display a text window (the ‘terminal window’ – it will have a black background unless you've changed the defaults), and prompt you to type your username and password into that window. (These prompts will include the PuTTY icon, to distinguish them from any text sent by the server in the same window.) +

      +

      +Enter the username and the password, and the server should grant you access and begin your session. If you have mistyped your password, most servers will give you several chances to get it right.

      While you are typing your password, you will not usually see the cursor moving in the window, but PuTTY is registering what you type, and will send it when you press Return. (It works this way to avoid revealing the length of your password to anyone watching your screen.)

      -If you are using SSH, be careful not to type your username wrongly, because you will not have a chance to correct it after you press Return; many SSH servers do not permit you to make two login attempts using different usernames. If you type your username wrongly, you must close PuTTY and start again. +If you are using SSH, be careful not to type your username wrongly, because you will not have a chance to correct it after you press Return; many SSH servers do not permit you to make two login attempts using different usernames. If you type your username wrongly, you must close PuTTY and start again.

      If your password is refused but you are sure you have typed it correctly, check that Caps Lock is not enabled. Many login servers, particularly Unix computers, treat upper case and lower case as different when checking your password; so if Caps Lock is on, your password will probably be refused.

      2.4 After logging in

      -After you log in to the server, what happens next is up to the server! Most servers will print some sort of login message and then present a prompt, at which you can type commands which the server will carry out. Some servers will offer you on-line help; others might not. If you are in doubt about what to do next, consult your system administrator. +After you log in to the server, what happens next is up to the server! Most servers will print some sort of login message and then present a prompt, at which you can type commands which the server will carry out. Some servers will offer you on-line help; others might not. If you are in doubt about what to do next, consult your system administrator.

      -

      2.5 Logging out

      +

      2.5 Logging out

      When you have finished your session, you should log out by typing the server's own logout command. This might vary between servers; if in doubt, try logout or exit, or consult a manual or your system administrator. When the server processes your logout command, the PuTTY window should close itself automatically.

      -You can close a PuTTY session using the Close button in the window border, but this might confuse the server - a bit like hanging up a telephone unexpectedly in the middle of a conversation. We recommend you do not do this unless the server has stopped responding to you and you cannot close the window any other way. +You can close a PuTTY session using the Close button in the window border, but this might confuse the server - a bit like hanging up a telephone unexpectedly in the middle of a conversation. We recommend you do not do this unless the server has stopped responding to you and you cannot close the window any other way.


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/Chapter3.html b/doc/Chapter3.html index 5bdff67..615fe0f 100644 --- a/doc/Chapter3.html +++ b/doc/Chapter3.html @@ -26,13 +26,16 @@
    • 3.3 Altering your character set configuration
    • 3.4 Using X11 forwarding in SSH
    • 3.5 Using port forwarding in SSH
    • -
    • 3.6 Making raw TCP connections
    • -
    • 3.7 Connecting to a local serial line
    • -
    • 3.8 The PuTTY command line +
    • 3.6 Connecting to a local serial line
    • +
    • 3.7 Making raw TCP connections
    • +
    • 3.8 Connecting using the Telnet protocol
    • +
    • 3.9 Connecting using the Rlogin protocol
    • +
    • 3.10 Connecting using the SUPDUP protocol
    • +
    • 3.11 The PuTTY command line
  • @@ -125,14 +128,14 @@

    3.1.3.2 Repeat key exchange

    -Only available in SSH-2. Forces a repeat key exchange immediately (and resets associated timers and counters). For more information about repeat key exchanges, see section 4.20.2. +Only available in SSH-2. Forces a repeat key exchange immediately (and resets associated timers and counters). For more information about repeat key exchanges, see section 4.18.2.

  • Cache new host key type

    -Only available in SSH-2. This submenu appears only if the server has host keys of a type that PuTTY doesn't already have cached, and so won't consider. Selecting a key here will allow PuTTY to use that key now and in future: PuTTY will do a fresh key-exchange with the selected key, and immediately add that key to its permanent cache (relying on the host key used at the start of the connection to cross-certify the new key). That key will be used for the rest of the current session; it may not actually be used for future sessions, depending on your preferences (see section 4.21.1). +Only available in SSH-2. This submenu appears only if the server has host keys of a type that PuTTY doesn't already have cached, and so won't consider. Selecting a key here will allow PuTTY to use that key now and in future: PuTTY will do a fresh key-exchange with the selected key, and immediately add that key to its permanent cache (relying on the host key used at the start of the connection to cross-certify the new key). That key will be used for the rest of the current session; it may not actually be used for future sessions, depending on your preferences (see section 4.19.1).

    Normally, PuTTY will carry on using a host key it already knows, even if the server offers key formats that PuTTY would otherwise prefer, to avoid host key prompts. As a result, if you've been using a server for some years, you may still be using an older key than a new user would use, due to server upgrades in the meantime. The SSH protocol unfortunately does not have organised facilities for host key migration and rollover, but this allows you to manually upgrade. @@ -169,7 +172,7 @@

    3.1.3.2 Erase Character

    -PuTTY can also be configured to send this when the Backspace key is pressed; see section 4.17.3. +PuTTY can also be configured to send this when the Backspace key is pressed; see section 4.29.3.

  • @@ -195,14 +198,14 @@

    3.1.3.2 Interrupt Process

    -PuTTY can also be configured to send this when Ctrl-C is typed; see section 4.17.3. +PuTTY can also be configured to send this when Ctrl-C is typed; see section 4.29.3.

  • Suspend Process

    -PuTTY can also be configured to send this when Ctrl-Z is typed; see section 4.17.3. +PuTTY can also be configured to send this when Ctrl-Z is typed; see section 4.29.3.

  • @@ -286,7 +289,7 @@

    3.4 Using In order to use this feature, you will need an X display server for your Windows machine, such as Cygwin/X, X-Win32, or Exceed. This will probably install itself as display number 0 on your local machine; if it doesn't, the manual for the X server should tell you what it does do.

    -You should then tick the ‘Enable X11 forwarding’ box in the X11 panel (see section 4.26) before starting your SSH session. The ‘X display location’ box is blank by default, which means that PuTTY will try to use a sensible default such as :0, which is the usual display location where your X server will be installed. If that needs changing, then change it. +You should then tick the ‘Enable X11 forwarding’ box in the X11 panel (see section 4.24) before starting your SSH session. The ‘X display location’ box is blank by default, which means that PuTTY will try to use a sensible default such as :0, which is the usual display location where your X server will be installed. If that needs changing, then change it.

    Now you should be able to log in to the SSH server as normal. To check that X forwarding has been successfully negotiated during connection startup, you can check the PuTTY Event Log (see section 3.1.3.1). It should say something like this: @@ -304,7 +307,7 @@

    3.4 Using If this works, you should then be able to run X applications in the remote session and have them display their windows on your PC.

    -For more options relating to X11 forwarding, see section 4.26. +For more options relating to X11 forwarding, see section 4.24.

    3.5 Using port forwarding in SSH

    @@ -317,7 +320,7 @@

    3.5 Using port number on your local machine where PuTTY should listen for incoming connections. There are likely to be plenty of unused port numbers above 3000. (You can also use a local loopback address here; see below for more details.)
  • -Now, before you start your SSH connection, go to the Tunnels panel (see section 4.27). Make sure the ‘Local’ radio button is set. Enter the local port number into the ‘Source port’ box. Enter the destination host name and port number into the ‘Destination’ box, separated by a colon (for example, popserver.example.com:110 to connect to a POP-3 server). +Now, before you start your SSH connection, go to the Tunnels panel (see section 4.25). Make sure the ‘Local’ radio button is set. Enter the local port number into the ‘Source port’ box. Enter the destination host name and port number into the ‘Destination’ box, separated by a colon (for example, popserver.example.com:110 to connect to a POP-3 server).
  • Now click the ‘Add’ button. The details of your port forwarding should appear in the list box. @@ -355,12 +358,25 @@

    3.5 Using question A.7.17.)

    -For more options relating to port forwarding, see section 4.27. +For more options relating to port forwarding, see section 4.25.

    If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the ‘logical host name’ configuration option useful to warn PuTTY of which host key it should be expecting. See section 4.14.5 for details of this.

    -

    3.6 Making raw TCP connections

    +

    3.6 Connecting to a local serial line

    +

    +PuTTY can connect directly to a local serial line as an alternative to making a network connection. In this mode, text typed into the PuTTY window will be sent straight out of your computer's serial port, and data received through that port will be displayed in the PuTTY window. You might use this mode, for example, if your serial port is connected to another computer which has a serial connection. +

    +

    +To make a connection of this type, simply select ‘Serial’ from the ‘Connection type’ radio buttons on the ‘Session’ configuration panel (see section 4.1.1). The ‘Host Name’ and ‘Port’ boxes will transform into ‘Serial line’ and ‘Speed’, allowing you to specify which serial line to use (if your computer has more than one) and what speed (baud rate) to use when transferring data. For further configuration options (data bits, stop bits, parity, flow control), you can use the ‘Serial’ configuration panel (see section 4.28). +

    +

    +After you start up PuTTY in serial mode, you might find that you have to make the first move, by sending some data out of the serial line in order to notify the device at the other end that someone is there for it to talk to. This probably depends on the device. If you start up a PuTTY serial session and nothing appears in the window, try pressing Return a few times and see if that helps. +

    +

    +A serial line provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in serial mode will remain connected until you close the window using the close button. +

    +

    3.7 Making raw TCP connections

    A lot of Internet protocols are composed of commands and responses in plain text. For example, SMTP (the protocol used to transfer e-mail), NNTP (the protocol used to transfer Usenet news), and HTTP (the protocol used to serve Web pages) all consist of commands in readable plain text.

    @@ -373,37 +389,63 @@

    3.6 Making In order to make a debugging connection to a service of this type, you simply select the fourth protocol name, ‘Raw’, from the ‘Protocol’ buttons in the ‘Session’ configuration panel. (See section 4.1.1.) You can then enter a host name and a port number, and make the connection.

    -

    3.7 Connecting to a local serial line

    +

    3.8 Connecting using the Telnet protocol

    -PuTTY can connect directly to a local serial line as an alternative to making a network connection. In this mode, text typed into the PuTTY window will be sent straight out of your computer's serial port, and data received through that port will be displayed in the PuTTY window. You might use this mode, for example, if your serial port is connected to another computer which has a serial connection. +PuTTY can use the Telnet protocol to connect to a server.

    -To make a connection of this type, simply select ‘Serial’ from the ‘Connection type’ radio buttons on the ‘Session’ configuration panel (see section 4.1.1). The ‘Host Name’ and ‘Port’ boxes will transform into ‘Serial line’ and ‘Speed’, allowing you to specify which serial line to use (if your computer has more than one) and what speed (baud rate) to use when transferring data. For further configuration options (data bits, stop bits, parity, flow control), you can use the ‘Serial’ configuration panel (see section 4.29). +Telnet was perhaps the most popular remote login protocol before SSH was introduced. It was general enough to be used by multiple server operating systems (Unix and VMS in particular), and supported many optional protocol extensions providing extra support for particular server features.

    -After you start up PuTTY in serial mode, you might find that you have to make the first move, by sending some data out of the serial line in order to notify the device at the other end that someone is there for it to talk to. This probably depends on the device. If you start up a PuTTY serial session and nothing appears in the window, try pressing Return a few times and see if that helps. +Unlike SSH, Telnet runs over an unsecured network connection, so it is a very bad idea to use it over the hostile Internet (though it is still used to some extent as of 2020).

    +

    3.9 Connecting using the Rlogin protocol

    -A serial line provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in serial mode will remain connected until you close the window using the close button. +PuTTY can use the Rlogin protocol to connect to a server. +

    +

    +Rlogin was similar to Telnet in concept, but more focused on connections between Unix machines. It supported a feature for passwordless login, based on use of ‘privileged ports’ (ports with numbers below 1024, which Unix traditionally does not allow users other than root to allocate). Ultimately, based on the server trusting that the client's IP address was owned by the Unix machine it claimed to be, and that that machine would guard its privileged ports appropriately. +

    +

    +Like Telnet, Rlogin runs over an unsecured network connection. +

    +

    3.10 Connecting using the SUPDUP protocol

    +

    +PuTTY can use the SUPDUP protocol to connect to a server.

    -

    3.8 The PuTTY command line

    -PuTTY can be made to do various things without user intervention by supplying command-line arguments (e.g., from a command prompt window, or a Windows shortcut). +SUPDUP is a login protocol used mainly by PDP-10 and Lisp machines during the period 1975-1990. Like Telnet and Rlogin, it is unsecured, so modern systems almost never support it.

    -

    3.8.1 Starting a session from the command line

    -These options allow you to bypass the configuration window and launch straight into a session. +To make a connection of this type, select ‘SUPDUP’ from the ‘Connection type’ radio buttons on the ‘Session’ panel (see section 4.1.1). For further configuration options (character set, more processing, scrolling), you can use the ‘SUPDUP’ configuration panel (see section 4.31). +

    +

    +In SUPDUP, terminal emulation is more integrated with the network protocol than in other protocols such as SSH. The SUPDUP protocol can thus only be used with PuTTY proper, not with the command-line tool Plink. +

    +

    +The SUPDUP protocol does not support changing the terminal dimensions, so this capability is disabled during a SUPDUP session. +

    +

    +SUPDUP provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in SUPDUP mode will remain connected until you close the window using the close button. +

    +

    3.11 The PuTTY command line

    +

    +PuTTY can be made to do various things without user intervention by supplying command-line arguments (e.g., from a command prompt window, or a Windows shortcut). +

    +

    3.11.1 Starting a session from the command line

    +

    +These options allow you to bypass the configuration window and launch straight into a session.

    To start a connection to a server called host:

    -
    putty.exe [-ssh | -telnet | -rlogin | -raw] [user@]host
    +
    putty.exe [-ssh | -ssh-connection | -telnet | -rlogin | -supdup | -raw] [user@]host
     

    -If this syntax is used, settings are taken from the Default Settings (see section 4.1.2); user overrides these settings if supplied. Also, you can specify a protocol, which will override the default protocol (see section 3.8.3.2). +If this syntax is used, settings are taken from the Default Settings (see section 4.1.2); user overrides these settings if supplied. Also, you can specify a protocol, which will override the default protocol (see section 3.11.3.2).

    -For telnet sessions, the following alternative syntax is supported (this makes PuTTY suitable for use as a URL handler for telnet URLs in web browsers): +For telnet sessions, the following alternative syntax is supported (this makes PuTTY suitable for use as a URL handler for telnet URLs in web browsers):

    putty.exe telnet://host[:port]/
     
    @@ -413,96 +455,102 @@

    3.8.1 Starting a se
    putty.exe -serial com1
     

    -In order to start an existing saved session called sessionname, use the -load option (described in section 3.8.3.1). +In order to start an existing saved session called sessionname, use the -load option (described in section 3.11.3.1).

    putty.exe -load "session name"
     
    -

    3.8.2 -cleanup

    +

    3.11.2 -cleanup

    -If invoked with the -cleanup option, rather than running as normal, PuTTY will remove its registry entries and random seed file from the local machine (after confirming with the user). It will also attempt to remove information about recently launched sessions stored in the ‘jump list’ on Windows 7 and up. +If invoked with the -cleanup option, rather than running as normal, PuTTY will remove its registry entries and random seed file from the local machine (after confirming with the user). It will also attempt to remove information about recently launched sessions stored in the ‘jump list’ on Windows 7 and up.

    -Note that on multi-user systems, -cleanup only removes registry entries and files associated with the currently logged-in user. +Note that on multi-user systems, -cleanup only removes registry entries and files associated with the currently logged-in user.

    -

    3.8.3 Standard command-line options

    +

    3.11.3 Standard command-line options

    PuTTY and its associated tools support a range of command-line options, most of which are consistent across all the tools. This section lists the available options in all tools. Options which are specific to a particular tool are covered in the chapter about that tool.

    -

    3.8.3.1 -load: load a saved session

    +

    3.11.3.1 -load: load a saved session

    -The -load option causes PuTTY to load configuration details out of a saved session. If these details include a host name, then this option is all you need to make PuTTY start a session. +The -load option causes PuTTY to load configuration details out of a saved session. If these details include a host name, then this option is all you need to make PuTTY start a session.

    You need double quotes around the session name if it contains spaces.

    -If you want to create a Windows shortcut to start a PuTTY saved session, this is the option you should use: your shortcut should call something like +If you want to create a Windows shortcut to start a PuTTY saved session, this is the option you should use: your shortcut should call something like

    d:\path\to\putty.exe -load "my session"
     

    -(Note that PuTTY itself supports an alternative form of this option, for backwards compatibility. If you execute putty @sessionname it will have the same effect as putty -load "sessionname". With the @ form, no double quotes are required, and the @ sign must be the very first thing on the command line. This form of the option is deprecated.) +(Note that PuTTY itself supports an alternative form of this option, for backwards compatibility. If you execute putty @sessionname it will have the same effect as putty -load "sessionname". With the @ form, no double quotes are required, and the @ sign must be the very first thing on the command line. This form of the option is deprecated.)

    -

    3.8.3.2 Selecting a protocol: -ssh, -telnet, -rlogin, -raw -serial

    +

    3.11.3.2 Selecting a protocol: -ssh, -ssh-connection, -telnet, -rlogin, -supdup, -raw, -serial

    To choose which protocol you want to connect with, you can use one of these options:

    • --ssh selects the SSH protocol. +-ssh selects the SSH protocol.
    • --telnet selects the Telnet protocol. +-ssh-connection selects the bare ssh-connection protocol. (This is only useful in specialised circumstances; see section 4.27 for more information.)
    • --rlogin selects the Rlogin protocol. +-telnet selects the Telnet protocol.
    • --raw selects the raw protocol. +-rlogin selects the Rlogin protocol.
    • --serial selects a serial connection. +-supdup selects the SUPDUP protocol. +
    • +
    • +-raw selects the raw protocol. +
    • +
    • +-serial selects a serial connection.

    -These options are not available in the file transfer tools PSCP and PSFTP (which only work with the SSH protocol). +Most of these options are not available in the file transfer tools PSCP and PSFTP (which only work with the SSH protocol and the bare ssh-connection protocol).

    -These options are equivalent to the protocol selection buttons in the Session panel of the PuTTY configuration box (see section 4.1.1). +These options are equivalent to the protocol selection buttons in the Session panel of the PuTTY configuration box (see section 4.1.1).

    -

    3.8.3.3 -v: increase verbosity

    +

    3.11.3.3 -v: increase verbosity

    -Most of the PuTTY tools can be made to tell you more about what they are doing by supplying the -v option. If you are having trouble when making a connection, or you're simply curious, you can turn this switch on and hope to find out more about what is happening. +Most of the PuTTY tools can be made to tell you more about what they are doing by supplying the -v option. If you are having trouble when making a connection, or you're simply curious, you can turn this switch on and hope to find out more about what is happening.

    -

    3.8.3.4 -l: specify a login name

    +

    3.11.3.4 -l: specify a login name

    You can specify the user name to log in as on the remote server using the -l option. For example, plink login.example.com -l fred.

    These options are equivalent to the username selection box in the Connection panel of the PuTTY configuration box (see section 4.15.1).

    -

    3.8.3.5 -L, -R and -D: set up port forwardings

    +

    3.11.3.5 -L, -R and -D: set up port forwardings

    -As well as setting up port forwardings in the PuTTY configuration (see section 4.27), you can also set up forwardings on the command line. The command-line options work just like the ones in Unix ssh programs. +As well as setting up port forwardings in the PuTTY configuration (see section 4.25), you can also set up forwardings on the command line. The command-line options work just like the ones in Unix ssh programs.

    -To forward a local port (say 5110) to a remote destination (say popserver.example.com port 110), you can write something like one of these: +To forward a local port (say 5110) to a remote destination (say popserver.example.com port 110), you can write something like one of these:

    putty -L 5110:popserver.example.com:110 -load mysession
     plink mysession -L 5110:popserver.example.com:110
     

    -To forward a remote port to a local destination, just use the -R option instead of -L: +To forward a remote port to a local destination, just use the -R option instead of -L:

    putty -R 5023:mytelnetserver.myhouse.org:23 -load mysession
     plink mysession -R 5023:mytelnetserver.myhouse.org:23
     

    -To specify an IP address for the listening end of the tunnel, prepend it to the argument: +To specify an IP address for the listening end of the tunnel, prepend it to the argument:

    plink -L 127.0.0.5:23:localhost:23 myhost
     

    -To set up SOCKS-based dynamic port forwarding on a local port, use the -D option. For this one you only have to pass the port number: +To set up SOCKS-based dynamic port forwarding on a local port, use the -D option. For this one you only have to pass the port number:

    putty -D 4096 -load mysession
     
    @@ -512,17 +560,17 @@

    3.8.3.5 3.8.3.6 -m: read a remote command or script from a file

    +

    3.11.3.6 -m: read a remote command or script from a file

    -The -m option performs a similar function to the ‘Remote command’ box in the SSH panel of the PuTTY configuration box (see section 4.19.1). However, the -m option expects to be given a local file name, and it will read a command from that file. +The -m option performs a similar function to the ‘Remote command’ box in the SSH panel of the PuTTY configuration box (see section 4.17.1). However, the -m option expects to be given a local file name, and it will read a command from that file.

    -With some servers (particularly Unix systems), you can even put multiple lines in this file and execute more than one command in sequence, or a whole shell script; but this is arguably an abuse, and cannot be expected to work on all servers. In particular, it is known not to work with certain ‘embedded’ servers, such as Cisco routers. +With some servers (particularly Unix systems), you can even put multiple lines in this file and execute more than one command in sequence, or a whole shell script; but this is arguably an abuse, and cannot be expected to work on all servers. In particular, it is known not to work with certain ‘embedded’ servers, such as Cisco routers.

    This option is not available in the file transfer tools PSCP and PSFTP.

    -

    3.8.3.7 -P: specify a port number

    +

    3.11.3.7 -P: specify a port number

    The -P option is used to specify the port number to connect to. If you have a Telnet server running on port 9696 of a machine instead of port 23, for example:

    @@ -535,37 +583,37 @@

    3.8.3.7

    This option is equivalent to the port number control in the Session panel of the PuTTY configuration box (see section 4.1.1).

    -

    3.8.3.8 -pw: specify a password

    +

    3.11.3.8 -pw: specify a password

    A simple way to automate a remote login is to supply your password on the command line. This is not recommended for reasons of security. If you possibly can, we recommend you set up public-key authentication instead. See chapter 8 for details.

    -Note that the -pw option only works when you are using the SSH protocol. Due to fundamental limitations of Telnet and Rlogin, these protocols do not support automated password authentication. +Note that the -pw option only works when you are using the SSH protocol. Due to fundamental limitations of Telnet, Rlogin, and SUPDUP, these protocols do not support automated password authentication.

    -

    3.8.3.9 -agent and -noagent: control use of Pageant for authentication

    +

    3.11.3.9 -agent and -noagent: control use of Pageant for authentication

    The -agent option turns on SSH authentication using Pageant, and -noagent turns it off. These options are only meaningful if you are using SSH.

    -See chapter 9 for general information on Pageant. +See chapter 9 for general information on Pageant.

    -These options are equivalent to the agent authentication checkbox in the Auth panel of the PuTTY configuration box (see section 4.23.3). +These options are equivalent to the agent authentication checkbox in the Auth panel of the PuTTY configuration box (see section 4.21.4).

    -

    3.8.3.10 -A and -a: control agent forwarding

    +

    3.11.3.10 -A and -a: control agent forwarding

    The -A option turns on SSH agent forwarding, and -a turns it off. These options are only meaningful if you are using SSH.

    -See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.5 for details. +See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.6 for details.

    -These options are equivalent to the agent forwarding checkbox in the Auth panel of the PuTTY configuration box (see section 4.23.6). +These options are equivalent to the agent forwarding checkbox in the Auth panel of the PuTTY configuration box (see section 4.21.7).

    These options are not available in the file transfer tools PSCP and PSFTP.

    -

    3.8.3.11 -X and -x: control X11 forwarding

    +

    3.11.3.11 -X and -x: control X11 forwarding

    The -X option turns on X11 forwarding in SSH, and -x turns it off. These options are only meaningful if you are using SSH.

    @@ -573,22 +621,22 @@

    3.8.3.11 section 3.4.

    -These options are equivalent to the X11 forwarding checkbox in the X11 panel of the PuTTY configuration box (see section 4.26). +These options are equivalent to the X11 forwarding checkbox in the X11 panel of the PuTTY configuration box (see section 4.24).

    These options are not available in the file transfer tools PSCP and PSFTP.

    -

    3.8.3.12 -t and -T: control pseudo-terminal allocation

    +

    3.11.3.12 -t and -T: control pseudo-terminal allocation

    The -t option ensures PuTTY attempts to allocate a pseudo-terminal at the server, and -T stops it from allocating one. These options are only meaningful if you are using SSH.

    -These options are equivalent to the ‘Don't allocate a pseudo-terminal’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.25.1). +These options are equivalent to the ‘Don't allocate a pseudo-terminal’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.23.1).

    These options are not available in the file transfer tools PSCP and PSFTP.

    -

    3.8.3.13 -N: suppress starting a shell or command

    +

    3.11.3.13 -N: suppress starting a shell or command

    The -N option prevents PuTTY from attempting to start a shell or command on the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell.

    @@ -596,12 +644,12 @@

    3.8.3.13

    -This option is equivalent to the ‘Don't start a shell or command at all’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.19.2). +This option is equivalent to the ‘Don't start a shell or command at all’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.17.2).

    This option is not available in the file transfer tools PSCP and PSFTP.

    -

    3.8.3.14 -nc: make a remote network connection in place of a remote shell or command

    +

    3.11.3.14 -nc: make a remote network connection in place of a remote shell or command

    The -nc option prevents Plink (or PuTTY) from attempting to start a shell or command on the remote server. Instead, it will instruct the remote server to open a network connection to a host name and port number specified by you, and treat that network connection as if it were the main session.

    @@ -619,56 +667,63 @@

    3.8.3.14 (The option is named -nc after the Unix program nc, short for ‘netcat’. The command ‘plink host1 -nc host2:port’ is very similar in functionality to ‘plink host1 nc host2 port’, which invokes nc on the server and tells it to connect to the specified destination. However, Plink's built-in -nc option does not depend on the nc program being installed on the server.)

    -

    3.8.3.15 -C: enable compression

    +

    3.11.3.15 -C: enable compression

    The -C option enables compression of the data sent across the network. This option is only meaningful if you are using SSH.

    -This option is equivalent to the ‘Enable compression’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.19.3). +This option is equivalent to the ‘Enable compression’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.17.3).

    -

    3.8.3.16 -1 and -2: specify an SSH protocol version

    +

    3.11.3.16 -1 and -2: specify an SSH protocol version

    -The -1 and -2 options force PuTTY to use version 1 or version 2 of the SSH protocol. These options are only meaningful if you are using SSH. +The -1 and -2 options force PuTTY to use version 1 or version 2 of the SSH protocol. These options are only meaningful if you are using SSH.

    -These options are equivalent to selecting the SSH protocol version in the SSH panel of the PuTTY configuration box (see section 4.19.4). +These options are equivalent to selecting the SSH protocol version in the SSH panel of the PuTTY configuration box (see section 4.17.4).

    -

    3.8.3.17 -4 and -6: specify an Internet protocol version

    +

    3.11.3.17 -4 and -6: specify an Internet protocol version

    -The -4 and -6 options force PuTTY to use the older Internet protocol IPv4 or the newer IPv6 for most outgoing connections. +The -4 and -6 options force PuTTY to use the older Internet protocol IPv4 or the newer IPv6 for most outgoing connections.

    These options are equivalent to selecting your preferred Internet protocol version as ‘IPv4’ or ‘IPv6’ in the Connection panel of the PuTTY configuration box (see section 4.14.4).

    -

    3.8.3.18 -i: specify an SSH private key

    +

    3.11.3.18 -i: specify an SSH private key

    -The -i option allows you to specify the name of a private key file in *.PPK format which PuTTY will use to authenticate with the server. This option is only meaningful if you are using SSH. +The -i option allows you to specify the name of a private key file in *.PPK format which PuTTY will use to authenticate with the server. This option is only meaningful if you are using SSH.

    If you are using Pageant, you can also specify a public key file (in RFC 4716 or OpenSSH format) to identify a specific key file to use. (This won't work if you're not running Pageant, of course.)

    -For general information on public-key authentication, see chapter 8. +For general information on public-key authentication, see chapter 8. +

    +

    +This option is equivalent to the ‘Private key file for authentication’ box in the Auth panel of the PuTTY configuration box (see section 4.21.9). +

    +

    3.11.3.19 -no-trivial-auth: disconnect if SSH authentication succeeds trivially

    +

    +This option causes PuTTY to abandon an SSH session if the server accepts authentication without ever having asked for any kind of password or signature or token.

    -This option is equivalent to the ‘Private key file for authentication’ box in the Auth panel of the PuTTY configuration box (see section 4.23.8). +See section 4.21.3 for why you might want this.

    -

    3.8.3.19 -loghost: specify a logical host name

    +

    3.11.3.20 -loghost: specify a logical host name

    -This option overrides PuTTY's normal SSH host key caching policy by telling it the name of the host you expect your connection to end up at (in cases where this differs from the location PuTTY thinks it's connecting to). It can be a plain host name, or a host name followed by a colon and a port number. See section 4.14.5 for more detail on this. +This option overrides PuTTY's normal SSH host key caching policy by telling it the name of the host you expect your connection to end up at (in cases where this differs from the location PuTTY thinks it's connecting to). It can be a plain host name, or a host name followed by a colon and a port number. See section 4.14.5 for more detail on this.

    -

    3.8.3.20 -hostkey: manually specify an expected host key

    +

    3.11.3.21 -hostkey: manually specify an expected host key

    -This option overrides PuTTY's normal SSH host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key fingerprint, or an SSH-2 public key blob. See section 4.21.2 for more information. +This option overrides PuTTY's normal SSH host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key fingerprint, or an SSH-2 public key blob. See section 4.19.3 for more information.

    You can specify this option more than once if you want to configure more than one key to be accepted.

    -

    3.8.3.21 -pgpfp: display PGP key fingerprints

    +

    3.11.3.22 -pgpfp: display PGP key fingerprints

    -This option causes the PuTTY tools not to run as normal, but instead to display the fingerprints of the PuTTY PGP Master Keys, in order to aid with verifying new versions. See appendix E for more information. +This option causes the PuTTY tools not to run as normal, but instead to display the fingerprints of the PuTTY PGP Master Keys, in order to aid with verifying new versions. See appendix F for more information.

    -

    3.8.3.22 -sercfg: specify serial port configuration

    +

    3.11.3.23 -sercfg: specify serial port configuration

    This option specifies the configuration parameters for the serial port (baud rate, stop bits etc). Its argument is interpreted as a comma-separated list of configuration options, which can be as follows:

    @@ -691,9 +746,9 @@

    3.8.3.22 For example, ‘-sercfg 19200,8,n,1,N’ denotes a baud rate of 19200, 8 data bits, no parity, 1 stop bit and no flow control.

    -

    3.8.3.23 -sessionlog, -sshlog, -sshrawlog: specify session logging

    +

    3.11.3.24 -sessionlog, -sshlog, -sshrawlog: enable session logging

    -These options cause the PuTTY network tools to write out a log file. Each of them expects a file name as an argument, e.g. ‘-sshlog putty.log’ causes an SSH packet log to be written to a file called ‘putty.log’. The three different options select different logging modes, all available from the GUI too: +These options cause the PuTTY network tools to write out a log file. Each of them expects a file name as an argument, e.g. ‘-sshlog putty.log’ causes an SSH packet log to be written to a file called ‘putty.log’. The three different options select different logging modes, all available from the GUI too:

  • -
  • 4.17 The Telnet panel +
  • 4.17 The SSH panel
  • -
  • 4.18 The Rlogin panel +
  • 4.18 The Kex panel
  • -
  • 4.19 The SSH panel +
  • 4.19 The Host Keys panel
  • -
  • 4.20 The Kex panel +
  • 4.20 The Cipher panel
  • +
  • 4.21 The Auth panel
  • -
  • 4.21 The Host Keys panel +
  • 4.22 The GSSAPI panel
  • -
  • 4.22 The Cipher panel
  • -
  • 4.23 The Auth panel +
  • 4.23 The TTY panel
  • -
  • 4.24 The GSSAPI panel +
  • 4.24 The X11 panel
  • -
  • 4.25 The TTY panel +
  • 4.25 The Tunnels panel
  • -
  • 4.26 The X11 panel +
  • 4.26 The Bugs and More Bugs panels
  • -
  • 4.27 The Tunnels panel +
  • 4.27 The ‘Bare ssh-connection’ protocol
  • +
  • 4.28 The Serial panel
  • -
  • 4.28 The Bugs and More Bugs panels +
  • 4.29 The Telnet panel
  • -
  • 4.29 The Serial panel +
  • 4.30 The Rlogin panel
  • -
  • 4.30 Storing configuration in a file
  • +
  • 4.31 The SUPDUP panel +
  • +
  • 4.32 Storing configuration in a file
  • Chapter 4: Configuring PuTTY

    @@ -257,27 +267,50 @@

    4.1 The Session panel

    4.1.1 The host name section

    -The top box on the Session panel, labelled ‘Specify your connection by host name’, contains the details that need to be filled in before PuTTY can open a session at all. +The top box on the Session panel, labelled ‘Specify the destination you want to connect to’, contains the details that need to be filled in before PuTTY can open a session at all.

    • The ‘Host Name’ box is where you type the name, or the IP address, of the server you want to connect to.
    • -The ‘Connection type’ radio buttons let you choose what type of connection you want to make: a raw connection, a Telnet connection, an Rlogin connection, an SSH connection, or a connection to a local serial line. (See section 1.2 for a summary of the differences between SSH, Telnet and rlogin; see section 3.6 for an explanation of ‘raw’ connections; see section 3.7 for information about using a serial line.) +The ‘Connection type’ controls let you choose what type of connection you want to make: an SSH network connection, a connection to a local serial line, or various other kinds of network connection. +
      • +See section 1.2 for a summary of the differences between the network remote login protocols SSH, Telnet, Rlogin, and SUPDUP. +
      • +
      • +See section 3.6 for information about using a serial line. +
      • +
      • +See section 3.7 for an explanation of ‘raw’ connections. +
      • +
      • +See section 3.8 for a little information about Telnet. +
      • +
      • +See section 3.9 for information about using Rlogin.
      • -The ‘Port’ box lets you specify which port number on the server to connect to. If you select Telnet, Rlogin, or SSH, this box will be filled in automatically to the usual value, and you will only need to change it if you have an unusual server. If you select Raw mode, you will almost certainly need to fill in the ‘Port’ box yourself. +See section 3.10 for information about using SUPDUP. +
      • +
      • +The ‘Bare ssh-connection’ option in the ‘Connection type’ control is intended for specialist uses not involving network connections. See section 4.27 for some information about it. +
      • +
      + +
    • +
    • +The ‘Port’ box lets you specify which port number on the server to connect to. If you select Telnet, Rlogin, SUPDUP, or SSH, this box will be filled in automatically to the usual value, and you will only need to change it if you have an unusual server. If you select Raw mode, you will almost certainly need to fill in the ‘Port’ box yourself.

    -If you select ‘Serial’ from the ‘Connection type’ radio buttons, the ‘Host Name’ and ‘Port’ boxes are replaced by ‘Serial line’ and ‘Speed’; see section 4.29 for more details of these. +If you select ‘Serial’ from the ‘Connection type’ radio buttons, the ‘Host Name’ and ‘Port’ boxes are replaced by ‘Serial line’ and ‘Speed’; see section 4.28 for more details of these.

    -

    4.1.2 Loading and storing saved sessions

    +

    4.1.2 Loading and storing saved sessions

    The next part of the Session configuration panel allows you to save your preferred PuTTY options so they will appear automatically the next time you start PuTTY. It also allows you to create saved sessions, which contain a full set of configuration options plus a host name and protocol. A saved session contains all the information PuTTY needs to start exactly the session you want.

    • -To save your default settings: first set up the settings the way you want them saved. Then come back to the Session panel. Select the ‘Default Settings’ entry in the saved sessions list, with a single click. Then press the ‘Save’ button. +To save your default settings: first set up the settings the way you want them saved. Then come back to the Session panel. Select the ‘Default Settings’ entry in the saved sessions list, with a single click. Then press the ‘Save’ button.

    @@ -311,23 +344,23 @@

    4.1.2 Load Each saved session is independent of the Default Settings configuration. If you change your preferences and update Default Settings, you must also update every saved session separately.

    -Saved sessions are stored in the Registry, at the location +Saved sessions are stored in the Registry, at the location

    HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions
     

    -If you need to store them in a file, you could try the method described in section 4.30. +If you need to store them in a file, you could try the method described in section 4.32.

    -

    4.1.3 ‘Close window on exit’

    +

    4.1.3 ‘Close window on exit’

    -Finally in the Session panel, there is an option labelled ‘Close window on exit’. This controls whether the PuTTY terminal window disappears as soon as the session inside it terminates. If you are likely to want to copy and paste text out of the session after it has terminated, or restart the session, you should arrange for this option to be off. +Finally in the Session panel, there is an option labelled ‘Close window on exit’. This controls whether the PuTTY terminal window disappears as soon as the session inside it terminates. If you are likely to want to copy and paste text out of the session after it has terminated, or restart the session, you should arrange for this option to be off.

    -‘Close window on exit’ has three settings. ‘Always’ means always close the window on exit; ‘Never’ means never close on exit (always leave the window open, but inactive). The third setting, and the default one, is ‘Only on clean exit’. In this mode, a session which terminates normally will cause its window to close, but one which is aborted unexpectedly by network trouble or a confusing message from the server will leave the window up. +‘Close window on exit’ has three settings. ‘Always’ means always close the window on exit; ‘Never’ means never close on exit (always leave the window open, but inactive). The third setting, and the default one, is ‘Only on clean exit’. In this mode, a session which terminates normally will cause its window to close, but one which is aborted unexpectedly by network trouble or a confusing message from the server will leave the window up.

    4.2 The Logging panel

    -The Logging configuration panel allows you to save log files of your PuTTY sessions, for debugging, analysis or future reference. +The Logging configuration panel allows you to save log files of your PuTTY sessions, for debugging, analysis or future reference.

    The main option is a radio-button set that specifies whether PuTTY will log anything at all. The options are: @@ -342,7 +375,7 @@

    4.2 The Logging panel

    ‘All session output’. In this mode, everything sent by the server into your terminal session is logged. If you view the log file in a text editor, therefore, you may well find it full of strange control characters. This is a particularly useful mode if you are experiencing problems with PuTTY's terminal handling: you can record everything that went to the terminal, so that someone else can replay the session later in slow motion and watch to see what went wrong.
  • -‘SSH packets’. In this mode (which is only used by SSH connections), the SSH message packets sent over the encrypted connection are written to the log file (as well as Event Log entries). You might need this to debug a network-level problem, or more likely to send to the PuTTY authors as part of a bug report. BE WARNED that if you log in using a password, the password can appear in the log file; see section 4.2.5 for options that may help to remove sensitive material from the log file before you send it to anyone else. +‘SSH packets’. In this mode (which is only used by SSH connections), the SSH message packets sent over the encrypted connection are written to the log file (as well as Event Log entries). You might need this to debug a network-level problem, or more likely to send to the PuTTY authors as part of a bug report. BE WARNED that if you log in using a password, the password can appear in the log file; see section 4.2.5 for options that may help to remove sensitive material from the log file before you send it to anyone else.
  • ‘SSH packets and raw data’. In this mode, as well as the decrypted packets (as in the previous mode), the raw (encrypted, compressed, etc) packets are also logged. This could be useful to diagnose corruption in transit. (The same caveats as the previous mode apply, of course.) @@ -387,18 +420,18 @@

    4.2.2 ‘What to

    This control allows you to specify what PuTTY should do if it tries to start writing to a log file and it finds the file already exists. You might want to automatically destroy the existing log file and start a new one with the same name. Alternatively, you might want to open the existing log file and add data to the end of it. Finally (the default option), you might not want to have any automatic behaviour, but to ask the user every time the problem comes up.

    -

    4.2.3 ‘Flush log file frequently’

    +

    4.2.3 ‘Flush log file frequently’

    This option allows you to control how frequently logged data is flushed to disc. By default, PuTTY will flush data as soon as it is displayed, so that if you view the log file while a session is still open, it will be up to date; and if the client system crashes, there's a greater chance that the data will be preserved.

    However, this can incur a performance penalty. If PuTTY is running slowly with logging enabled, you could try unchecking this option. Be warned that the log file may not always be up to date as a result (although it will of course be flushed when it is closed, for instance at the end of a session).

    -

    4.2.4 ‘Include header’

    +

    4.2.4 ‘Include header’

    This option allows you to choose whether to include a header line with the date and time when the log file is opened. It may be useful to disable this if the log file is being used as realtime input to other programs that don't expect the header line.

    -

    4.2.5 Options specific to SSH packet logging

    +

    4.2.5 Options specific to SSH packet logging

    These options only apply if SSH packet data is being logged.

    @@ -424,11 +457,11 @@

    4.2.5.2 ‘O

    4.3 The Terminal panel

    -The Terminal configuration panel allows you to control the behaviour of PuTTY's terminal emulation. +The Terminal configuration panel allows you to control the behaviour of PuTTY's terminal emulation.

    4.3.1 ‘Auto wrap mode initially on’

    -Auto wrap mode controls what happens when text printed in a PuTTY window reaches the right-hand edge of the window. +Auto wrap mode controls what happens when text printed in a PuTTY window reaches the right-hand edge of the window.

    With auto wrap mode on, if a long line of text reaches the right-hand edge, it will wrap over on to the next line so you can still see all the text. With auto wrap mode off, the cursor will stay at the right-hand edge of the screen, and all the characters in the line will be printed on top of each other. @@ -437,17 +470,17 @@

    4.3.1 ‘Auto wrap mo If you are running a full-screen application and you occasionally find the screen scrolling up when it looks as if it shouldn't, you could try turning this option off.

    -Auto wrap mode can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately. +Auto wrap mode can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

    4.3.2 ‘DEC Origin Mode initially on’

    -DEC Origin Mode is a minor option which controls how PuTTY interprets cursor-position control sequences sent by the server. +DEC Origin Mode is a minor option which controls how PuTTY interprets cursor-position control sequences sent by the server.

    -The server can send a control sequence that restricts the scrolling region of the display. For example, in an editor, the server might reserve a line at the top of the screen and a line at the bottom, and might send a control sequence that causes scrolling operations to affect only the remaining lines. +The server can send a control sequence that restricts the scrolling region of the display. For example, in an editor, the server might reserve a line at the top of the screen and a line at the bottom, and might send a control sequence that causes scrolling operations to affect only the remaining lines.

    -With DEC Origin Mode on, cursor coordinates are counted from the top of the scrolling region. With it turned off, cursor coordinates are counted from the top of the whole screen regardless of the scrolling region. +With DEC Origin Mode on, cursor coordinates are counted from the top of the scrolling region. With it turned off, cursor coordinates are counted from the top of the whole screen regardless of the scrolling region.

    It is unlikely you would need to change this option, but if you find a full-screen application is displaying pieces of text in what looks like the wrong part of the screen, you could try turning DEC Origin Mode on to see whether that helps. @@ -457,10 +490,10 @@

    4.3.2 ‘DEC Origin Mode

    4.3.3 ‘Implicit CR in every LF’

    -Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll). +Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll).

    -Some servers only send LF, and expect the terminal to move the cursor over to the left automatically. If you come across a server that does this, you will see a stepped effect on the screen, like this: +Some servers only send LF, and expect the terminal to move the cursor over to the left automatically. If you come across a server that does this, you will see a stepped effect on the screen, like this:

    First line of text
                       Second line
    @@ -475,14 +508,14 @@ 

    4.3.3 ‘Implicit CR in e

    4.3.4 ‘Implicit LF in every CR’

    -Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll). +Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll).

    Some servers only send CR, and so the newly written line is overwritten by the following line. This option causes a line feed so that all lines are displayed.

    -

    4.3.5 ‘Use background colour to erase screen’

    +

    4.3.5 ‘Use background colour to erase screen’

    -Not all terminals agree on what colour to turn the screen when the server sends a ‘clear screen’ sequence. Some terminals believe the screen should always be cleared to the default background colour. Others believe the screen should be cleared to whatever the server has selected as a background colour. +Not all terminals agree on what colour to turn the screen when the server sends a ‘clear screen’ sequence. Some terminals believe the screen should always be cleared to the default background colour. Others believe the screen should be cleared to whatever the server has selected as a background colour.

    There exist applications that expect both kinds of behaviour. Therefore, PuTTY can be configured to do either. @@ -491,39 +524,39 @@

    4.3.5 ‘Use control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately. +Background-colour erase can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

    -

    4.3.6 ‘Enable blinking text’

    +

    4.3.6 ‘Enable blinking text’

    The server can ask PuTTY to display text that blinks on and off. This is very distracting, so PuTTY allows you to turn blinking text off completely.

    -When blinking text is disabled and the server attempts to make some text blink, PuTTY will instead display the text with a bolded background colour. +When blinking text is disabled and the server attempts to make some text blink, PuTTY will instead display the text with a bolded background colour.

    -Blinking text can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately. +Blinking text can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

    -

    4.3.7 ‘Answerback to ^E’

    +

    4.3.7 ‘Answerback to ^E’

    -This option controls what PuTTY will send back to the server if the server sends it the ^E enquiry character. Normally it just sends the string ‘PuTTY’. +This option controls what PuTTY will send back to the server if the server sends it the ^E enquiry character. Normally it just sends the string ‘PuTTY’.

    If you accidentally write the contents of a binary file to your terminal, you will probably find that it contains more than one ^E character, and as a result your next command line will probably read ‘PuTTYPuTTYPuTTY...’ as if you had typed the answerback string multiple times at the keyboard. If you set the answerback string to be empty, this problem should go away, but doing so might cause other problems.

    -Note that this is not the feature of PuTTY which the server will typically use to determine your terminal type. That feature is the ‘Terminal-type string’ in the Connection panel; see section 4.15.3 for details. +Note that this is not the feature of PuTTY which the server will typically use to determine your terminal type. That feature is the ‘Terminal-type string’ in the Connection panel; see section 4.15.3 for details.

    You can include control characters in the answerback string using ^C notation. (Use ^~ to get a literal ^.)

    -

    4.3.8 ‘Local echo’

    +

    4.3.8 ‘Local echo’

    -With local echo disabled, characters you type into the PuTTY window are not echoed in the window by PuTTY. They are simply sent to the server. (The server might choose to echo them back to you; this can't be controlled from the PuTTY control panel.) +With local echo disabled, characters you type into the PuTTY window are not echoed in the window by PuTTY. They are simply sent to the server. (The server might choose to echo them back to you; this can't be controlled from the PuTTY control panel.)

    Some types of session need local echo, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local echo is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local echo to be turned on, or force it to be turned off, instead of relying on the automatic detection.

    -

    4.3.9 ‘Local line editing’

    +

    4.3.9 ‘Local line editing’

    Normally, every character you type into the PuTTY window is sent immediately to the server the moment you type it.

    @@ -531,12 +564,12 @@

    4.3.9 ‘local echo (section 4.3.8). This makes it ideal for use in raw mode or when connecting to MUDs or talkers. (Although some more advanced MUDs do occasionally turn local line editing on and turn local echo off, in order to accept a password from the user.) +Since it is hard to edit a line locally without being able to see it, local line editing is mostly used in conjunction with local echo (section 4.3.8). This makes it ideal for use in raw mode or when connecting to MUDs or talkers. (Although some more advanced MUDs do occasionally turn local line editing on and turn local echo off, in order to accept a password from the user.)

    Some types of session need local line editing, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local line editing is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local line editing to be turned on, or force it to be turned off, instead of relying on the automatic detection.

    -

    4.3.10 Remote-controlled printing

    +

    4.3.10 Remote-controlled printing

    A lot of VT100-compatible terminals support printing under control of the remote server (sometimes called ‘passthrough printing’). PuTTY supports this feature as well, but it is turned off by default.

    @@ -554,32 +587,32 @@

    4.3.10

    4.4 The Keyboard panel

    -The Keyboard configuration panel allows you to control the behaviour of the keyboard in PuTTY. The correct state for many of these settings depends on what the server to which PuTTY is connecting expects. With a Unix server, this is likely to depend on the termcap or terminfo entry it uses, which in turn is likely to be controlled by the ‘Terminal-type string’ setting in the Connection panel; see section 4.15.3 for details. If none of the settings here seems to help, you may find question A.7.13 to be useful. +The Keyboard configuration panel allows you to control the behaviour of the keyboard in PuTTY. The correct state for many of these settings depends on what the server to which PuTTY is connecting expects. With a Unix server, this is likely to depend on the termcap or terminfo entry it uses, which in turn is likely to be controlled by the ‘Terminal-type string’ setting in the Connection panel; see section 4.15.3 for details. If none of the settings here seems to help, you may find question A.7.13 to be useful.

    -

    4.4.1 Changing the action of the Backspace key

    +

    4.4.1 Changing the action of the Backspace key

    -Some terminals believe that the Backspace key should send the same thing to the server as Control-H (ASCII code 8). Other terminals believe that the Backspace key should send ASCII code 127 (usually known as Control-?) so that it can be distinguished from Control-H. This option allows you to choose which code PuTTY generates when you press Backspace. +Some terminals believe that the Backspace key should send the same thing to the server as Control-H (ASCII code 8). Other terminals believe that the Backspace key should send ASCII code 127 (usually known as Control-?) so that it can be distinguished from Control-H. This option allows you to choose which code PuTTY generates when you press Backspace.

    -If you are connecting over SSH, PuTTY by default tells the server the value of this option (see section 4.25.2), so you may find that the Backspace key does the right thing either way. Similarly, if you are connecting to a Unix system, you will probably find that the Unix stty command lets you configure which the server expects to see, so again you might not need to change which one PuTTY generates. On other systems, the server's expectation might be fixed and you might have no choice but to configure PuTTY. +If you are connecting over SSH, PuTTY by default tells the server the value of this option (see section 4.23.2), so you may find that the Backspace key does the right thing either way. Similarly, if you are connecting to a Unix system, you will probably find that the Unix stty command lets you configure which the server expects to see, so again you might not need to change which one PuTTY generates. On other systems, the server's expectation might be fixed and you might have no choice but to configure PuTTY.

    If you do have the choice, we recommend configuring PuTTY to generate Control-? and configuring the server to expect it, because that allows applications such as emacs to use Control-H for help.

    -(Typing Shift-Backspace will cause PuTTY to send whichever code isn't configured here as the default.) +(Typing Shift-Backspace will cause PuTTY to send whichever code isn't configured here as the default.)

    -

    4.4.2 Changing the action of the Home and End keys

    +

    4.4.2 Changing the action of the Home and End keys

    -The Unix terminal emulator rxvt disagrees with the rest of the world about what character sequences should be sent to the server by the Home and End keys. +The Unix terminal emulator rxvt disagrees with the rest of the world about what character sequences should be sent to the server by the Home and End keys.

    -xterm, and other terminals, send ESC [1~ for the Home key, and ESC [4~ for the End key. rxvt sends ESC [H for the Home key and ESC [Ow for the End key. +xterm, and other terminals, send ESC [1~ for the Home key, and ESC [4~ for the End key. rxvt sends ESC [H for the Home key and ESC [Ow for the End key.

    If you find an application on which the Home and End keys aren't working, you could try switching this option to see if it helps.

    -

    4.4.3 Changing the action of the function keys and keypad

    +

    4.4.3 Changing the action of the function keys and keypad

    This option affects the function keys (F1 to F12) and the top row of the numeric keypad.

    @@ -587,25 +620,25 @@

    4.4.3 Changing the action In the default mode, labelled ESC [n~, the function keys generate sequences like ESC [11~, ESC [12~ and so on. This matches the general behaviour of Digital's terminals.

  • -In Linux mode, F6 to F12 behave just like the default mode, but F1 to F5 generate ESC [[A through to ESC [[E. This mimics the Linux virtual console. +In Linux mode, F6 to F12 behave just like the default mode, but F1 to F5 generate ESC [[A through to ESC [[E. This mimics the Linux virtual console.
  • -In Xterm R6 mode, F5 to F12 behave like the default mode, but F1 to F4 generate ESC OP through to ESC OS, which are the sequences produced by the top row of the keypad on Digital's terminals. +In Xterm R6 mode, F5 to F12 behave like the default mode, but F1 to F4 generate ESC OP through to ESC OS, which are the sequences produced by the top row of the keypad on Digital's terminals.
  • -In VT400 mode, all the function keys behave like the default mode, but the actual top row of the numeric keypad generates ESC OP through to ESC OS. +In VT400 mode, all the function keys behave like the default mode, but the actual top row of the numeric keypad generates ESC OP through to ESC OS.
  • -In VT100+ mode, the function keys generate ESC OP through to ESC O[ +In VT100+ mode, the function keys generate ESC OP through to ESC O[
  • -In SCO mode, the function keys F1 to F12 generate ESC [M through to ESC [X. Together with shift, they generate ESC [Y through to ESC [j. With control they generate ESC [k through to ESC [v, and with shift and control together they generate ESC [w through to ESC [{. +In SCO mode, the function keys F1 to F12 generate ESC [M through to ESC [X. Together with shift, they generate ESC [Y through to ESC [j. With control they generate ESC [k through to ESC [v, and with shift and control together they generate ESC [w through to ESC [{.
  • If you don't know what any of this means, you probably don't need to fiddle with it.

    -

    4.4.4 Controlling Application Cursor Keys mode

    +

    4.4.4 Controlling Application Cursor Keys mode

    Application Cursor Keys mode is a way for the server to change the control sequences sent by the arrow keys. In normal mode, the arrow keys send ESC [A through to ESC [D. In application mode, they send ESC OA through to ESC OD.

    @@ -615,12 +648,12 @@

    4.4.4 Controlling You can also disable application cursor keys mode completely, using the ‘Features’ configuration panel; see section 4.6.1.

    -

    4.4.5 Controlling Application Keypad mode

    +

    4.4.5 Controlling Application Keypad mode

    Application Keypad mode is a way for the server to change the behaviour of the numeric keypad.

    -In normal mode, the keypad behaves like a normal Windows keypad: with NumLock on, the number keys generate numbers, and with NumLock off they act like the arrow keys and Home, End etc. +In normal mode, the keypad behaves like a normal Windows keypad: with NumLock on, the number keys generate numbers, and with NumLock off they act like the arrow keys and Home, End etc.

    In application mode, all the keypad keys send special control sequences, including Num Lock. Num Lock stops behaving like Num Lock and becomes another function key. @@ -634,7 +667,7 @@

    4.4.5 Controlling You can also disable application keypad mode completely, using the ‘Features’ configuration panel; see section 4.6.1.

    -

    4.4.6 Using NetHack keypad mode

    +

    4.4.6 Using NetHack keypad mode

    PuTTY has a special mode for playing NetHack. You can enable it by selecting ‘NetHack’ in the ‘Initial state of numeric keypad’ control.

    @@ -645,16 +678,16 @@

    4.4.6 Using In addition, pressing Shift or Ctrl with the keypad keys generate the Shift- or Ctrl-keys you would expect (e.g. keypad-7 generates ‘y’, so Shift-keypad-7 generates ‘Y’ and Ctrl-keypad-7 generates Ctrl-Y); these commands tell NetHack to keep moving you in the same direction until you encounter something interesting.

    -For some reason, this feature only works properly when Num Lock is on. We don't know why. +For some reason, this feature only works properly when Num Lock is on. We don't know why.

    -

    4.4.7 Enabling a DEC-like Compose key

    +

    4.4.7 Enabling a DEC-like Compose key

    -DEC terminals have a Compose key, which provides an easy-to-remember way of typing accented characters. You press Compose and then type two more characters. The two characters are ‘combined’ to produce an accented character. The choices of character are designed to be easy to remember; for example, composing ‘e’ and ‘`’ produces the ‘è’ character. +DEC terminals have a Compose key, which provides an easy-to-remember way of typing accented characters. You press Compose and then type two more characters. The two characters are ‘combined’ to produce an accented character. The choices of character are designed to be easy to remember; for example, composing ‘e’ and ‘`’ produces the ‘è’ character.

    -If your keyboard has a Windows Application key, it acts as a Compose key in PuTTY. Alternatively, if you enable the ‘AltGr acts as Compose key’ option, the AltGr key will become a Compose key. +If your keyboard has a Windows Application key, it acts as a Compose key in PuTTY. Alternatively, if you enable the ‘AltGr acts as Compose key’ option, the AltGr key will become a Compose key.

    -

    4.4.8 ‘Control-Alt is different from AltGr’

    +

    4.4.8 ‘Control-Alt is different from AltGr’

    Some old keyboards do not have an AltGr key, which can make it difficult to type some characters. PuTTY can be configured to treat the key combination Ctrl + Left Alt the same way as the AltGr key.

    @@ -669,32 +702,32 @@

    4.4.8 ‘Control-Alt i

    4.5 The Bell panel

    -The Bell panel controls the terminal bell feature: the server's ability to cause PuTTY to beep at you. +The Bell panel controls the terminal bell feature: the server's ability to cause PuTTY to beep at you.

    -In the default configuration, when the server sends the character with ASCII code 7 (Control-G), PuTTY will play the Windows Default Beep sound. This is not always what you want the terminal bell feature to do; the Bell panel allows you to configure alternative actions. +In the default configuration, when the server sends the character with ASCII code 7 (Control-G), PuTTY will play the Windows Default Beep sound. This is not always what you want the terminal bell feature to do; the Bell panel allows you to configure alternative actions.

    4.5.1 ‘Set the style of bell’

    This control allows you to select various different actions to occur on a terminal bell:

    • -Selecting ‘None’ disables the bell completely. In this mode, the server can send as many Control-G characters as it likes and nothing at all will happen. +Selecting ‘None’ disables the bell completely. In this mode, the server can send as many Control-G characters as it likes and nothing at all will happen.
    • ‘Make default system alert sound’ is the default setting. It causes the Windows ‘Default Beep’ sound to be played. To change what this sound is, or to test it if nothing seems to be happening, use the Sound configurer in the Windows Control Panel.
    • -‘Visual bell’ is a silent alternative to a beeping computer. In this mode, when the server sends a Control-G, the whole PuTTY window will flash white for a fraction of a second. +‘Visual bell’ is a silent alternative to a beeping computer. In this mode, when the server sends a Control-G, the whole PuTTY window will flash white for a fraction of a second.
    • -‘Beep using the PC speaker’ is self-explanatory. +‘Beep using the PC speaker’ is self-explanatory.
    • -‘Play a custom sound file’ allows you to specify a particular sound file to be used by PuTTY alone, or even by a particular individual PuTTY session. This allows you to distinguish your PuTTY beeps from any other beeps on the system. If you select this option, you will also need to enter the name of your sound file in the edit control ‘Custom sound file to play as a bell’. +‘Play a custom sound file’ allows you to specify a particular sound file to be used by PuTTY alone, or even by a particular individual PuTTY session. This allows you to distinguish your PuTTY beeps from any other beeps on the system. If you select this option, you will also need to enter the name of your sound file in the edit control ‘Custom sound file to play as a bell’.
    -

    4.5.2 ‘Taskbar/caption indication on bell’

    +

    4.5.2 ‘Taskbar/caption indication on bell’

    This feature controls what happens to the PuTTY window's entry in the Windows Taskbar if a bell occurs while the window does not have the input focus.

    @@ -707,12 +740,12 @@

    4.5.2 ‘4.5.3 ‘Control the bell overload behaviour’

    +

    4.5.3 ‘Control the bell overload behaviour’

    A common user error in a terminal session is to accidentally run the Unix command cat (or equivalent) on an inappropriate file type, such as an executable, image file, or ZIP file. This produces a huge stream of non-text characters sent to the terminal, which typically includes a lot of bell characters. As a result of this the terminal often doesn't stop beeping for ten minutes, and everybody else in the office gets annoyed.

    -To try to avoid this behaviour, or any other cause of excessive beeping, PuTTY includes a bell overload management feature. In the default configuration, receiving more than five bell characters in a two-second period will cause the overload feature to activate. Once the overload feature is active, further bells will have no effect at all, so the rest of your binary file will be sent to the screen in silence. After a period of five seconds during which no further bells are received, the overload feature will turn itself off again and bells will be re-enabled. +To try to avoid this behaviour, or any other cause of excessive beeping, PuTTY includes a bell overload management feature. In the default configuration, receiving more than five bell characters in a two-second period will cause the overload feature to activate. Once the overload feature is active, further bells will have no effect at all, so the rest of your binary file will be sent to the screen in silence. After a period of five seconds during which no further bells are received, the overload feature will turn itself off again and bells will be re-enabled.

    If you want this feature completely disabled, you can turn it off using the checkbox ‘Bell is temporarily disabled when over-used’. @@ -725,18 +758,18 @@

    4.5.3 ‘Control the <

    4.6 The Features panel

    -PuTTY's terminal emulation is very highly featured, and can do a lot of things under remote server control. Some of these features can cause problems due to buggy or strangely configured server applications. +PuTTY's terminal emulation is very highly featured, and can do a lot of things under remote server control. Some of these features can cause problems due to buggy or strangely configured server applications.

    The Features configuration panel allows you to disable some of PuTTY's more advanced terminal features, in case they cause trouble.

    4.6.1 Disabling application keypad and cursor keys

    -Application keypad mode (see section 4.4.5) and application cursor keys mode (see section 4.4.4) alter the behaviour of the keypad and cursor keys. Some applications enable these modes but then do not deal correctly with the modified keys. You can force these modes to be permanently disabled no matter what the server tries to do. +Application keypad mode (see section 4.4.5) and application cursor keys mode (see section 4.4.4) alter the behaviour of the keypad and cursor keys. Some applications enable these modes but then do not deal correctly with the modified keys. You can force these modes to be permanently disabled no matter what the server tries to do.

    -

    4.6.2 Disabling xterm-style mouse reporting

    +

    4.6.2 Disabling xterm-style mouse reporting

    -PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser links, the Usenet newsreader trn version 4, and the file manager mc (Midnight Commander). +PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser links, the Usenet newsreader trn version 4, and the file manager mc (Midnight Commander).

    If you find this feature inconvenient, you can disable it using the ‘Disable xterm-style mouse reporting’ control. With this box ticked, the mouse will always do copy and paste in the normal way. @@ -744,27 +777,27 @@

    4.6.2 Disabling Note that even if the application takes over the mouse, you can still manage PuTTY's copy and paste by holding down the Shift key while you select and paste, unless you have deliberately turned this feature off (see section 4.11.2).

    -

    4.6.3 Disabling remote terminal resizing

    +

    4.6.3 Disabling remote terminal resizing

    PuTTY has the ability to change the terminal's size and position in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands.

    -

    4.6.4 Disabling switching to the alternate screen

    +

    4.6.4 Disabling switching to the alternate screen

    Many terminals, including PuTTY, support an ‘alternate screen’. This is the same size as the ordinary terminal screen, but separate. Typically a screen-based program such as a text editor might switch the terminal to the alternate screen before starting up. Then at the end of the run, it switches back to the primary screen, and you see the screen contents just as they were before starting the editor.

    Some people prefer this not to happen. If you want your editor to run in the same screen as the rest of your terminal activity, you can disable the alternate screen feature completely.

    -

    4.6.5 Disabling remote window title changing

    +

    4.6.5 Disabling remote window title changing

    PuTTY has the ability to change the window title in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands.

    -

    4.6.6 Response to remote window title querying

    +

    4.6.6 Response to remote window title querying

    PuTTY can optionally provide the xterm service of allowing server applications to find out the local window title. This feature is disabled by default, but you can turn it on if you really want it.

    -NOTE that this feature is a potential security hazard. If a malicious application can write data to your terminal (for example, if you merely cat a file owned by someone else on the server machine), it can change your window title (unless you have disabled this as mentioned in section 4.6.5) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn't want. Therefore this feature is disabled by default, and we recommend you do not set it to ‘Window title’ unless you really know what you are doing. +NOTE that this feature is a potential security hazard. If a malicious application can write data to your terminal (for example, if you merely cat a file owned by someone else on the server machine), it can change your window title (unless you have disabled this as mentioned in section 4.6.5) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn't want. Therefore this feature is disabled by default, and we recommend you do not set it to ‘Window title’ unless you really know what you are doing.

    There are three settings for this option: @@ -788,52 +821,52 @@

    4.6.6 Response to PuTTY responds with the actual window title. This is dangerous for the reasons described above. -

    4.6.7 Disabling remote scrollback clearing

    +

    4.6.7 Disabling remote scrollback clearing

    PuTTY has the ability to clear the terminal's scrollback buffer in response to a command from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to that server command.

    -

    4.6.8 Disabling destructive backspace

    +

    4.6.8 Disabling destructive backspace

    Normally, when PuTTY receives character 127 (^?) from the server, it will perform a ‘destructive backspace’: move the cursor one space left and delete the character under it. This can apparently cause problems in some applications, so PuTTY provides the ability to configure character 127 to perform a normal backspace (without deleting a character) instead.

    -

    4.6.9 Disabling remote character set configuration

    +

    4.6.9 Disabling remote character set configuration

    -PuTTY has the ability to change its character set configuration in response to commands from the server. Some programs send these commands unexpectedly or inconveniently. In particular, BitchX (an IRC client) seems to have a habit of reconfiguring the character set to something other than the user intended. +PuTTY has the ability to change its character set configuration in response to commands from the server. Some programs send these commands unexpectedly or inconveniently. In particular, BitchX (an IRC client) seems to have a habit of reconfiguring the character set to something other than the user intended.

    If you find that accented characters are not showing up the way you expect them to, particularly if you're running BitchX, you could try disabling the remote character set configuration commands.

    -

    4.6.10 Disabling Arabic text shaping

    +

    4.6.10 Disabling Arabic text shaping

    -PuTTY supports shaping of Arabic text, which means that if your server sends text written in the basic Unicode Arabic alphabet then it will convert it to the correct display forms before printing it on the screen. +PuTTY supports shaping of Arabic text, which means that if your server sends text written in the basic Unicode Arabic alphabet then it will convert it to the correct display forms before printing it on the screen.

    -If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable Arabic text shaping so that PuTTY displays precisely the characters it is told to display. +If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable Arabic text shaping so that PuTTY displays precisely the characters it is told to display.

    You may also find you need to disable bidirectional text display; see section 4.6.11.

    -

    4.6.11 Disabling bidirectional text display

    +

    4.6.11 Disabling bidirectional text display

    -PuTTY supports bidirectional text display, which means that if your server sends text written in a language which is usually displayed from right to left (such as Arabic or Hebrew) then PuTTY will automatically flip it round so that it is displayed in the right direction on the screen. +PuTTY supports bidirectional text display, which means that if your server sends text written in a language which is usually displayed from right to left (such as Arabic or Hebrew) then PuTTY will automatically flip it round so that it is displayed in the right direction on the screen.

    -If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable bidirectional text display, so that PuTTY displays text from left to right in all situations. +If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable bidirectional text display, so that PuTTY displays text from left to right in all situations.

    You may also find you need to disable Arabic text shaping; see section 4.6.10.

    4.7 The Window panel

    -The Window configuration panel allows you to control aspects of the PuTTY window. +The Window configuration panel allows you to control aspects of the PuTTY window.

    -

    4.7.1 Setting the size of the PuTTY window

    +

    4.7.1 Setting the size of the PuTTY window

    -The ‘Columns’ and ‘Rows’ boxes let you set the PuTTY window to a precise size. Of course you can also drag the window to a new size while a session is running. +The ‘Columns’ and ‘Rows’ boxes let you set the PuTTY window to a precise size. Of course you can also drag the window to a new size while a session is running.

    4.7.2 What to do when the window is resized

    -These options allow you to control what happens when the user tries to resize the PuTTY window using its window furniture. +These options allow you to control what happens when the user tries to resize the PuTTY window using its window furniture.

    There are four options here: @@ -842,21 +875,21 @@

    4.7.2 What to do when ‘Change the number of rows and columns’: the font size will not change. (This is the default.)
  • -‘Change the size of the font’: the number of rows and columns in the terminal will stay the same, and the font size will change. +‘Change the size of the font’: the number of rows and columns in the terminal will stay the same, and the font size will change.
  • -‘Change font size when maximised’: when the window is resized, the number of rows and columns will change, except when the window is maximised (or restored), when the font size will change. (In this mode, holding down the Alt key while resizing will also cause the font size to change.) +‘Change font size when maximised’: when the window is resized, the number of rows and columns will change, except when the window is maximised (or restored), when the font size will change. (In this mode, holding down the Alt key while resizing will also cause the font size to change.)
  • ‘Forbid resizing completely’: the terminal will refuse to be resized at all.
  • -

    4.7.3 Controlling scrollback

    +

    4.7.3 Controlling scrollback

    These options let you configure the way PuTTY keeps text after it scrolls off the top of the screen (see section 3.1.2).

    -The ‘Lines of scrollback’ box lets you configure how many lines of text PuTTY keeps. The ‘Display scrollbar’ options allow you to hide the scrollbar (although you can still view the scrollback using the keyboard as described in section 3.1.2). You can separately configure whether the scrollbar is shown in full-screen mode and in normal modes. +The ‘Lines of scrollback’ box lets you configure how many lines of text PuTTY keeps. The ‘Display scrollbar’ options allow you to hide the scrollbar (although you can still view the scrollback using the keyboard as described in section 3.1.2). You can separately configure whether the scrollbar is shown in full-screen mode and in normal modes.

    If you are viewing part of the scrollback when the server sends more text to PuTTY, the screen will revert to showing the current terminal contents. You can disable this behaviour by turning off ‘Reset scrollback on display activity’. You can also make the screen revert when you press a key, by turning on ‘Reset scrollback on keypress’. @@ -866,37 +899,37 @@

    4.7.4 ‘Pus When this option is enabled, the contents of the terminal screen will be pushed into the scrollback when a server-side application clears the screen, so that your scrollback will contain a better record of what was on your screen in the past.

    -If the application switches to the alternate screen (see section 4.6.4 for more about this), then the contents of the primary screen will be visible in the scrollback until the application switches back again. +If the application switches to the alternate screen (see section 4.6.4 for more about this), then the contents of the primary screen will be visible in the scrollback until the application switches back again.

    This option is enabled by default.

    4.8 The Appearance panel

    -The Appearance configuration panel allows you to control aspects of the appearance of PuTTY's window. +The Appearance configuration panel allows you to control aspects of the appearance of PuTTY's window.

    -

    4.8.1 Controlling the appearance of the cursor

    +

    4.8.1 Controlling the appearance of the cursor

    The ‘Cursor appearance’ option lets you configure the cursor to be a block, an underline, or a vertical line. A block cursor becomes an empty box when the window loses focus; an underline or a vertical line becomes dotted.

    -The ‘Cursor blinks’ option makes the cursor blink on and off. This works in any of the cursor modes. +The ‘Cursor blinks’ option makes the cursor blink on and off. This works in any of the cursor modes.

    -

    4.8.2 Controlling the font used in the terminal window

    +

    4.8.2 Controlling the font used in the terminal window

    -This option allows you to choose what font, in what size, the PuTTY terminal window uses to display the text in the session. +This option allows you to choose what font, in what size, the PuTTY terminal window uses to display the text in the session.

    By default, you will be offered a choice from all the fixed-width fonts installed on the system, since VT100-style terminal handling expects a fixed-width font. If you tick the box marked ‘Allow selection of variable-pitch fonts’, however, PuTTY will offer variable-width fonts as well: if you select one of these, the font will be coerced into fixed-size character cells, which will probably not look very good (but can work OK with some fonts).

    -

    4.8.3 ‘Hide mouse pointer when typing in window’

    +

    4.8.3 ‘Hide mouse pointer when typing in window’

    If you enable this option, the mouse pointer will disappear if the PuTTY window is selected and you press a key. This way, it will not obscure any of the text in the window while you work in your session. As soon as you move the mouse, the pointer will reappear.

    This option is disabled by default, so the mouse pointer remains visible at all times.

    -

    4.8.4 Controlling the window border

    +

    4.8.4 Controlling the window border

    PuTTY allows you to configure the appearance of the window border to some extent.

    @@ -908,57 +941,57 @@

    4.8.4 Controlling the

    4.9 The Behaviour panel

    -The Behaviour configuration panel allows you to control aspects of the behaviour of PuTTY's window. +The Behaviour configuration panel allows you to control aspects of the behaviour of PuTTY's window.

    -

    4.9.1 Controlling the window title

    +

    4.9.1 Controlling the window title

    -The ‘Window title’ edit box allows you to set the title of the PuTTY window. By default the window title will contain the host name followed by ‘PuTTY’, for example server1.example.com - PuTTY. If you want a different window title, this is where to set it. +The ‘Window title’ edit box allows you to set the title of the PuTTY window. By default the window title will contain the host name followed by ‘PuTTY’, for example server1.example.com - PuTTY. If you want a different window title, this is where to set it.

    -PuTTY allows the server to send xterm control sequences which modify the title of the window in mid-session (unless this is disabled - see section 4.6.5); the title string set here is therefore only the initial window title. +PuTTY allows the server to send xterm control sequences which modify the title of the window in mid-session (unless this is disabled - see section 4.6.5); the title string set here is therefore only the initial window title.

    -As well as the window title, there is also an xterm sequence to modify the title of the window's icon. This makes sense in a windowing system where the window becomes an icon when minimised, such as Windows 3.1 or most X Window System setups; but in the Windows 95-like user interface it isn't as applicable. +As well as the window title, there is also an xterm sequence to modify the title of the window's icon. This makes sense in a windowing system where the window becomes an icon when minimised, such as Windows 3.1 or most X Window System setups; but in the Windows 95-like user interface it isn't as applicable.

    -By default, PuTTY only uses the server-supplied window title, and ignores the icon title entirely. If for some reason you want to see both titles, check the box marked ‘Separate window and icon titles’. If you do this, PuTTY's window title and Taskbar caption will change into the server-supplied icon title if you minimise the PuTTY window, and change back to the server-supplied window title if you restore it. (If the server has not bothered to supply a window or icon title, none of this will happen.) +By default, PuTTY only uses the server-supplied window title, and ignores the icon title entirely. If for some reason you want to see both titles, check the box marked ‘Separate window and icon titles’. If you do this, PuTTY's window title and Taskbar caption will change into the server-supplied icon title if you minimise the PuTTY window, and change back to the server-supplied window title if you restore it. (If the server has not bothered to supply a window or icon title, none of this will happen.)

    -

    4.9.2 ‘Warn before closing window’

    +

    4.9.2 ‘Warn before closing window’

    -If you press the Close button in a PuTTY window that contains a running session, PuTTY will put up a warning window asking if you really meant to close the window. A window whose session has already terminated can always be closed without a warning. +If you press the Close button in a PuTTY window that contains a running session, PuTTY will put up a warning window asking if you really meant to close the window. A window whose session has already terminated can always be closed without a warning.

    If you want to be able to close a window quickly, you can disable the ‘Warn before closing window’ option.

    -

    4.9.3 ‘Window closes on ALT-F4’

    +

    4.9.3 ‘Window closes on ALT-F4’

    -By default, pressing ALT-F4 causes the window to close (or a warning box to appear; see section 4.9.2). If you disable the ‘Window closes on ALT-F4’ option, then pressing ALT-F4 will simply send a key sequence to the server. +By default, pressing ALT-F4 causes the window to close (or a warning box to appear; see section 4.9.2). If you disable the ‘Window closes on ALT-F4’ option, then pressing ALT-F4 will simply send a key sequence to the server.

    -

    4.9.4 ‘System menu appears on ALT-Space’

    +

    4.9.4 ‘System menu appears on ALT-Space’

    If this option is enabled, then pressing ALT-Space will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing ALT-Space will just send ESC SPACE to the server.

    -Some accessibility programs for Windows may need this option enabling to be able to control PuTTY's window successfully. For instance, Dragon NaturallySpeaking requires it both to open the system menu via voice, and to close, minimise, maximise and restore the window. +Some accessibility programs for Windows may need this option enabling to be able to control PuTTY's window successfully. For instance, Dragon NaturallySpeaking requires it both to open the system menu via voice, and to close, minimise, maximise and restore the window.

    -

    4.9.5 ‘System menu appears on Alt alone’

    +

    4.9.5 ‘System menu appears on Alt alone’

    If this option is enabled, then pressing and releasing ALT will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing and releasing ALT will have no effect.

    -

    4.9.6 ‘Ensure window is always on top’

    +

    4.9.6 ‘Ensure window is always on top’

    If this option is enabled, the PuTTY window will stay on top of all other windows.

    -

    4.9.7 ‘Full screen on Alt-Enter’

    +

    4.9.7 ‘Full screen on Alt-Enter’

    If this option is enabled, then pressing Alt-Enter will cause the PuTTY window to become full-screen. Pressing Alt-Enter again will restore the previous window size.

    -The full-screen feature is also available from the System menu, even when it is configured not to be available on the Alt-Enter key. See section 3.1.3.7. +The full-screen feature is also available from the System menu, even when it is configured not to be available on the Alt-Enter key. See section 3.1.3.7.

    4.10 The Translation panel

    -The Translation configuration panel allows you to control the translation between the character set understood by the server and the character set understood by PuTTY. +The Translation configuration panel allows you to control the translation between the character set understood by the server and the character set understood by PuTTY.

    4.10.1 Controlling character set translation

    @@ -968,47 +1001,47 @@

    4.10.1 Controlling charac There are a lot of character sets to choose from. The ‘Remote character set’ option lets you select one.

    -By default PuTTY will use the UTF-8 encoding of Unicode, which can represent pretty much any character; data coming from the server is interpreted as UTF-8, and keystrokes are sent UTF-8 encoded. This is what most modern distributions of Linux will expect by default. However, if this is wrong for your server, you can select a different character set using this control. +By default PuTTY will use the UTF-8 encoding of Unicode, which can represent pretty much any character; data coming from the server is interpreted as UTF-8, and keystrokes are sent UTF-8 encoded. This is what most modern distributions of Linux will expect by default. However, if this is wrong for your server, you can select a different character set using this control.

    A few other notable character sets are:

    • -The ISO-8859 series are all standard character sets that include various accented characters appropriate for different sets of languages. +The ISO-8859 series are all standard character sets that include various accented characters appropriate for different sets of languages.
    • -The Win125x series are defined by Microsoft, for similar purposes. In particular Win1252 is almost equivalent to ISO-8859-1, but contains a few extra characters such as matched quotes and the Euro symbol. +The Win125x series are defined by Microsoft, for similar purposes. In particular Win1252 is almost equivalent to ISO-8859-1, but contains a few extra characters such as matched quotes and the Euro symbol.
    • -If you want the old IBM PC character set with block graphics and line-drawing characters, you can select ‘CP437’. +If you want the old IBM PC character set with block graphics and line-drawing characters, you can select ‘CP437’.

    -If you need support for a numeric code page which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (CP866 for example) in the list box. If the underlying version of Windows has the appropriate translation table installed, PuTTY will use it. +If you need support for a numeric code page which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (CP866 for example) in the list box. If the underlying version of Windows has the appropriate translation table installed, PuTTY will use it.

    -

    4.10.2 ‘Treat CJK ambiguous characters as wide’

    +

    4.10.2 ‘Treat CJK ambiguous characters as wide’

    -There are some Unicode characters whose width is not well-defined. In most contexts, such characters should be treated as single-width for the purposes of wrapping and so on; however, in some CJK contexts, they are better treated as double-width for historical reasons, and some server-side applications may expect them to be displayed as such. Setting this option will cause PuTTY to take the double-width interpretation. +There are some Unicode characters whose width is not well-defined. In most contexts, such characters should be treated as single-width for the purposes of wrapping and so on; however, in some CJK contexts, they are better treated as double-width for historical reasons, and some server-side applications may expect them to be displayed as such. Setting this option will cause PuTTY to take the double-width interpretation.

    If you use legacy CJK applications, and you find your lines are wrapping in the wrong places, or you are having other display problems, you might want to play with this setting.

    -This option only has any effect in UTF-8 mode (see section 4.10.1). +This option only has any effect in UTF-8 mode (see section 4.10.1).

    -

    4.10.3 ‘Caps Lock acts as Cyrillic switch’

    +

    4.10.3 ‘Caps Lock acts as Cyrillic switch’

    -This feature allows you to switch between a US/UK keyboard layout and a Cyrillic keyboard layout by using the Caps Lock key, if you need to type (for example) Russian and English side by side in the same document. +This feature allows you to switch between a US/UK keyboard layout and a Cyrillic keyboard layout by using the Caps Lock key, if you need to type (for example) Russian and English side by side in the same document.

    Currently this feature is not expected to work properly if your native keyboard layout is not US or UK.

    -

    4.10.4 Controlling display of line-drawing characters

    +

    4.10.4 Controlling display of line-drawing characters

    -VT100-series terminals allow the server to send control sequences that shift temporarily into a separate character set for drawing simple lines and boxes. However, there are a variety of ways in which PuTTY can attempt to find appropriate characters, and the right one to use depends on the locally configured font. In general you should probably try lots of options until you find one that your particular font supports. +VT100-series terminals allow the server to send control sequences that shift temporarily into a separate character set for drawing simple lines and boxes. However, there are a variety of ways in which PuTTY can attempt to find appropriate characters, and the right one to use depends on the locally configured font. In general you should probably try lots of options until you find one that your particular font supports.

    • -‘Use Unicode line drawing code points’ tries to use the box characters that are present in Unicode. For good Unicode-supporting fonts this is probably the most reliable and functional option. +‘Use Unicode line drawing code points’ tries to use the box characters that are present in Unicode. For good Unicode-supporting fonts this is probably the most reliable and functional option.
    • ‘Poor man's line drawing’ assumes that the font cannot generate the line and box characters at all, so it will use the +, - and | characters to draw approximations to boxes. You should use this option if none of the other options works. @@ -1023,9 +1056,9 @@

      4.10.4 Controlling displ ‘Use font in OEM mode only’ is more reliable than that, but can miss out other characters from the main character set.

    -

    4.10.5 Controlling copy and paste of line drawing characters

    +

    4.10.5 Controlling copy and paste of line drawing characters

    -By default, when you copy and paste a piece of the PuTTY screen that contains VT100 line and box drawing characters, PuTTY will paste them in the form they appear on the screen: either Unicode line drawing code points, or the ‘poor man's’ line-drawing characters +, - and |. The checkbox ‘Copy and paste VT100 line drawing chars as lqqqk’ disables this feature, so line-drawing characters will be pasted as the ASCII characters that were printed to produce them. This will typically mean they come out mostly as q and x, with a scattering of jklmntuvw at the corners. This might be useful if you were trying to recreate the same box layout in another program, for example. +By default, when you copy and paste a piece of the PuTTY screen that contains VT100 line and box drawing characters, PuTTY will paste them in the form they appear on the screen: either Unicode line drawing code points, or the ‘poor man's’ line-drawing characters +, - and |. The checkbox ‘Copy and paste VT100 line drawing chars as lqqqk’ disables this feature, so line-drawing characters will be pasted as the ASCII characters that were printed to produce them. This will typically mean they come out mostly as q and x, with a scattering of jklmntuvw at the corners. This might be useful if you were trying to recreate the same box layout in another program, for example.

    Note that this option only applies to line-drawing characters which were printed by using the VT100 mechanism. Line-drawing characters that were received as Unicode code points will paste as Unicode always. @@ -1045,27 +1078,27 @@

    4.10.6 Combining VT1

    4.11 The Selection panel

    -The Selection panel allows you to control the way copy and paste work in the PuTTY window. +The Selection panel allows you to control the way copy and paste work in the PuTTY window.

    4.11.1 Changing the actions of the mouse buttons

    -PuTTY's copy and paste mechanism is by default modelled on the Unix xterm application. The X Window System uses a three-button mouse, and the convention in that system is that the left button selects, the right button extends an existing selection, and the middle button pastes. +PuTTY's copy and paste mechanism is by default modelled on the Unix xterm application. The X Window System uses a three-button mouse, and the convention in that system is that the left button selects, the right button extends an existing selection, and the middle button pastes.

    -Windows often only has two mouse buttons, so when run on Windows, PuTTY is configurable. In PuTTY's default configuration (‘Compromise’), the right button pastes, and the middle button (if you have one) extends a selection. +Windows often only has two mouse buttons, so when run on Windows, PuTTY is configurable. In PuTTY's default configuration (‘Compromise’), the right button pastes, and the middle button (if you have one) extends a selection.

    -If you have a three-button mouse and you are already used to the xterm arrangement, you can select it using the ‘Action of mouse buttons’ control. +If you have a three-button mouse and you are already used to the xterm arrangement, you can select it using the ‘Action of mouse buttons’ control.

    -Alternatively, with the ‘Windows’ option selected, the middle button extends, and the right button brings up a context menu (on which one of the options is ‘Paste’). (This context menu is always available by holding down Ctrl and right-clicking, regardless of the setting of this option.) +Alternatively, with the ‘Windows’ option selected, the middle button extends, and the right button brings up a context menu (on which one of the options is ‘Paste’). (This context menu is always available by holding down Ctrl and right-clicking, regardless of the setting of this option.)

    (When PuTTY iself is running on Unix, it follows the X Window System convention.)

    4.11.2 ‘Shift overrides application's use of mouse’

    -PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser links, the Usenet newsreader trn version 4, and the file manager mc (Midnight Commander). +PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser links, the Usenet newsreader trn version 4, and the file manager mc (Midnight Commander).

    When running one of these applications, pressing the mouse buttons no longer performs copy and paste. If you do need to copy and paste, you can still do so if you hold down Shift while you do your mouse clicks. @@ -1081,7 +1114,7 @@

    4.11.3 Default selecti As described in section 3.1.1, PuTTY has two modes of selecting text to be copied to the clipboard. In the default mode (‘Normal’), dragging the mouse from point A to point B selects to the end of the line containing A, all the lines in between, and from the very beginning of the line containing B. In the other mode (‘Rectangular block’), dragging the mouse between two points defines a rectangle, and everything within that rectangle is copied.

    -Normally, you have to hold down Alt while dragging the mouse to select a rectangular block. Using the ‘Default selection mode’ control, you can set rectangular selection as the default, and then you have to hold down Alt to get the normal behaviour. +Normally, you have to hold down Alt while dragging the mouse to select a rectangular block. Using the ‘Default selection mode’ control, you can set rectangular selection as the default, and then you have to hold down Alt to get the normal behaviour.

    4.11.4 Assigning copy and paste actions to clipboards

    @@ -1091,10 +1124,10 @@

    4.11.4 Assigning copy Most platforms, including Windows, have a single system clipboard. On these platforms, PuTTY provides a second clipboard-like facility by permitting you to paste the text you last selected in this window, whether or not it is currently also in the system clipboard. This is not enabled by default.

    -The X Window System (which underlies most Unix graphical interfaces) provides multiple clipboards (or ‘selections’), and many applications support more than one of them by a different user interface mechanism. When PuTTY itself is running on Unix, it has more configurability relating to these selections. +The X Window System (which underlies most Unix graphical interfaces) provides multiple clipboards (or ‘selections’), and many applications support more than one of them by a different user interface mechanism. When PuTTY itself is running on Unix, it has more configurability relating to these selections.

    -The two most commonly used selections are called ‘PRIMARY’ and ‘CLIPBOARD’; in applications supporting both, the usual behaviour is that PRIMARY is used by mouse-only actions (selecting text automatically copies it to PRIMARY, and middle-clicking pastes from PRIMARY), whereas CLIPBOARD is used by explicit Copy and Paste menu items or keypresses such as Ctrl-C and Ctrl-V. +The two most commonly used selections are called ‘PRIMARY’ and ‘CLIPBOARD’; in applications supporting both, the usual behaviour is that PRIMARY is used by mouse-only actions (selecting text automatically copies it to PRIMARY, and middle-clicking pastes from PRIMARY), whereas CLIPBOARD is used by explicit Copy and Paste menu items or keypresses such as Ctrl-C and Ctrl-V.

    4.11.4.1 ‘Auto-copy selected text’

    @@ -1105,19 +1138,19 @@

    4.11.4.1 

    4.11.4.2 Choosing a clipboard for UI actions

    -PuTTY has three user-interface actions which can be configured to paste into the terminal (not counting menu items). You can click whichever mouse button (if any) is configured to paste (see section 4.11.1); you can press Shift-Ins; or you can press Ctrl-Shift-V, although that action is not enabled by default. +PuTTY has three user-interface actions which can be configured to paste into the terminal (not counting menu items). You can click whichever mouse button (if any) is configured to paste (see section 4.11.1); you can press Shift-Ins; or you can press Ctrl-Shift-V, although that action is not enabled by default.

    -You can configure which of the available clipboards each of these actions pastes from (including turning the paste action off completely). On platforms with a single system clipboard (such as Windows), the available options are to paste from that clipboard or to paste from PuTTY's internal memory of the last selected text within that window. On X, the standard options are CLIPBOARD or PRIMARY. +You can configure which of the available clipboards each of these actions pastes from (including turning the paste action off completely). On platforms with a single system clipboard (such as Windows), the available options are to paste from that clipboard or to paste from PuTTY's internal memory of the last selected text within that window. On X, the standard options are CLIPBOARD or PRIMARY.

    (PRIMARY is conceptually similar in that it also refers to the last selected text – just across all applications instead of just this window.)

    -The two keyboard options each come with a corresponding key to copy to the same clipboard. Whatever you configure Shift-Ins to paste from, Ctrl-Ins will copy to the same location; similarly, Ctrl-Shift-C will copy to whatever Ctrl-Shift-V pastes from. +The two keyboard options each come with a corresponding key to copy to the same clipboard. Whatever you configure Shift-Ins to paste from, Ctrl-Ins will copy to the same location; similarly, Ctrl-Shift-C will copy to whatever Ctrl-Shift-V pastes from.

    -On X, you can also enter a selection name of your choice. For example, there is a rarely-used standard selection called ‘SECONDARY’, which Emacs (for example) can work with if you hold down the Meta key while dragging to select or clicking to paste; if you configure a PuTTY keyboard action to access this clipboard, then you can interoperate with other applications' use of it. Another thing you could do would be to invent a clipboard name yourself, to create a special clipboard shared only between instances of PuTTY, or between just instances configured in that particular way. +On X, you can also enter a selection name of your choice. For example, there is a rarely-used standard selection called ‘SECONDARY’, which Emacs (for example) can work with if you hold down the Meta key while dragging to select or clicking to paste; if you configure a PuTTY keyboard action to access this clipboard, then you can interoperate with other applications' use of it. Another thing you could do would be to invent a clipboard name yourself, to create a special clipboard shared only between instances of PuTTY, or between just instances configured in that particular way.

    4.11.5 ‘Permit control characters in pasted text’

    @@ -1135,19 +1168,19 @@

    4.12 The Copy panel<

    4.12.1 Character classes

    -PuTTY will select a word at a time in the terminal window if you double-click to begin the drag. This section allows you to control precisely what is considered to be a word. +PuTTY will select a word at a time in the terminal window if you double-click to begin the drag. This section allows you to control precisely what is considered to be a word.

    Each character is given a class, which is a small number (typically 0, 1 or 2). PuTTY considers a single word to be any number of adjacent characters in the same class. So by modifying the assignment of characters to classes, you can modify the word-by-word selection behaviour.

    -In the default configuration, the character classes are: +In the default configuration, the character classes are:

    • -Class 0 contains white space and control characters. +Class 0 contains white space and control characters.
    • -Class 1 contains most punctuation. +Class 1 contains most punctuation.
    • Class 2 contains letters, numbers and a few pieces of punctuation (the double quote, minus sign, period, forward slash and underscore). @@ -1163,32 +1196,32 @@

      4.12.1 Character clas This mechanism currently only covers ASCII characters, because it isn't feasible to expand the list to cover the whole of Unicode.

      -Character class definitions can be modified by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately. +Character class definitions can be modified by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

      -

      4.12.2 Copying in Rich Text Format

      +

      4.12.2 Copying in Rich Text Format

      -If you enable ‘Copy to clipboard in RTF as well as plain text’, PuTTY will write formatting information to the clipboard as well as the actual text you copy. The effect of this is that if you paste into (say) a word processor, the text will appear in the word processor in the same font, colour, and style (e.g. bold, underline) PuTTY was using to display it. +If you enable ‘Copy to clipboard in RTF as well as plain text’, PuTTY will write formatting information to the clipboard as well as the actual text you copy. The effect of this is that if you paste into (say) a word processor, the text will appear in the word processor in the same font, colour, and style (e.g. bold, underline) PuTTY was using to display it.

      This option can easily be inconvenient, so by default it is disabled.

      4.13 The Colours panel

      -The Colours panel allows you to control PuTTY's use of colour. +The Colours panel allows you to control PuTTY's use of colour.

      -

      4.13.1 ‘Allow terminal to specify ANSI colours’

      +

      4.13.1 ‘Allow terminal to specify ANSI colours’

      -This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server to request coloured text. +This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server to request coloured text.

      If you have a particularly garish application, you might want to turn this option off and make PuTTY only use the default foreground and background colours.

      -

      4.13.2 ‘Allow terminal to use xterm 256-colour mode’

      +

      4.13.2 ‘Allow terminal to use xterm 256-colour mode’

      This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the extended 256-colour mode supported by recent versions of xterm.

      -If you have an application which is supposed to use 256-colour mode and it isn't working, you may find you need to tell your server that your terminal supports 256 colours. On Unix, you do this by ensuring that the setting of TERM describes a 256-colour-capable terminal. You can check this using a command such as infocmp: +If you have an application which is supposed to use 256-colour mode and it isn't working, you may find you need to tell your server that your terminal supports 256 colours. On Unix, you do this by ensuring that the setting of TERM describes a 256-colour-capable terminal. You can check this using a command such as infocmp:

      $ infocmp | grep colors
               colors#256, cols#80, it#8, lines#24, pairs#256,
      @@ -1202,69 +1235,69 @@ 

      4.13.3 ‘Allow te

      4.13.4 ‘Indicate bolded text by changing...’

      -When the server sends a control sequence indicating that some text should be displayed in bold, PuTTY can handle this in several ways. It can either change the font for a bold version, or use the same font in a brighter colour, or it can do both (brighten the colour and embolden the font). This control lets you choose which. +When the server sends a control sequence indicating that some text should be displayed in bold, PuTTY can handle this in several ways. It can either change the font for a bold version, or use the same font in a brighter colour, or it can do both (brighten the colour and embolden the font). This control lets you choose which.

      By default bold is indicated by colour, so non-bold text is displayed in light grey and bold text is displayed in bright white (and similarly in other colours). If you change the setting to ‘The font’ box, bold and non-bold text will be displayed in the same colour, and instead the font will change to indicate the difference. If you select ‘Both’, the font and the colour will both change.

      -Some applications rely on ‘bold black’ being distinguishable from a black background; if you choose ‘The font’, their text may become invisible. +Some applications rely on ‘bold black’ being distinguishable from a black background; if you choose ‘The font’, their text may become invisible.

      -

      4.13.5 ‘Attempt to use logical palettes’

      +

      4.13.5 ‘Attempt to use logical palettes’

      -Logical palettes are a mechanism by which a Windows application running on an 8-bit colour display can select precisely the colours it wants instead of going with the Windows standard defaults. +Logical palettes are a mechanism by which a Windows application running on an 8-bit colour display can select precisely the colours it wants instead of going with the Windows standard defaults.

      If you are not getting the colours you ask for on an 8-bit display, you can try enabling this option. However, be warned that it's never worked very well.

      -

      4.13.6 ‘Use system colours’

      +

      4.13.6 ‘Use system colours’

      -Enabling this option will cause PuTTY to ignore the configured colours for ‘Default Background/Foreground’ and ‘Cursor Colour/Text’ (see section 4.13.7), instead going with the system-wide defaults. +Enabling this option will cause PuTTY to ignore the configured colours for ‘Default Background/Foreground’ and ‘Cursor Colour/Text’ (see section 4.13.7), instead going with the system-wide defaults.

      -Note that non-bold and bold text will be the same colour if this option is enabled. You might want to change to indicating bold text by font changes (see section 4.13.4). +Note that non-bold and bold text will be the same colour if this option is enabled. You might want to change to indicating bold text by font changes (see section 4.13.4).

      -

      4.13.7 Adjusting the colours in the terminal window

      +

      4.13.7 Adjusting the colours in the terminal window

      -The main colour control allows you to specify exactly what colours things should be displayed in. To modify one of the PuTTY colours, use the list box to select which colour you want to modify. The RGB values for that colour will appear on the right-hand side of the list box. Now, if you press the ‘Modify’ button, you will be presented with a colour selector, in which you can choose a new colour to go in place of the old one. (You may also edit the RGB values directly in the edit boxes, if you wish; each value is an integer from 0 to 255.) +The main colour control allows you to specify exactly what colours things should be displayed in. To modify one of the PuTTY colours, use the list box to select which colour you want to modify. The RGB values for that colour will appear on the right-hand side of the list box. Now, if you press the ‘Modify’ button, you will be presented with a colour selector, in which you can choose a new colour to go in place of the old one. (You may also edit the RGB values directly in the edit boxes, if you wish; each value is an integer from 0 to 255.)

      -PuTTY allows you to set the cursor colour, the default foreground and background, and the precise shades of all the ANSI configurable colours (black, red, green, yellow, blue, magenta, cyan, and white). You can also modify the precise shades used for the bold versions of these colours; these are used to display bold text if you have chosen to indicate that by colour (see section 4.13.4), and can also be used if the server asks specifically to use them. (Note that ‘Default Bold Background’ is not the background colour used for bold text; it is only used if the server specifically asks for a bold background.) +PuTTY allows you to set the cursor colour, the default foreground and background, and the precise shades of all the ANSI configurable colours (black, red, green, yellow, blue, magenta, cyan, and white). You can also modify the precise shades used for the bold versions of these colours; these are used to display bold text if you have chosen to indicate that by colour (see section 4.13.4), and can also be used if the server asks specifically to use them. (Note that ‘Default Bold Background’ is not the background colour used for bold text; it is only used if the server specifically asks for a bold background.)

      4.14 The Connection panel

      -The Connection panel allows you to configure options that apply to more than one type of connection. +The Connection panel allows you to configure options that apply to more than one type of connection.

      -

      4.14.1 Using keepalives to prevent disconnection

      +

      4.14.1 Using keepalives to prevent disconnection

      If you find your sessions are closing unexpectedly (most often with ‘Connection reset by peer’) after they have been idle for a while, you might want to try using this option.

      -Some network routers and firewalls need to keep track of all connections through them. Usually, these firewalls will assume a connection is dead if no data is transferred in either direction after a certain time interval. This can cause PuTTY sessions to be unexpectedly closed by the firewall if no traffic is seen in the session for some time. +Some network routers and firewalls need to keep track of all connections through them. Usually, these firewalls will assume a connection is dead if no data is transferred in either direction after a certain time interval. This can cause PuTTY sessions to be unexpectedly closed by the firewall if no traffic is seen in the session for some time.

      -The keepalive option (‘Seconds between keepalives’) allows you to configure PuTTY to send data through the session at regular intervals, in a way that does not disrupt the actual terminal session. If you find your firewall is cutting idle connections off, you can try entering a non-zero value in this field. The value is measured in seconds; so, for example, if your firewall cuts connections off after ten minutes then you might want to enter 300 seconds (5 minutes) in the box. +The keepalive option (‘Seconds between keepalives’) allows you to configure PuTTY to send data through the session at regular intervals, in a way that does not disrupt the actual terminal session. If you find your firewall is cutting idle connections off, you can try entering a non-zero value in this field. The value is measured in seconds; so, for example, if your firewall cuts connections off after ten minutes then you might want to enter 300 seconds (5 minutes) in the box.

      -Note that keepalives are not always helpful. They help if you have a firewall which drops your connection after an idle period; but if the network between you and the server suffers from breaks in connectivity then keepalives can actually make things worse. If a session is idle, and connectivity is temporarily lost between the endpoints, but the connectivity is restored before either side tries to send anything, then there will be no problem - neither endpoint will notice that anything was wrong. However, if one side does send something during the break, it will repeatedly try to re-send, and eventually give up and abandon the connection. Then when connectivity is restored, the other side will find that the first side doesn't believe there is an open connection any more. Keepalives can make this sort of problem worse, because they increase the probability that PuTTY will attempt to send data during a break in connectivity. (Other types of periodic network activity can cause this behaviour; in particular, SSH-2 re-keys can have this effect. See section 4.20.2.) +Note that keepalives are not always helpful. They help if you have a firewall which drops your connection after an idle period; but if the network between you and the server suffers from breaks in connectivity then keepalives can actually make things worse. If a session is idle, and connectivity is temporarily lost between the endpoints, but the connectivity is restored before either side tries to send anything, then there will be no problem - neither endpoint will notice that anything was wrong. However, if one side does send something during the break, it will repeatedly try to re-send, and eventually give up and abandon the connection. Then when connectivity is restored, the other side will find that the first side doesn't believe there is an open connection any more. Keepalives can make this sort of problem worse, because they increase the probability that PuTTY will attempt to send data during a break in connectivity. (Other types of periodic network activity can cause this behaviour; in particular, SSH-2 re-keys can have this effect. See section 4.18.2.)

      Therefore, you might find that keepalives help connection loss, or you might find they make it worse, depending on what kind of network problems you have between you and the server.

      -Keepalives are only supported in Telnet and SSH; the Rlogin and Raw protocols offer no way of implementing them. (For an alternative, see section 4.14.3.) +Keepalives are only supported in Telnet and SSH; the Rlogin, SUPDUP, and Raw protocols offer no way of implementing them. (For an alternative, see section 4.14.3.)

      -Note that if you are using SSH-1 and the server has a bug that makes it unable to deal with SSH-1 ignore messages (see section 4.28.11), enabling keepalives will have no effect. +Note that if you are using SSH-1 and the server has a bug that makes it unable to deal with SSH-1 ignore messages (see section 4.26.11), enabling keepalives will have no effect.

      -

      4.14.2 ‘Disable Nagle's algorithm’

      +

      4.14.2 ‘Disable Nagle's algorithm’

      -Nagle's algorithm is a detail of TCP/IP implementations that tries to minimise the number of small data packets sent down a network connection. With Nagle's algorithm enabled, PuTTY's bandwidth usage will be slightly more efficient; with it disabled, you may find you get a faster response to your keystrokes when connecting to some types of server. +Nagle's algorithm is a detail of TCP/IP implementations that tries to minimise the number of small data packets sent down a network connection. With Nagle's algorithm enabled, PuTTY's bandwidth usage will be slightly more efficient; with it disabled, you may find you get a faster response to your keystrokes when connecting to some types of server.

      -The Nagle algorithm is disabled by default for interactive connections. +The Nagle algorithm is disabled by default for interactive connections.

      -

      4.14.3 ‘Enable TCP keepalives’

      +

      4.14.3 ‘Enable TCP keepalives’

      NOTE: TCP keepalives should not be confused with the application-level keepalives described in section 4.14.1. If in doubt, you probably want application-level keepalives; TCP keepalives are provided for completeness.

      @@ -1272,7 +1305,7 @@

      4.14.3 ‘Enab The idea of TCP keepalives is similar to application-level keepalives, and the same caveats apply. The main differences are:

      • -TCP keepalives are available on all connection types, including Raw and Rlogin. +TCP keepalives are available on all network connection types, including Raw, Rlogin, and SUPDUP.
      • The interval between TCP keepalives is usually much longer, typically two hours; this is set by the operating system, and cannot be configured within PuTTY. @@ -1282,22 +1315,22 @@

        4.14.3 ‘Enab

      -TCP keepalives may be more useful for ensuring that half-open connections are terminated than for keeping a connection alive. +TCP keepalives may be more useful for ensuring that half-open connections are terminated than for keeping a connection alive.

      TCP keepalives are disabled by default.

      -

      4.14.4 ‘Internet protocol version’

      +

      4.14.4 ‘Internet protocol version’

      -This option allows the user to select between the old and new Internet protocols and addressing schemes (IPv4 and IPv6). The selected protocol will be used for most outgoing network connections (including connections to proxies); however, tunnels have their own configuration, for which see section 4.27.2. +This option allows the user to select between the old and new Internet protocols and addressing schemes (IPv4 and IPv6). The selected protocol will be used for most outgoing network connections (including connections to proxies); however, tunnels have their own configuration, for which see section 4.25.2.

      -The default setting is ‘Auto’, which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify a literal Internet address, it will use whichever protocol that address implies. If you provide a hostname, it will see what kinds of address exist for that hostname; it will use IPv6 if there is an IPv6 address available, and fall back to IPv4 if not.) +The default setting is ‘Auto’, which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify a literal Internet address, it will use whichever protocol that address implies. If you provide a hostname, it will see what kinds of address exist for that hostname; it will use IPv6 if there is an IPv6 address available, and fall back to IPv4 if not.)

      If you need to force PuTTY to use a particular protocol, you can explicitly set this to ‘IPv4’ or ‘IPv6’.

      -

      4.14.5 ‘Logical name of remote host’

      +

      4.14.5 ‘Logical name of remote host’

      This allows you to tell PuTTY that the host it will really end up connecting to is different from where it thinks it is making a network connection.

      @@ -1305,10 +1338,10 @@

      4.14.5 localhost port 10022) were forwarded to a second machine's SSH port (say, foovax port 22), and then started a second PuTTY connecting to the forwarded port.

      -In normal usage, the second PuTTY will access the host key cache under the host name and port it actually connected to (i.e. localhost port 10022 in this example). Using the logical host name option, however, you can configure the second PuTTY to cache the host key under the name of the host you know that it's really going to end up talking to (here foovax). +In normal usage, the second PuTTY will access the host key cache under the host name and port it actually connected to (i.e. localhost port 10022 in this example). Using the logical host name option, however, you can configure the second PuTTY to cache the host key under the name of the host you know that it's really going to end up talking to (here foovax).

      -This can be useful if you expect to connect to the same actual server through many different channels (perhaps because your port forwarding arrangements keep changing): by consistently setting the logical host name, you can arrange that PuTTY will not keep asking you to reconfirm its host key. Conversely, if you expect to use the same local port number for port forwardings to lots of different servers, you probably didn't want any particular server's host key cached under that local port number. (For this latter case, you could instead explicitly configure host keys in the relevant sessions; see section 4.21.2.) +This can be useful if you expect to connect to the same actual server through many different channels (perhaps because your port forwarding arrangements keep changing): by consistently setting the logical host name, you can arrange that PuTTY will not keep asking you to reconfirm its host key. Conversely, if you expect to use the same local port number for port forwardings to lots of different servers, you probably didn't want any particular server's host key cached under that local port number. (For this latter case, you could instead explicitly configure host keys in the relevant sessions; see section 4.19.3.)

      If you just enter a host name for this option, PuTTY will cache the SSH host key under the default SSH port for that host, irrespective of the port you really connected to (since the typical scenario is like the above example: you connect to a silly real port number and your connection ends up forwarded to the normal port-22 SSH server of some other machine). To override this, you can append a port number to the logical host name, separated by a colon. E.g. entering ‘foovax:2200’ as the logical host name will cause the host key to be cached as if you had connected to port 2200 of foovax. @@ -1323,9 +1356,9 @@

      4.15 The Data panel

      Each option on this panel applies to more than one protocol. Options which apply to only one protocol appear on that protocol's configuration panels.

      -

      4.15.1 ‘Auto-login username’

      +

      4.15.1 ‘Auto-login username’

      -All three of the SSH, Telnet and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.) +All three of the SSH, Telnet, and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.)

      In this box you can type that user name. @@ -1335,27 +1368,27 @@

      4.15.2 Use of s When the previous box (section 4.15.1) is left blank, by default, PuTTY will prompt for a username at the time you make a connection.

      -In some environments, such as the networks of large organisations implementing single sign-on, a more sensible default may be to use the name of the user logged in to the local operating system (if any); this is particularly likely to be useful with GSSAPI key exchange and user authentication (see section 4.24 and section 4.20.1.1). This control allows you to change the default behaviour. +In some environments, such as the networks of large organisations implementing single sign-on, a more sensible default may be to use the name of the user logged in to the local operating system (if any); this is particularly likely to be useful with GSSAPI key exchange and user authentication (see section 4.22 and section 4.18.1.1). This control allows you to change the default behaviour.

      The current system username is displayed in the dialog as a convenience. It is not saved in the configuration; if a saved session is later used by a different user, that user's name will be used.

      -

      4.15.3 ‘Terminal-type string’

      +

      4.15.3 ‘Terminal-type string’

      -Most servers you might connect to with PuTTY are designed to be connected to from lots of different types of terminal. In order to send the right control sequences to each one, the server will need to know what type of terminal it is dealing with. Therefore, each of the SSH, Telnet and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a Unix server, this selects an entry from the termcap or terminfo database that tells applications what control sequences to send to the terminal, and what character sequences to expect the keyboard to generate. +Most servers you might connect to with PuTTY are designed to be connected to from lots of different types of terminal. In order to send the right control sequences to each one, the server will need to know what type of terminal it is dealing with. Therefore, each of the SSH, Telnet, and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a Unix server, this selects an entry from the termcap or terminfo database that tells applications what control sequences to send to the terminal, and what character sequences to expect the keyboard to generate.

      -PuTTY attempts to emulate the Unix xterm program, and by default it reflects this by sending xterm as a terminal-type string. If you find this is not doing what you want - perhaps the remote system reports ‘Unknown terminal type’ - you could try setting this to something different, such as vt220. +PuTTY attempts to emulate the Unix xterm program, and by default it reflects this by sending xterm as a terminal-type string. If you find this is not doing what you want - perhaps the remote system reports ‘Unknown terminal type’ - you could try setting this to something different, such as vt220.

      If you're not sure whether a problem is due to the terminal type setting or not, you probably need to consult the manual for your application or your server.

      -

      4.15.4 ‘Terminal speeds’

      +

      4.15.4 ‘Terminal speeds’

      The Telnet, Rlogin, and SSH protocols allow the client to specify terminal speeds to the server.

      -This parameter does not affect the actual speed of the connection, which is always ‘as fast as possible’; it is just a hint that is sometimes used by server software to modify its behaviour. For instance, if a slow speed is indicated, the server may switch to a less bandwidth-hungry display mode. +This parameter does not affect the actual speed of the connection, which is always ‘as fast as possible’; it is just a hint that is sometimes used by server software to modify its behaviour. For instance, if a slow speed is indicated, the server may switch to a less bandwidth-hungry display mode.

      The value is usually meaningless in a network environment, but PuTTY lets you configure it, in case you find the server is reacting badly to the default value. @@ -1366,12 +1399,12 @@

      4.15.4 ‘4.15.5 Setting environment variables on the server

      +

      4.15.5 Setting environment variables on the server

      The Telnet protocol provides a means for the client to pass environment variables to the server. Many Telnet servers have stopped supporting this feature due to security flaws, but PuTTY still supports it for the benefit of any servers which have found other ways around the security problems than just disabling the whole mechanism.

      -Version 2 of the SSH protocol also provides a similar mechanism, which is easier to implement without security flaws. Newer SSH-2 servers are more likely to support it than older ones. +Version 2 of the SSH protocol also provides a similar mechanism, which is easier to implement without security flaws. Newer SSH-2 servers are more likely to support it than older ones.

      This configuration data is not used in the SSH-1, rlogin or raw protocols. @@ -1381,7 +1414,7 @@

      4.15.5 Setting 4.16 The Proxy panel

      -The Proxy panel allows you to configure PuTTY to use various types of proxy in order to make its network connections. The settings in this panel affect the primary network connection forming your PuTTY session, and also any extra connections made as a result of SSH port forwarding (see section 3.5). +The Proxy panel allows you to configure PuTTY to use various types of proxy in order to make its network connections. The settings in this panel affect the primary network connection forming your PuTTY session, and also any extra connections made as a result of SSH port forwarding (see section 3.5).

      Note that unlike some software (such as web browsers), PuTTY does not attempt to automatically determine whether to use a proxy and (if so) which one to use for a given destination. If you need to use a proxy, it must always be explicitly configured. @@ -1391,24 +1424,24 @@

      4.16.1 Setting the pro The ‘Proxy type’ radio buttons allow you to configure what type of proxy you want PuTTY to use for its network connections. The default setting is ‘None’; in this mode no proxy is used for any connection.

      • -Selecting ‘HTTP’ allows you to proxy your connections through a web server supporting the HTTP CONNECT command, as documented in RFC 2817. +Selecting ‘HTTP’ allows you to proxy your connections through a web server supporting the HTTP CONNECT command, as documented in RFC 2817.
      • -Selecting ‘SOCKS 4’ or ‘SOCKS 5’ allows you to proxy your connections through a SOCKS server. +Selecting ‘SOCKS 4’ or ‘SOCKS 5’ allows you to proxy your connections through a SOCKS server.
      • -Many firewalls implement a less formal type of proxy in which a user can make a Telnet connection directly to the firewall machine and enter a command such as connect myhost.com 22 to connect through to an external host. Selecting ‘Telnet’ allows you to tell PuTTY to use this type of proxy. +Many firewalls implement a less formal type of proxy in which a user can make a Telnet connection directly to the firewall machine and enter a command such as connect myhost.com 22 to connect through to an external host. Selecting ‘Telnet’ allows you to tell PuTTY to use this type of proxy.
      • -Selecting ‘Local’ allows you to specify an arbitrary command on the local machine to act as a proxy. When the session is started, instead of creating a TCP connection, PuTTY runs the command (specified in section 4.16.5), and uses its standard input and output streams. +Selecting ‘Local’ allows you to specify an arbitrary command on the local machine to act as a proxy. When the session is started, instead of creating a TCP connection, PuTTY runs the command (specified in section 4.16.5), and uses its standard input and output streams.

        This could be used, for instance, to talk to some kind of network proxy that PuTTY does not natively support; or you could tunnel a connection over something other than TCP/IP entirely.

        -If you want your local proxy command to make a secondary SSH connection to a proxy host and then tunnel the primary connection over that, you might well want the -nc command-line option in Plink. See section 3.8.3.14 for more information. +If you want your local proxy command to make a secondary SSH connection to a proxy host and then tunnel the primary connection over that, you might well want the -nc command-line option in Plink. See section 3.11.3.14 for more information.

        -You can also enable this mode on the command line; see section 3.8.3.24. +You can also enable this mode on the command line; see section 3.11.3.26.

      • @@ -1436,14 +1469,14 @@

        4.16.2 Excluding pa This excludes both of the above ranges at once.

        -Connections to the local host (the host name localhost, and any loopback IP address) are never proxied, even if the proxy exclude list does not explicitly contain them. It is very unlikely that this behaviour would ever cause problems, but if it does you can change it by enabling ‘Consider proxying local host connections’. +Connections to the local host (the host name localhost, and any loopback IP address) are never proxied, even if the proxy exclude list does not explicitly contain them. It is very unlikely that this behaviour would ever cause problems, but if it does you can change it by enabling ‘Consider proxying local host connections’.

        -Note that if you are doing DNS at the proxy (see section 4.16.3), you should make sure that your proxy exclusion settings do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list. +Note that if you are doing DNS at the proxy (see section 4.16.3), you should make sure that your proxy exclusion settings do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list.

        -

        4.16.3 Name resolution when using a proxy

        +

        4.16.3 Name resolution when using a proxy

        -If you are using a proxy to access a private network, it can make a difference whether DNS name resolution is performed by PuTTY itself (on the client machine) or performed by the proxy. +If you are using a proxy to access a private network, it can make a difference whether DNS name resolution is performed by PuTTY itself (on the client machine) or performed by the proxy.

        The ‘Do DNS name lookup at proxy end’ configuration option allows you to control this. If you set it to ‘No’, PuTTY will always do its own DNS, and will always pass an IP address to the proxy. If you set it to ‘Yes’, PuTTY will always pass host names straight to the proxy without trying to look them up first. @@ -1457,12 +1490,12 @@

        4.16.3 <

        The original SOCKS 4 protocol does not support proxy-side DNS. There is a protocol extension (SOCKS 4A) which does support it, but not all SOCKS 4 servers provide this extension. If you enable proxy DNS and your SOCKS 4 server cannot deal with it, this might be why.

        -

        4.16.4 Username and password

        +

        4.16.4 Username and password

        -If your proxy requires authentication, you can enter a username and a password in the ‘Username’ and ‘Password’ boxes. +If your proxy requires authentication, you can enter a username and a password in the ‘Username’ and ‘Password’ boxes.

        -Note that if you save your session, the proxy password will be saved in plain text, so anyone who can access your PuTTY configuration data will be able to discover it. +Note that if you save your session, the proxy password will be saved in plain text, so anyone who can access your PuTTY configuration data will be able to discover it.

        Authentication is not fully supported for all forms of proxy: @@ -1470,10 +1503,10 @@

        4.16.4
        • Username and password authentication is supported for HTTP proxies and SOCKS 5 proxies.
          • -With SOCKS 5, authentication is via CHAP if the proxy supports it (this is not supported in PuTTYtel); otherwise the password is sent to the proxy in plain text. +With SOCKS 5, authentication is via CHAP if the proxy supports it (this is not supported in PuTTYtel); otherwise the password is sent to the proxy in plain text.
          • -With HTTP proxying, the only currently supported authentication method is ‘basic’, where the password is sent to the proxy in plain text. +With HTTP proxying, the only currently supported authentication method is ‘basic’, where the password is sent to the proxy in plain text.
          @@ -1487,10 +1520,10 @@

          4.16.4

        4.16.5 Specifying the Telnet or Local proxy command

        -If you are using the Telnet proxy type, the usual command required by the firewall's Telnet server is connect, followed by a host name and a port number. If your proxy needs a different command, you can enter an alternative here. +If you are using the Telnet proxy type, the usual command required by the firewall's Telnet server is connect, followed by a host name and a port number. If your proxy needs a different command, you can enter an alternative here.

        -If you are using the Local proxy type, the local command to run is specified here. +If you are using the Local proxy type, the local command to run is specified here.

        In this string, you can use \n to represent a new-line, \r to represent a carriage return, \t to represent a tab character, and \x followed by two hex digits to represent any other character. \\ is used to encode the \ character itself. @@ -1506,7 +1539,7 @@

        4.16.5 Specifying t

        This will send your username and password as the first two lines to the proxy, followed by a command to connect to the desired host and port. Note that if you do not include the %user or %pass tokens in the Telnet command, then the ‘Username’ and ‘Password’ configuration fields will be ignored.

        -

        4.16.6 Controlling proxy logging

        +

        4.16.6 Controlling proxy logging

        Often the proxy interaction has its own diagnostic output; this is particularly the case for local proxy commands.

        @@ -1516,112 +1549,37 @@

        4.16.6 Controlling

        By default (‘No’), proxy diagnostics are only sent to the Event Log; with ‘Yes’ they are also printed to the terminal, where they may get mixed up with your main session. ‘Only until session starts’ is a compromise; proxy messages will go to the terminal window until the main session is deemed to have started (in a protocol-dependent way), which is when they're most likely to be interesting; any further proxy-related messages during the session will only go to the Event Log.

        -

        4.17 The Telnet panel

        -

        -The Telnet panel allows you to configure options that only apply to Telnet sessions. -

        -

        4.17.1 ‘Handling of OLD_ENVIRON ambiguity’

        -

        -The original Telnet mechanism for passing environment variables was badly specified. At the time the standard (RFC 1408) was written, BSD telnet implementations were already supporting the feature, and the intention of the standard was to describe the behaviour the BSD implementations were already using. -

        -

        -Sadly there was a typing error in the standard when it was issued, and two vital function codes were specified the wrong way round. BSD implementations did not change, and the standard was not corrected. Therefore, it's possible you might find either BSD or RFC-compliant implementations out there. This switch allows you to choose which one PuTTY claims to be. -

        -

        -The problem was solved by issuing a second standard, defining a new Telnet mechanism called NEW_ENVIRON, which behaved exactly like the original OLD_ENVIRON but was not encumbered by existing implementations. Most Telnet servers now support this, and it's unambiguous. This feature should only be needed if you have trouble passing environment variables to quite an old server. -

        -

        4.17.2 Passive and active Telnet negotiation modes

        -

        -In a Telnet connection, there are two types of data passed between the client and the server: actual text, and negotiations about which Telnet extra features to use. -

        -

        -PuTTY can use two different strategies for negotiation: -

        -
        • -In active mode, PuTTY starts to send negotiations as soon as the connection is opened. -
        • -
        • -In passive mode, PuTTY will wait to negotiate until it sees a negotiation from the server. -
        • -
        -

        -The obvious disadvantage of passive mode is that if the server is also operating in a passive mode, then negotiation will never begin at all. For this reason PuTTY defaults to active mode. -

        -

        -However, sometimes passive mode is required in order to successfully get through certain types of firewall and Telnet proxy server. If you have confusing trouble with a firewall, you could try enabling passive mode to see if it helps. -

        -

        4.17.3 ‘Keyboard sends Telnet special commands’

        -

        -If this box is checked, several key sequences will have their normal actions modified: -

        -
        • -the Backspace key on the keyboard will send the Telnet special backspace code; -
        • -
        • -Control-C will send the Telnet special Interrupt Process code; -
        • -
        • -Control-Z will send the Telnet special Suspend Process code. -
        • -
        -

        -You probably shouldn't enable this unless you know what you're doing. -

        -

        4.17.4 ‘Return key sends Telnet New Line instead of ^M’

        -

        -Unlike most other remote login protocols, the Telnet protocol has a special ‘new line’ code that is not the same as the usual line endings of Control-M or Control-J. By default, PuTTY sends the Telnet New Line code when you press Return, instead of sending Control-M as it does in most other protocols. -

        -

        -Most Unix-style Telnet servers don't mind whether they receive Telnet New Line or Control-M; some servers do expect New Line, and some servers prefer to see ^M. If you are seeing surprising behaviour when you press Return in a Telnet session, you might try turning this option off to see if it helps. -

        -

        4.18 The Rlogin panel

        -

        -The Rlogin panel allows you to configure options that only apply to Rlogin sessions. -

        -

        4.18.1 ‘Local username’

        -

        -Rlogin allows an automated (password-free) form of login by means of a file called .rhosts on the server. You put a line in your .rhosts file saying something like jbloggs@pc1.example.com, and then when you make an Rlogin connection the client transmits the username of the user running the Rlogin client. The server checks the username and hostname against .rhosts, and if they match it does not ask for a password. -

        -

        -This only works because Unix systems contain a safeguard to stop a user from pretending to be another user in an Rlogin connection. Rlogin connections have to come from port numbers below 1024, and Unix systems prohibit this to unprivileged processes; so when the server sees a connection from a low-numbered port, it assumes the client end of the connection is held by a privileged (and therefore trusted) process, so it believes the claim of who the user is. -

        -

        -Windows does not have this restriction: any user can initiate an outgoing connection from a low-numbered port. Hence, the Rlogin .rhosts mechanism is completely useless for securely distinguishing several different users on a Windows machine. If you have a .rhosts entry pointing at a Windows PC, you should assume that anyone using that PC can spoof your username in an Rlogin connection and access your account on the server. -

        +

        4.17 The SSH panel

        -The ‘Local username’ control allows you to specify what user name PuTTY should claim you have, in case it doesn't match your Windows user name (or in case you didn't bother to set up a Windows user name). +The SSH panel allows you to configure options that only apply to SSH sessions.

        -

        4.19 The SSH panel

        +

        4.17.1 Executing a specific command on the server

        -The SSH panel allows you to configure options that only apply to SSH sessions. -

        -

        4.19.1 Executing a specific command on the server

        -

        -In SSH, you don't have to run a general shell session on the server. Instead, you can choose to run a single specific command (such as a mail user agent, for example). If you want to do this, enter the command in the ‘Remote command’ box. +In SSH, you don't have to run a general shell session on the server. Instead, you can choose to run a single specific command (such as a mail user agent, for example). If you want to do this, enter the command in the ‘Remote command’ box.

        Note that most servers will close the session after executing the command.

        -

        4.19.2 ‘Don't start a shell or command at all’

        +

        4.17.2 ‘Don't start a shell or command at all’

        -If you tick this box, PuTTY will not attempt to run a shell or command after connecting to the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell. +If you tick this box, PuTTY will not attempt to run a shell or command after connecting to the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell.

        -This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). +This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell).

        -This feature can also be enabled using the -N command-line option; see section 3.8.3.13. +This feature can also be enabled using the -N command-line option; see section 3.11.3.13.

        If you use this feature in Plink, you will not be able to terminate the Plink process by any graceful means; the only way to kill it will be by pressing Control-C or sending a kill signal from another program.

        -

        4.19.3 ‘Enable compression’

        +

        4.17.3 ‘Enable compression’

        -This enables data compression in the SSH connection: data sent by the server is compressed before sending, and decompressed at the client end. Likewise, data sent by PuTTY to the server is compressed first and the server decompresses it at the other end. This can help make the most of a low-bandwidth connection. +This enables data compression in the SSH connection: data sent by the server is compressed before sending, and decompressed at the client end. Likewise, data sent by PuTTY to the server is compressed first and the server decompresses it at the other end. This can help make the most of a low-bandwidth connection.

        -

        4.19.4 ‘SSH protocol version’

        +

        4.17.4 ‘SSH protocol version’

        -This allows you to select whether to use SSH protocol version 2 or the older version 1. +This allows you to select whether to use SSH protocol version 2 or the older version 1.

        You should normally leave this at the default of ‘2’. As well as having fewer features, the older SSH-1 protocol is no longer developed, has many known cryptographic weaknesses, and is generally not considered to be secure. PuTTY's protocol 1 implementation is provided mainly for compatibility, and is no longer being enhanced. @@ -1632,7 +1590,7 @@

        4.19.4 ‘4.19.5 Sharing an SSH connection between PuTTY tools

        +

        4.17.5 Sharing an SSH connection between PuTTY tools

        The controls in this box allow you to configure PuTTY to reuse an existing SSH connection, where possible.

        @@ -1663,12 +1621,12 @@

        4.19.5 Sharing an SSH

        It is possible to test programmatically for the existence of a live upstream using Plink. See section 7.2.3.4.

        -

        4.20 The Kex panel

        +

        4.18 The Kex panel

        -The Kex panel (short for ‘key exchange’) allows you to configure options related to SSH-2 key exchange. +The Kex panel (short for ‘key exchange’) allows you to configure options related to SSH-2 key exchange.

        -Key exchange occurs at the start of an SSH connection (and occasionally thereafter); it establishes a shared secret that is used as the basis for all of SSH's security features. It is therefore very important for the security of the connection that the key exchange is secure. +Key exchange occurs at the start of an SSH connection (and occasionally thereafter); it establishes a shared secret that is used as the basis for all of SSH's security features. It is therefore very important for the security of the connection that the key exchange is secure.

        Key exchange is a cryptographically intensive process; if either the client or the server is a relatively slow machine, the slower methods may take several tens of seconds to complete. @@ -1682,15 +1640,15 @@

        4.20 The Kex panel

        This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all.

        -

        4.20.1 Key exchange algorithm selection

        +

        4.18.1 Key exchange algorithm selection

        -PuTTY supports a variety of SSH-2 key exchange methods, and allows you to choose which one you prefer to use; configuration is similar to cipher selection (see section 4.22). +PuTTY supports a variety of SSH-2 key exchange methods, and allows you to choose which one you prefer to use; configuration is similar to cipher selection (see section 4.20).

        PuTTY currently supports the following key exchange methods:

        • -‘ECDH’: elliptic curve Diffie-Hellman key exchange. +‘ECDH’: elliptic curve Diffie-Hellman key exchange.
        • ‘Group 14’: Diffie-Hellman key exchange with a well-known 2048-bit group. @@ -1699,19 +1657,19 @@

          4.20.1 Group exchange’: with this method, instead of using a fixed group, PuTTY requests that the server suggest a group to use for key exchange; the server can avoid groups known to be weak, and possibly invent new ones over time, without any changes required to PuTTY's configuration. We recommend use of this method instead of the well-known groups, if possible. +‘Group exchange’: with this method, instead of using a fixed group, PuTTY requests that the server suggest a group to use for key exchange; the server can avoid groups known to be weak, and possibly invent new ones over time, without any changes required to PuTTY's configuration. We recommend use of this method instead of the well-known groups, if possible.

        • -‘RSA key exchange’: this requires much less computational effort on the part of the client, and somewhat less on the part of the server, than Diffie-Hellman key exchange. +‘RSA key exchange’: this requires much less computational effort on the part of the client, and somewhat less on the part of the server, than Diffie-Hellman key exchange.
        • -‘GSSAPI key exchange’: see section 4.20.1.1. +‘GSSAPI key exchange’: see section 4.18.1.1.

        -If the first algorithm PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.22). +If the first algorithm PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.20).

        -

        4.20.1.1 GSSAPI-based key exchange

        +

        4.18.1.1 GSSAPI-based key exchange

        PuTTY supports a set of key exchange methods that also incorporates GSSAPI-based authentication. They are enabled with the ‘Attempt GSSAPI key exchange’ checkbox (which also appears on the ‘GSSAPI’ panel).

        @@ -1719,7 +1677,7 @@

        4.20.1.1 GSSAPI- PuTTY can only perform the GSSAPI-authenticated key exchange methods when using Kerberos V5, and not other GSSAPI mechanisms. If the user running PuTTY has current Kerberos V5 credentials, then PuTTY will select the GSSAPI key exchange methods in preference to any of the ordinary SSH key exchange methods configured in the preference list.

        -The advantage of doing GSSAPI authentication as part of the SSH key exchange is apparent when you are using credential delegation (see section 4.24.1). The SSH key exchange can be repeated later in the session, and this allows your Kerberos V5 credentials (which are typically short-lived) to be automatically re-delegated to the server when they are refreshed on the client. (This feature is commonly referred to as ‘cascading credentials’.) +The advantage of doing GSSAPI authentication as part of the SSH key exchange is apparent when you are using credential delegation (see section 4.22.1). The SSH key exchange can be repeated later in the session, and this allows your Kerberos V5 credentials (which are typically short-lived) to be automatically re-delegated to the server when they are refreshed on the client. (This feature is commonly referred to as ‘cascading credentials’.)

        If your server doesn't support GSSAPI key exchange, it may still support GSSAPI in the SSH user authentication phase. This will still let you log in using your Kerberos credentials, but will only allow you to delegate the credentials that are active at the beginning of the session; they can't be refreshed automatically later, in a long-running session. @@ -1727,7 +1685,7 @@

        4.20.1.1 GSSAPI-

        Another effect of GSSAPI key exchange is that it replaces the usual SSH mechanism of permanent host keys described in section 2.2. So if you use this method, then you won't be asked any interactive questions about whether to accept the server's host key. Instead, the Kerberos exchange will verify the identity of the host you connect to, at the same time as verifying your identity to it.

        -

        4.20.2 Repeat key exchange

        +

        4.18.2 Repeat key exchange

        If the session key negotiated at connection startup is used too much or for too long, it may become feasible to mount attacks against the SSH connection. Therefore, the SSH-2 protocol specifies that a new key exchange should take place every so often; this can be initiated by either the client or the server.

        @@ -1742,7 +1700,7 @@

        4.20.2 keepalives aren't always helpful. If you anticipate suffering a network dropout of several hours in the middle of an SSH connection, but were not actually planning to send data down that connection during those hours, then an attempted rekey in the middle of the dropout will probably cause the connection to be abandoned, whereas if rekeys are disabled then the connection should in principle survive (in the absence of interfering firewalls). See section 4.14.1 for more discussion of these issues; for these purposes, rekeys have much the same properties as keepalives. (Except that rekeys have cryptographic value in themselves, so you should bear that in mind when deciding whether to turn them off.) Note, however, the the SSH server can still initiate rekeys. +You might have a need to disable time-based rekeys completely for the same reasons that keepalives aren't always helpful. If you anticipate suffering a network dropout of several hours in the middle of an SSH connection, but were not actually planning to send data down that connection during those hours, then an attempted rekey in the middle of the dropout will probably cause the connection to be abandoned, whereas if rekeys are disabled then the connection should in principle survive (in the absence of interfering firewalls). See section 4.14.1 for more discussion of these issues; for these purposes, rekeys have much the same properties as keepalives. (Except that rekeys have cryptographic value in themselves, so you should bear that in mind when deciding whether to turn them off.) Note, however, the the SSH server can still initiate rekeys.

        • ‘Minutes between GSSAPI checks’, if you're using GSSAPI key exchange, specifies how often the GSSAPI credential cache is checked to see whether new tickets are available for delegation, or current ones are near expiration. If forwarding of GSSAPI credentials is enabled, PuTTY will try to rekey as necessary to keep the delegated credentials from expiring. Frequent checks are recommended; rekeying only happens when needed. @@ -1766,11 +1724,11 @@

          4.20.2 integrity, and to a lesser extent, confidentiality of the SSH-2 protocol depend in part on rekeys occurring before a 32-bit packet sequence number wraps around. Unlike time-based rekeys, data-based rekeys won't occur when the SSH connection is idle, so they shouldn't cause the same problems. The SSH-1 protocol, incidentally, has even weaker integrity protection than SSH-2 without rekeys. +Disabling data-based rekeys entirely is a bad idea. The integrity, and to a lesser extent, confidentiality of the SSH-2 protocol depend in part on rekeys occurring before a 32-bit packet sequence number wraps around. Unlike time-based rekeys, data-based rekeys won't occur when the SSH connection is idle, so they shouldn't cause the same problems. The SSH-1 protocol, incidentally, has even weaker integrity protection than SSH-2 without rekeys.

          -

          4.21 The Host Keys panel

          +

          4.19 The Host Keys panel

          -The Host Keys panel allows you to configure options related to SSH-2 host key management. +The Host Keys panel allows you to configure options related to SSH-2 host key management.

          Host keys are used to prove the server's identity, and assure you that the server is not being spoofed (either by a man-in-the-middle attack or by completely replacing it on the network). See section 2.2 for a basic introduction to host keys. @@ -1778,36 +1736,52 @@

          4.21 The Host Keys pane

          This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all.

          -

          4.21.1 Host key type selection

          +

          4.19.1 Host key type selection

          -PuTTY supports a variety of SSH-2 host key types, and allows you to choose which one you prefer to use to identify the server. Configuration is similar to cipher selection (see section 4.22). +PuTTY supports a variety of SSH-2 host key types, and allows you to choose which one you prefer to use to identify the server. Configuration is similar to cipher selection (see section 4.20).

          PuTTY currently supports the following host key types:

          • -‘Ed25519’: Edwards-curve DSA using a twisted Edwards curve with modulus 2^255-19. +‘Ed25519’: Edwards-curve DSA using a twisted Edwards curve with modulus 2^255-19.
          • -‘ECDSA’: elliptic curve DSA using one of the NIST-standardised elliptic curves. +‘Ed448’: another Edwards-curve DSA type, using a larger elliptic curve with a 448-bit instead of 255-bit modulus (so it has a higher security level than Ed25519).
          • -‘DSA’: straightforward DSA using modular exponentiation. +‘ECDSA’: elliptic curve DSA using one of the NIST-standardised elliptic curves.
          • -‘RSA’: the ordinary RSA algorithm. +‘DSA’: straightforward DSA using modular exponentiation. +
          • +
          • +‘RSA’: the ordinary RSA algorithm.

          -If PuTTY already has one or more host keys stored for the server, it will prefer to use one of those, even if the server has a key type that is higher in the preference order. You can add such a key to PuTTY's cache from within an existing session using the ‘Special Commands’ menu; see section 3.1.3.2. +If PuTTY already has one or more host keys stored for the server, it will by default prefer to use one of those, even if the server has a key type that is higher in the preference order. You can add such a key to PuTTY's cache from within an existing session using the ‘Special Commands’ menu; see section 3.1.3.2.

          Otherwise, PuTTY will choose a key type based purely on the preference order you specify in the configuration.

          -If the first key type PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.22). +If the first key type PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.20). +

          +

          4.19.2 Preferring known host keys

          +

          +By default, PuTTY will adjust the preference order for host key algorithms so that any host keys it already knows are moved to the top of the list. +

          +

          +This prevents you from having to check and confirm a new host key for a server you already had one for (e.g. because the server has generated an alternative key of a type higher in PuTTY's preference order, or because you changed the preference order itself).

          -

          4.21.2 Manually configuring host keys

          +

          +However, on the other hand, it can leak information to a listener in the network about whether you already know a host key for this server. +

          +

          +For this reason, this policy is configurable. By turning this checkbox off, you can reset PuTTY to always use the exact order of host key algorithms configured in the preference list described in section 4.19.1, so that a listener will find out nothing about what keys you had stored. +

          +

          4.19.3 Manually configuring host keys

          In some situations, if PuTTY's automated host key management is not doing what you need, you might need to manually configure PuTTY to accept a specific host key, or one of a specific set of host keys.

          @@ -1815,7 +1789,7 @@

          4.21.2

          -Another reason is if PuTTY's automated host key management is completely unavailable, e.g. because PuTTY (or Plink or PSFTP, etc) is running in a Windows environment without access to the Registry. In that situation, you will probably want to use the -hostkey command-line option to configure the expected host key(s); see section 3.8.3.20. +Another reason is if PuTTY's automated host key management is completely unavailable, e.g. because PuTTY (or Plink or PSFTP, etc) is running in a Windows environment without access to the Registry. In that situation, you will probably want to use the -hostkey command-line option to configure the expected host key(s); see section 3.11.3.21.

          For situations where PuTTY's automated host key management simply picks the wrong host name to store a key under, you may want to consider setting a ‘logical host name’ instead; see section 4.14.5. @@ -1827,42 +1801,45 @@

          4.21.2

          -If this box contains at least one host key or fingerprint when PuTTY makes an SSH connection, then PuTTY's automated host key management is completely bypassed: the connection will be permitted if and only if the host key presented by the server is one of the keys listed in this box, and the host key store in the Registry will be neither read nor written, unless you explicitly do so. +If this box contains at least one host key or fingerprint when PuTTY makes an SSH connection, then PuTTY's automated host key management is completely bypassed: the connection will be permitted if and only if the host key presented by the server is one of the keys listed in this box, and the host key store in the Registry will be neither read nor written, unless you explicitly do so.

          If the box is empty (as it usually is), then PuTTY's automated host key management will work as normal.

          -

          4.22 The Cipher panel

          +

          4.20 The Cipher panel

          -PuTTY supports a variety of different encryption algorithms, and allows you to choose which one you prefer to use. You can do this by dragging the algorithms up and down in the list box (or moving them using the Up and Down buttons) to specify a preference order. When you make an SSH connection, PuTTY will search down the list from the top until it finds an algorithm supported by the server, and then use that. +PuTTY supports a variety of different encryption algorithms, and allows you to choose which one you prefer to use. You can do this by dragging the algorithms up and down in the list box (or moving them using the Up and Down buttons) to specify a preference order. When you make an SSH connection, PuTTY will search down the list from the top until it finds an algorithm supported by the server, and then use that.

          PuTTY currently supports the following algorithms:

          • -ChaCha20-Poly1305, a combined cipher and MAC (SSH-2 only) +ChaCha20-Poly1305, a combined cipher and MAC (SSH-2 only)
          • -AES (Rijndael) - 256, 192, or 128-bit SDCTR or CBC (SSH-2 only) +AES (Rijndael) - 256, 192, or 128-bit SDCTR or CBC (SSH-2 only)
          • -Arcfour (RC4) - 256 or 128-bit stream cipher (SSH-2 only) +Arcfour (RC4) - 256 or 128-bit stream cipher (SSH-2 only)
          • -Blowfish - 256-bit SDCTR (SSH-2 only) or 128-bit CBC +Blowfish - 256-bit SDCTR (SSH-2 only) or 128-bit CBC
          • -Triple-DES - 168-bit SDCTR (SSH-2 only) or CBC +Triple-DES - 168-bit SDCTR (SSH-2 only) or CBC
          • -Single-DES - 56-bit CBC (see below for SSH-2) +Single-DES - 56-bit CBC (see below for SSH-2)

          @@ -1882,18 +1859,18 @@

          4.22 The Cipher pane

          Single-DES is not recommended in the SSH-2 protocol standards, but one or two server implementations do support it. PuTTY can use single-DES to interoperate with these servers if you enable the ‘Enable legacy use of single-DES in SSH-2’ option; by default this is disabled and PuTTY will stick to recommended ciphers.

          -

          4.23 The Auth panel

          +

          4.21 The Auth panel

          -The Auth panel allows you to configure authentication options for SSH sessions. +The Auth panel allows you to configure authentication options for SSH sessions.

          -

          4.23.1 ‘Display pre-authentication banner’

          +

          4.21.1 ‘Display pre-authentication banner’

          -SSH-2 servers can provide a message for clients to display to the prospective user before the user logs in; this is sometimes known as a pre-authentication ‘banner’. Typically this is used to provide information about the server and legal notices. +SSH-2 servers can provide a message for clients to display to the prospective user before the user logs in; this is sometimes known as a pre-authentication ‘banner’. Typically this is used to provide information about the server and legal notices.

          By default, PuTTY displays this message before prompting for a password or similar credentials (although, unfortunately, not before prompting for a login name, due to the nature of the protocol design). By unchecking this option, display of the banner can be suppressed entirely.

          -

          4.23.2 ‘Bypass authentication entirely’

          +

          4.21.2 ‘Bypass authentication entirely’

          In SSH-2, it is in principle possible to establish a connection without using SSH's mechanisms to identify or prove who you are to the server. An SSH server could prefer to handle authentication in the data channel, for instance, or simply require no user authentication whatsoever.

          @@ -1901,12 +1878,31 @@

          4.23.2 ‘Bypass a By default, PuTTY assumes the server requires authentication (we've never heard of one that doesn't), and thus must start this process with a username. If you find you are getting username prompts that you cannot answer, you could try enabling this option. However, most SSH servers will reject this.

          -This is not the option you want if you have a username and just want PuTTY to remember it; for that see section 4.15.1. It's also probably not what if you're trying to set up passwordless login to a mainstream SSH server; depending on the server, you probably wanted public-key authentication (chapter 8) or perhaps GSSAPI authentication (section 4.24). (These are still forms of authentication, even if you don't have to interact with them.) +This is not the option you want if you have a username and just want PuTTY to remember it; for that see section 4.15.1. It's also probably not what if you're trying to set up passwordless login to a mainstream SSH server; depending on the server, you probably wanted public-key authentication (chapter 8) or perhaps GSSAPI authentication (section 4.22). (These are still forms of authentication, even if you don't have to interact with them.)

          This option only affects SSH-2 connections. SSH-1 connections always require an authentication step.

          -

          4.23.3 ‘Attempt authentication using Pageant’

          +

          4.21.3 ‘Disconnect if authentication succeeds trivially’

          +

          +This option causes PuTTY to abandon an SSH session and disconnect from the server, if the server accepted authentication without ever having asked for any kind of password or signature or token. +

          +

          +This might be used as a security measure. There are some forms of attack against an SSH client user which work by terminating the SSH authentication stage early, and then doing something in the main part of the SSH session which looks like part of the authentication, but isn't really. +

          +

          +For example, instead of demanding a signature from your public key, for which PuTTY would ask for your key's passphrase, a compromised or malicious server might allow you to log in with no signature or password at all, and then print a message that imitates PuTTY's request for your passphrase, in the hope that you would type it in. (In fact, the passphrase for your public key should not be sent to any server.) +

          +

          +PuTTY's main defence against attacks of this type is the ‘trust sigil’ system: messages in the PuTTY window that are truly originated by PuTTY itself are shown next to a small copy of the PuTTY icon, which the server cannot fake when it tries to imitate the same message using terminal output. +

          +

          +However, if you think you might be at risk of this kind of thing anyway (if you don't watch closely for the trust sigils, or if you think you're at extra risk of one of your servers being malicious), then you could enable this option as an extra defence. Then, if the server tries any of these attacks involving letting you through the authentication stage, PuTTY will disconnect from the server before it can send a follow-up fake prompt or other type of attack. +

          +

          +On the other hand, some servers legitimately let you through the SSH authentication phase trivially, either because they are genuinely public, or because the important authentication step happens during the terminal session. (An example might be an SSH server that connects you directly to the terminal login prompt of a legacy mainframe.) So enabling this option might cause some kinds of session to stop working. It's up to you. +

          +

          4.21.4 ‘Attempt authentication using Pageant’

          If this option is enabled, then PuTTY will look for Pageant (the SSH private-key storage agent) and attempt to authenticate with any suitable public keys Pageant currently holds.

          @@ -1914,58 +1910,58 @@

          4.23.3 ‘Attemp This behaviour is almost always desirable, and is therefore enabled by default. In rare cases you might need to turn it off in order to force authentication by some non-public-key method such as passwords.

          -This option can also be controlled using the -noagent command-line option. See section 3.8.3.9. +This option can also be controlled using the -noagent command-line option. See section 3.11.3.9.

          See chapter 9 for more information about Pageant in general.

          -

          4.23.4 ‘Attempt TIS or CryptoCard authentication’

          +

          4.21.5 ‘Attempt TIS or CryptoCard authentication’

          -TIS and CryptoCard authentication are (despite their names) generic forms of simple challenge/response authentication available in SSH protocol version 1 only. You might use them if you were using S/Key one-time passwords, for example, or if you had a physical security token that generated responses to authentication challenges. They can even be used to prompt for simple passwords. +TIS and CryptoCard authentication are (despite their names) generic forms of simple challenge/response authentication available in SSH protocol version 1 only. You might use them if you were using S/Key one-time passwords, for example, or if you had a physical security token that generated responses to authentication challenges. They can even be used to prompt for simple passwords.

          With this switch enabled, PuTTY will attempt these forms of authentication if the server is willing to try them. You will be presented with a challenge string (which may be different every time) and must supply the correct response in order to log in. If your server supports this, you should talk to your system administrator about precisely what form these challenges and responses take.

          -

          4.23.5 ‘Attempt keyboard-interactive authentication’

          +

          4.21.6 ‘Attempt keyboard-interactive authentication’

          -The SSH-2 equivalent of TIS authentication is called ‘keyboard-interactive’. It is a flexible authentication method using an arbitrary sequence of requests and responses; so it is not only useful for challenge/response mechanisms such as S/Key, but it can also be used for (for example) asking the user for a new password when the old one has expired. +The SSH-2 equivalent of TIS authentication is called ‘keyboard-interactive’. It is a flexible authentication method using an arbitrary sequence of requests and responses; so it is not only useful for challenge/response mechanisms such as S/Key, but it can also be used for (for example) asking the user for a new password when the old one has expired.

          PuTTY leaves this option enabled by default, but supplies a switch to turn it off in case you should have trouble with it.

          -

          4.23.6 ‘Allow agent forwarding’

          +

          4.21.7 ‘Allow agent forwarding’

          -This option allows the SSH server to open forwarded connections back to your local copy of Pageant. If you are not running Pageant, this option will do nothing. +This option allows the SSH server to open forwarded connections back to your local copy of Pageant. If you are not running Pageant, this option will do nothing.

          -See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.5 for details. +See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.6 for details.

          -

          4.23.7 ‘Allow attempted changes of username in SSH-2’

          +

          4.21.8 ‘Allow attempted changes of username in SSH-2’

          In the SSH-1 protocol, it is impossible to change username after failing to authenticate. So if you mis-type your username at the PuTTY ‘login as:’ prompt, you will not be able to change it except by restarting PuTTY.

          -The SSH-2 protocol does allow changes of username, in principle, but does not make it mandatory for SSH-2 servers to accept them. In particular, OpenSSH does not accept a change of username; once you have sent one username, it will reject attempts to try to authenticate as another user. (Depending on the version of OpenSSH, it may quietly return failure for all login attempts, or it may send an error message.) +The SSH-2 protocol does allow changes of username, in principle, but does not make it mandatory for SSH-2 servers to accept them. In particular, OpenSSH does not accept a change of username; once you have sent one username, it will reject attempts to try to authenticate as another user. (Depending on the version of OpenSSH, it may quietly return failure for all login attempts, or it may send an error message.)

          For this reason, PuTTY will by default not prompt you for your username more than once, in case the server complains. If you know your server can cope with it, you can enable the ‘Allow attempted changes of username’ option to modify PuTTY's behaviour.

          -

          4.23.8 ‘Private key file for authentication’

          +

          4.21.9 ‘Private key file for authentication’

          -This box is where you enter the name of your private key file if you are using public key authentication. See chapter 8 for information about public key authentication in SSH. +This box is where you enter the name of your private key file if you are using public key authentication. See chapter 8 for information about public key authentication in SSH.

          -This key must be in PuTTY's native format (*.PPK). If you have a private key in another format that you want to use with PuTTY, see section 8.2.12. +This key must be in PuTTY's native format (*.PPK). If you have a private key in another format that you want to use with PuTTY, see section 8.2.14.

          -You can use the authentication agent Pageant so that you do not need to explicitly configure a key here; see chapter 9. +You can use the authentication agent Pageant so that you do not need to explicitly configure a key here; see chapter 9.

          If a private key file is specified here with Pageant running, PuTTY will first try asking Pageant to authenticate with that key, and ignore any other keys Pageant may have. If that fails, PuTTY will ask for a passphrase as normal. You can also specify a public key file in this case (in RFC 4716 or OpenSSH format), as that's sufficient to identify the key to Pageant, but of course if Pageant isn't present PuTTY can't fall back to using this file itself.

          -

          4.24 The GSSAPI panel

          +

          4.22 The GSSAPI panel

          -The ‘GSSAPI’ subpanel of the ‘Auth’ panel controls the use of GSSAPI authentication. This is a mechanism which delegates the authentication exchange to a library elsewhere on the client machine, which in principle can authenticate in many different ways but in practice is usually used with the Kerberos single sign-on protocol to implement passwordless login. +The ‘GSSAPI’ subpanel of the ‘Auth’ panel controls the use of GSSAPI authentication. This is a mechanism which delegates the authentication exchange to a library elsewhere on the client machine, which in principle can authenticate in many different ways but in practice is usually used with the Kerberos single sign-on protocol to implement passwordless login.

          GSSAPI authentication is only available in the SSH-2 protocol. @@ -1974,7 +1970,7 @@

          4.24 The section 4.20.1.1 for more information about this method. The checkbox labelled ‘Attempt GSSAPI key exchange’ controls this form. (The same checkbox appears on the ‘Kex’ panel.) +In the other method, GSSAPI-based authentication is combined with the SSH key exchange phase. If this succeeds, then the SSH authentication step has nothing left to do. See section 4.18.1.1 for more information about this method. The checkbox labelled ‘Attempt GSSAPI key exchange’ controls this form. (The same checkbox appears on the ‘Kex’ panel.)

          If one or both of these controls is enabled, then GSSAPI authentication will be attempted in one form or the other, and (typically) if your client machine has valid Kerberos credentials loaded, then PuTTY should be able to authenticate automatically to servers that support Kerberos logins. @@ -1982,9 +1978,9 @@

          4.24 The 4.24.1 ‘Allow GSSAPI credential delegation’

          +

          4.22.1 ‘Allow GSSAPI credential delegation’

          -GSSAPI credential delegation is a mechanism for passing on your Kerberos (or other) identity to the session on the SSH server. If you enable this option, then not only will PuTTY be able to log in automatically to a server that accepts your Kerberos credentials, but also you will be able to connect out from that server to other Kerberos-supporting services and use the same credentials just as automatically. +GSSAPI credential delegation is a mechanism for passing on your Kerberos (or other) identity to the session on the SSH server. If you enable this option, then not only will PuTTY be able to log in automatically to a server that accepts your Kerberos credentials, but also you will be able to connect out from that server to other Kerberos-supporting services and use the same credentials just as automatically.

          (This option is the Kerberos analogue of SSH agent forwarding; see section 9.4 for some information on that.) @@ -1993,33 +1989,33 @@

          4.24.1 Note that, like SSH agent forwarding, there is a security implication in the use of this option: the administrator of the server you connect to, or anyone else who has cracked the administrator account on that server, could fake your identity when connecting to further Kerberos-supporting services. However, Kerberos sites are typically run by a central authority, so the administrator of one server is likely to already have access to the other services too; so this would typically be less of a risk than SSH agent forwarding.

          -If your connection is not using GSSAPI key exchange, it is possible for the delegation to expire during your session. See section 4.20.1.1 for more information. +If your connection is not using GSSAPI key exchange, it is possible for the delegation to expire during your session. See section 4.18.1.1 for more information.

          -

          4.24.2 Preference order for GSSAPI libraries

          +

          4.22.2 Preference order for GSSAPI libraries

          GSSAPI is a mechanism which allows more than one authentication method to be accessed through the same interface. Therefore, more than one authentication library may exist on your system which can be accessed using GSSAPI.

          -PuTTY contains native support for a few well-known such libraries (including Windows' SSPI), and will look for all of them on your system and use whichever it finds. If more than one exists on your system and you need to use a specific one, you can adjust the order in which it will search using this preference list control. +PuTTY contains native support for a few well-known such libraries (including Windows' SSPI), and will look for all of them on your system and use whichever it finds. If more than one exists on your system and you need to use a specific one, you can adjust the order in which it will search using this preference list control.

          One of the options in the preference list is to use a user-specified GSSAPI library. If the library you want to use is not mentioned by name in PuTTY's list of options, you can enter its full pathname in the ‘User-supplied GSSAPI library path’ field, and move the ‘User-supplied GSSAPI library’ option in the preference list to make sure it is selected before anything else.

          -On Windows, such libraries are files with a .dll extension, and must have been built in the same way as the PuTTY executable you're running; if you have a 32-bit DLL, you must run a 32-bit version of PuTTY, and the same with 64-bit (see question A.6.10). On Unix, shared libraries generally have a .so extension. +On Windows, such libraries are files with a .dll extension, and must have been built in the same way as the PuTTY executable you're running; if you have a 32-bit DLL, you must run a 32-bit version of PuTTY, and the same with 64-bit (see question A.6.10). On Unix, shared libraries generally have a .so extension.

          -

          4.25 The TTY panel

          +

          4.23 The TTY panel

          The TTY panel lets you configure the remote pseudo-terminal.

          -

          4.25.1 ‘Don't allocate a pseudo-terminal’

          +

          4.23.1 ‘Don't allocate a pseudo-terminal’

          -When connecting to a Unix system, most interactive shell sessions are run in a pseudo-terminal, which allows the Unix system to pretend it's talking to a real physical terminal device but allows the SSH server to catch all the data coming from that fake device and send it back to the client. +When connecting to a Unix system, most interactive shell sessions are run in a pseudo-terminal, which allows the Unix system to pretend it's talking to a real physical terminal device but allows the SSH server to catch all the data coming from that fake device and send it back to the client.

          Occasionally you might find you have a need to run a session not in a pseudo-terminal. In PuTTY, this is generally only useful for very specialist purposes; although in Plink (see chapter 7) it is the usual way of working.

          -

          4.25.2 Sending terminal modes

          +

          4.23.2 Sending terminal modes

          The SSH protocol allows the client to send ‘terminal modes’ for the remote pseudo-terminal. These usually control the server's expectation of the local terminal's behaviour.

          @@ -2050,37 +2046,37 @@

          4.25.2 Sending POSIX and other Unix systems, and they are most likely to have a useful effect on such systems. (These are the same settings that can usually be changed using the stty command once logged in to such servers.) +The precise effect of each setting, if any, is up to the server. Their names come from POSIX and other Unix systems, and they are most likely to have a useful effect on such systems. (These are the same settings that can usually be changed using the stty command once logged in to such servers.)

          Some notable modes are described below; for fuller explanations, see your server documentation.

          • -ERASE is the character that when typed by the user will delete one space to the left. When set to ‘Auto’ (the default setting), this follows the setting of the local Backspace key in PuTTY (see section 4.4.1). +ERASE is the character that when typed by the user will delete one space to the left. When set to ‘Auto’ (the default setting), this follows the setting of the local Backspace key in PuTTY (see section 4.4.1).

            -This and other special characters are specified using ^C notation for Ctrl-C, and so on. Use ^<27> or ^<0x1B> to specify a character numerically, and ^~ to get a literal ^. Other non-control characters are denoted by themselves. Leaving the box entirely blank indicates that no character should be assigned to the specified function, although this may not be supported by all servers. +This and other special characters are specified using ^C notation for Ctrl-C, and so on. Use ^<27> or ^<0x1B> to specify a character numerically, and ^~ to get a literal ^. Other non-control characters are denoted by themselves. Leaving the box entirely blank indicates that no character should be assigned to the specified function, although this may not be supported by all servers.

          • -QUIT is a special character that usually forcefully ends the current process on the server (SIGQUIT). On many servers its default setting is Ctrl-backslash (^\), which is easy to accidentally invoke on many keyboards. If this is getting in your way, you may want to change it to another character or turn it off entirely. +QUIT is a special character that usually forcefully ends the current process on the server (SIGQUIT). On many servers its default setting is Ctrl-backslash (^\), which is easy to accidentally invoke on many keyboards. If this is getting in your way, you may want to change it to another character or turn it off entirely.
          • Boolean modes such as ECHO and ICANON can be specified in PuTTY in a variety of ways, such as true/false, yes/no, and 0/1. (Explicitly specifying a value of no is different from not sending the mode at all.)
          • -The boolean mode IUTF8 signals to the server whether the terminal character set is UTF-8 or not, for purposes such as basic line editing; if this is set incorrectly, the backspace key may erase the wrong amount of text, for instance. However, simply setting this is not usually sufficient for the server to use UTF-8; POSIX servers will generally also require the locale to be set (by some server-dependent means), although many newer installations default to UTF-8. Also, since this mode was added to the SSH protocol much later than the others, many servers (particularly older servers) do not honour this mode sent over SSH; indeed, a few poorly-written servers object to its mere presence, so you may find you need to set it to not be sent at all. When set to ‘Auto’, this follows the local configured character set (see section 4.10.1). +The boolean mode IUTF8 signals to the server whether the terminal character set is UTF-8 or not, for purposes such as basic line editing; if this is set incorrectly, the backspace key may erase the wrong amount of text, for instance. However, simply setting this is not usually sufficient for the server to use UTF-8; POSIX servers will generally also require the locale to be set (by some server-dependent means), although many newer installations default to UTF-8. Also, since this mode was added to the SSH protocol much later than the others, many servers (particularly older servers) do not honour this mode sent over SSH; indeed, a few poorly-written servers object to its mere presence, so you may find you need to set it to not be sent at all. When set to ‘Auto’, this follows the local configured character set (see section 4.10.1).
          • Terminal speeds are configured elsewhere; see section 4.15.4.
          -

          4.26 The X11 panel

          +

          4.24 The X11 panel

          -The X11 panel allows you to configure forwarding of X11 over an SSH connection. +The X11 panel allows you to configure forwarding of X11 over an SSH connection.

          -If your server lets you run X Window System graphical applications, X11 forwarding allows you to securely give those applications access to a local X display on your PC. +If your server lets you run X Window System graphical applications, X11 forwarding allows you to securely give those applications access to a local X display on your PC.

          To enable X11 forwarding, check the ‘Enable X11 forwarding’ box. If your X display is somewhere unusual, you will need to enter its location in the ‘X display location’ box; if this is left blank, PuTTY will try to find a sensible default in the environment, or use the primary local display (:0) if that fails. @@ -2088,15 +2084,15 @@

          4.26 The X11 panel

          See section 3.4 for more information about X11 forwarding.

          -

          4.26.1 Remote X11 authentication

          +

          4.24.1 Remote X11 authentication

          If you are using X11 forwarding, the virtual X server created on the SSH server machine will be protected by authorisation data. This data is invented, and checked, by PuTTY.

          -The usual authorisation method used for this is called MIT-MAGIC-COOKIE-1. This is a simple password-style protocol: the X client sends some cookie data to the server, and the server checks that it matches the real cookie. The cookie data is sent over an unencrypted X11 connection; so if you allow a client on a third machine to access the virtual X server, then the cookie will be sent in the clear. +The usual authorisation method used for this is called MIT-MAGIC-COOKIE-1. This is a simple password-style protocol: the X client sends some cookie data to the server, and the server checks that it matches the real cookie. The cookie data is sent over an unencrypted X11 connection; so if you allow a client on a third machine to access the virtual X server, then the cookie will be sent in the clear.

          -PuTTY offers the alternative protocol XDM-AUTHORIZATION-1. This is a cryptographically authenticated protocol: the data sent by the X client is different every time, and it depends on the IP address and port of the client's end of the connection and is also stamped with the current time. So an eavesdropper who captures an XDM-AUTHORIZATION-1 string cannot immediately re-use it for their own X connection. +PuTTY offers the alternative protocol XDM-AUTHORIZATION-1. This is a cryptographically authenticated protocol: the data sent by the X client is different every time, and it depends on the IP address and port of the client's end of the connection and is also stamped with the current time. So an eavesdropper who captures an XDM-AUTHORIZATION-1 string cannot immediately re-use it for their own X connection.

          PuTTY's support for XDM-AUTHORIZATION-1 is a somewhat experimental feature, and may encounter several problems: @@ -2114,7 +2110,7 @@

          4.26.1 Remote PuTTY's default is MIT-MAGIC-COOKIE-1. If you change it, you should be sure you know what you're doing.

          -

          4.26.2 X authority file for local display

          +

          4.24.2 X authority file for local display

          If you are using X11 forwarding, the local X server to which your forwarded connections are eventually directed may itself require authorisation.

          @@ -2124,12 +2120,12 @@

          4.26.2 X authority

          One way in which this data might be made available is for the X server to store it somewhere in a file which has the same format as the Unix .Xauthority file. If this is how your Windows X server works, then you can tell PuTTY where to find this file by configuring this option. By default, PuTTY will not attempt to find any authorisation for your local display.

          -

          4.27 The Tunnels panel

          +

          4.25 The Tunnels panel

          The Tunnels panel allows you to configure tunnelling of arbitrary connection types through an SSH connection.

          -Port forwarding allows you to tunnel other types of network connection down an SSH session. See section 3.5 for a general discussion of port forwarding and how it works. +Port forwarding allows you to tunnel other types of network connection down an SSH session. See section 3.5 for a general discussion of port forwarding and how it works.

          The port forwarding section in the Tunnels panel shows a list of all the port forwardings that PuTTY will try to set up when it connects to the server. By default no port forwardings are set up, so this list is empty. @@ -2138,13 +2134,13 @@

          4.27
          • -Set one of the ‘Local’ or ‘Remote’ radio buttons, depending on whether you want to forward a local port to a remote destination (‘Local’) or forward a remote port to a local destination (‘Remote’). Alternatively, select ‘Dynamic’ if you want PuTTY to provide a local SOCKS 4/4A/5 proxy on a local port (note that this proxy only supports TCP connections; the SSH protocol does not support forwarding UDP). +Set one of the ‘Local’ or ‘Remote’ radio buttons, depending on whether you want to forward a local port to a remote destination (‘Local’) or forward a remote port to a local destination (‘Remote’). Alternatively, select ‘Dynamic’ if you want PuTTY to provide a local SOCKS 4/4A/5 proxy on a local port (note that this proxy only supports TCP connections; the SSH protocol does not support forwarding UDP).
          • -Enter a source port number into the ‘Source port’ box. For local forwardings, PuTTY will listen on this port of your PC. For remote forwardings, your SSH server will listen on this port of the remote machine. Note that most servers will not allow you to listen on port numbers less than 1024. +Enter a source port number into the ‘Source port’ box. For local forwardings, PuTTY will listen on this port of your PC. For remote forwardings, your SSH server will listen on this port of the remote machine. Note that most servers will not allow you to listen on port numbers less than 1024.
          • -If you have selected ‘Local’ or ‘Remote’ (this step is not needed with ‘Dynamic’), enter a hostname and port number separated by a colon, in the ‘Destination’ box. Connections received on the source port will be directed to this destination. For example, to connect to a POP-3 server, you might enter popserver.example.com:110. (If you need to enter a literal IPv6 address, enclose it in square brackets, for instance ‘[::1]:2200’.) +If you have selected ‘Local’ or ‘Remote’ (this step is not needed with ‘Dynamic’), enter a hostname and port number separated by a colon, in the ‘Destination’ box. Connections received on the source port will be directed to this destination. For example, to connect to a POP-3 server, you might enter popserver.example.com:110. (If you need to enter a literal IPv6 address, enclose it in square brackets, for instance ‘[::1]:2200’.)
          • Click the ‘Add’ button. Your forwarding details should appear in the list box. @@ -2154,19 +2150,19 @@

            4.27

            -In the ‘Source port’ box, you can also optionally enter an IP address to listen on, by specifying (for instance) 127.0.0.5:79. See section 3.5 for more information on how this works and its restrictions. +In the ‘Source port’ box, you can also optionally enter an IP address to listen on, by specifying (for instance) 127.0.0.5:79. See section 3.5 for more information on how this works and its restrictions.

            -In place of port numbers, you can enter service names, if they are known to the local system. For instance, in the ‘Destination’ box, you could enter popserver.example.com:pop3. +In place of port numbers, you can enter service names, if they are known to the local system. For instance, in the ‘Destination’ box, you could enter popserver.example.com:pop3.

            -You can modify the currently active set of port forwardings in mid-session using ‘Change Settings’ (see section 3.1.3.4). If you delete a local or dynamic port forwarding in mid-session, PuTTY will stop listening for connections on that port, so it can be re-used by another program. If you delete a remote port forwarding, note that: +You can modify the currently active set of port forwardings in mid-session using ‘Change Settings’ (see section 3.1.3.4). If you delete a local or dynamic port forwarding in mid-session, PuTTY will stop listening for connections on that port, so it can be re-used by another program. If you delete a remote port forwarding, note that:

            • The SSH-1 protocol contains no mechanism for asking the server to stop listening on a remote port.
            • -The SSH-2 protocol does contain such a mechanism, but not all SSH servers support it. (In particular, OpenSSH does not support it in any version earlier than 3.9.) +The SSH-2 protocol does contain such a mechanism, but not all SSH servers support it. (In particular, OpenSSH does not support it in any version earlier than 3.9.)

            @@ -2178,20 +2174,20 @@

            4.27 If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the ‘logical host name’ configuration option useful to warn PuTTY of which host key it should be expecting. See section 4.14.5 for details of this.

            -

            4.27.1 Controlling the visibility of forwarded ports

            +

            4.25.1 Controlling the visibility of forwarded ports

            -The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this: +The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this:

            • The ‘Local ports accept connections from other hosts’ option allows you to set up local-to-remote port forwardings in such a way that machines other than your client PC can connect to the forwarded port. (This also applies to dynamic SOCKS forwarding.)
            • -The ‘Remote ports do the same’ option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers support it (OpenSSH 3.0 does not, for example). +The ‘Remote ports do the same’ option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers support it (OpenSSH 3.0 does not, for example).
            -

            4.27.2 Selecting Internet protocol version for forwarded ports

            +

            4.25.2 Selecting Internet protocol version for forwarded ports

            -This switch allows you to select a specific Internet protocol (IPv4 or IPv6) for the local end of a forwarded port. By default, it is set on ‘Auto’, which means that: +This switch allows you to select a specific Internet protocol (IPv4 or IPv6) for the local end of a forwarded port. By default, it is set on ‘Auto’, which means that:

            • for a local-to-remote port forwarding, PuTTY will listen for incoming connections in both IPv4 and (if available) IPv6 @@ -2204,9 +2200,9 @@

              4.27.2 This overrides the general Internet protocol version preference on the Connection panel (see section 4.14.4).

              -Note that some operating systems may listen for incoming connections in IPv4 even if you specifically asked for IPv6, because their IPv4 and IPv6 protocol stacks are linked together. Apparently Linux does this, and Windows does not. So if you're running PuTTY on Windows and you tick ‘IPv6’ for a local or dynamic port forwarding, it will only be usable by connecting to it using IPv6; whereas if you do the same on Linux, you can also use it with IPv4. However, ticking ‘Auto’ should always give you a port which you can connect to using either protocol. +Note that some operating systems may listen for incoming connections in IPv4 even if you specifically asked for IPv6, because their IPv4 and IPv6 protocol stacks are linked together. Apparently Linux does this, and Windows does not. So if you're running PuTTY on Windows and you tick ‘IPv6’ for a local or dynamic port forwarding, it will only be usable by connecting to it using IPv6; whereas if you do the same on Linux, you can also use it with IPv4. However, ticking ‘Auto’ should always give you a port which you can connect to using either protocol.

              -

              4.28 The Bugs and More Bugs panels

              +

              4.26 The Bugs and More Bugs panels

              Not all SSH servers work properly. Various existing servers have bugs in them, which can make it impossible for a client to talk to them unless it knows about the bug and works around it.

              @@ -2226,16 +2222,16 @@

              4.28 Th ‘Auto’: PuTTY will use the server's version number announcement to try to guess whether or not the server has the bug.

            -

            4.28.1 ‘Chokes on SSH-2 ignore messages’

            +

            4.26.1 ‘Chokes on SSH-2 ignore messages’

            -An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages in SSH-2 to confuse the encrypted data stream and make it harder to cryptanalyse. It also uses ignore messages for connection keepalives (see section 4.14.1). +An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages in SSH-2 to confuse the encrypted data stream and make it harder to cryptanalyse. It also uses ignore messages for connection keepalives (see section 4.14.1).

            If it believes the server to have this bug, PuTTY will stop using ignore messages. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be less cryptographically secure than it could be.

            -

            4.28.2 ‘Handles SSH-2 key re-exchange badly’

            +

            4.26.2 ‘Handles SSH-2 key re-exchange badly’

            -Some SSH servers cannot cope with repeat key exchange at all, and will ignore attempts by the client to start one. Since PuTTY pauses the session while performing a repeat key exchange, the effect of this would be to cause the session to hang after an hour (unless you have your rekey timeout set differently; see section 4.20.2 for more about rekeys). Other, very old, SSH servers handle repeat key exchange even more badly, and disconnect upon receiving a repeat key exchange request. +Some SSH servers cannot cope with repeat key exchange at all, and will ignore attempts by the client to start one. Since PuTTY pauses the session while performing a repeat key exchange, the effect of this would be to cause the session to hang after an hour (unless you have your rekey timeout set differently; see section 4.18.2 for more about rekeys). Other, very old, SSH servers handle repeat key exchange even more badly, and disconnect upon receiving a repeat key exchange request.

            If this bug is detected, PuTTY will never initiate a repeat key exchange. If this bug is enabled when talking to a correct server, the session should still function, but may be less secure than you would expect. @@ -2243,14 +2239,14 @@

            4.28.2 ‘Handl

            This is an SSH-2-specific bug.

            -

            4.28.3 ‘Chokes on PuTTY's SSH-2 ‘winadj’ requests’

            +

            4.26.3 ‘Chokes on PuTTY's SSH-2 ‘winadj’ requests’

            -PuTTY sometimes sends a special request to SSH servers in the middle of channel data, with the name winadj@putty.projects.tartarus.org (see section F.1). The purpose of this request is to measure the round-trip time to the server, which PuTTY uses to tune its flow control. The server does not actually have to understand the message; it is expected to send back a SSH_MSG_CHANNEL_FAILURE message indicating that it didn't understand it. (All PuTTY needs for its timing calculations is some kind of response.) +PuTTY sometimes sends a special request to SSH servers in the middle of channel data, with the name winadj@putty.projects.tartarus.org (see section G.1). The purpose of this request is to measure the round-trip time to the server, which PuTTY uses to tune its flow control. The server does not actually have to understand the message; it is expected to send back a SSH_MSG_CHANNEL_FAILURE message indicating that it didn't understand it. (All PuTTY needs for its timing calculations is some kind of response.)

            It has been known for some SSH servers to get confused by this message in one way or another – because it has a long name, or because they can't cope with unrecognised request names even to the extent of sending back the correct failure response, or because they handle it sensibly but fill up the server's log file with pointless spam, or whatever. PuTTY therefore supports this bug-compatibility flag: if it believes the server has this bug, it will never send its ‘winadj@putty.projects.tartarus.org’ request, and will make do without its timing data.

            -

            4.28.4 ‘Replies to requests on closed channels’

            +

            4.26.4 ‘Replies to requests on closed channels’

            The SSH protocol as published in RFC 4254 has an ambiguity which arises if one side of a connection tries to close a channel, while the other side simultaneously sends a request within the channel and asks for a reply. RFC 4254 leaves it unclear whether the closing side should reply to the channel request after having announced its intention to close the channel.

            @@ -2258,18 +2254,18 @@

            4.28.4 ‘Rep Discussion on the ietf-ssh mailing list in April 2014 formed a clear consensus that the right answer is no. However, because of the ambiguity in the specification, some SSH servers have implemented the other policy; for example, OpenSSH used to until it was fixed.

            -Because PuTTY sends channel requests with the ‘want reply’ flag throughout channels' lifetime (see section 4.28.3), it's possible that when connecting to such a server it might receive a reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of ‘Received SSH2_MSG_CHANNEL_FAILURE for nonexistent channel 256’. +Because PuTTY sends channel requests with the ‘want reply’ flag throughout channels' lifetime (see section 4.26.3), it's possible that when connecting to such a server it might receive a reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of ‘Received SSH2_MSG_CHANNEL_FAILURE for nonexistent channel 256’.

            -

            4.28.5 ‘Ignores SSH-2 maximum packet size’

            +

            4.26.5 ‘Ignores SSH-2 maximum packet size’

            When an SSH-2 channel is set up, each end announces the maximum size of data packet that it is willing to receive for that channel. Some servers ignore PuTTY's announcement and send packets larger than PuTTY is willing to accept, causing it to report ‘Incoming packet was garbled on decryption’.

            -If this bug is detected, PuTTY never allows the channel's flow-control window to grow large enough to allow the server to send an over-sized packet. If this bug is enabled when talking to a correct server, the session will work correctly, but download performance will be less than it could be. +If this bug is detected, PuTTY never allows the channel's flow-control window to grow large enough to allow the server to send an over-sized packet. If this bug is enabled when talking to a correct server, the session will work correctly, but download performance will be less than it could be.

            -

            4.28.6 ‘Requires padding on SSH-2 RSA signatures’

            +

            4.26.6 ‘Requires padding on SSH-2 RSA signatures’

            -Versions below 3.3 of OpenSSH require SSH-2 RSA signatures to be padded with zero bytes to the same length as the RSA key modulus. The SSH-2 specification says that an unpadded signature MUST be accepted, so this is a bug. A typical symptom of this problem is that PuTTY mysteriously fails RSA authentication once in every few hundred attempts, and falls back to passwords. +Versions below 3.3 of OpenSSH require SSH-2 RSA signatures to be padded with zero bytes to the same length as the RSA key modulus. The SSH-2 specification says that an unpadded signature MUST be accepted, so this is a bug. A typical symptom of this problem is that PuTTY mysteriously fails RSA authentication once in every few hundred attempts, and falls back to passwords.

            If this bug is detected, PuTTY will pad its signatures in the way OpenSSH expects. If this bug is enabled when talking to a correct server, it is likely that no damage will be done, since correct servers usually still accept padded signatures because they're used to talking to OpenSSH. @@ -2277,7 +2273,7 @@

            4.28.6 ‘Require

            This is an SSH-2-specific bug.

            -

            4.28.7 ‘Only supports pre-RFC4419 SSH-2 DH GEX’

            +

            4.26.7 ‘Only supports pre-RFC4419 SSH-2 DH GEX’

            The SSH key exchange method that uses Diffie-Hellman group exchange was redesigned after its original release, to use a slightly more sophisticated setup message. Almost all SSH implementations switched over to the new version. (PuTTY was one of the last.) A few old servers still only support the old one.

            @@ -2287,9 +2283,9 @@

            4.28.7 ‘Onl

            This is an SSH-2-specific bug.

            -

            4.28.8 ‘Miscomputes SSH-2 HMAC keys’

            +

            4.26.8 ‘Miscomputes SSH-2 HMAC keys’

            -Versions 2.3.0 and below of the SSH server software from ssh.com compute the keys for their HMAC message authentication codes incorrectly. A typical symptom of this problem is that PuTTY dies unexpectedly at the beginning of the session, saying ‘Incorrect MAC received on packet’. +Versions 2.3.0 and below of the SSH server software from ssh.com compute the keys for their HMAC message authentication codes incorrectly. A typical symptom of this problem is that PuTTY dies unexpectedly at the beginning of the session, saying ‘Incorrect MAC received on packet’.

            If this bug is detected, PuTTY will compute its HMAC keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail. @@ -2297,9 +2293,9 @@

            4.28.8 ‘Misco

            This is an SSH-2-specific bug.

            -

            4.28.9 ‘Misuses the session ID in SSH-2 PK auth’

            +

            4.26.9 ‘Misuses the session ID in SSH-2 PK auth’

            -Versions below 2.3 of OpenSSH require SSH-2 public-key authentication to be done slightly differently: the data to be signed by the client contains the session ID formatted in a different way. If public-key authentication mysteriously does not work but the Event Log (see section 3.1.3.1) thinks it has successfully sent a signature, it might be worth enabling the workaround for this bug to see if it helps. +Versions below 2.3 of OpenSSH require SSH-2 public-key authentication to be done slightly differently: the data to be signed by the client contains the session ID formatted in a different way. If public-key authentication mysteriously does not work but the Event Log (see section 3.1.3.1) thinks it has successfully sent a signature, it might be worth enabling the workaround for this bug to see if it helps.

            If this bug is detected, PuTTY will sign data in the way OpenSSH expects. If this bug is enabled when talking to a correct server, SSH-2 public-key authentication will fail. @@ -2307,9 +2303,9 @@

            4.28.9 ‘M

            This is an SSH-2-specific bug.

            -

            4.28.10 ‘Miscomputes SSH-2 encryption keys’

            +

            4.26.10 ‘Miscomputes SSH-2 encryption keys’

            -Versions below 2.0.11 of the SSH server software from ssh.com compute the keys for the session encryption incorrectly. This problem can cause various error messages, such as ‘Incoming packet was garbled on decryption’, or possibly even ‘Out of memory’. +Versions below 2.0.11 of the SSH server software from ssh.com compute the keys for the session encryption incorrectly. This problem can cause various error messages, such as ‘Incoming packet was garbled on decryption’, or possibly even ‘Out of memory’.

            If this bug is detected, PuTTY will compute its encryption keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail. @@ -2317,16 +2313,16 @@

            4.28.10 ̵

            This is an SSH-2-specific bug.

            -

            4.28.11 ‘Chokes on SSH-1 ignore messages’

            +

            4.26.11 ‘Chokes on SSH-1 ignore messages’

            -An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages to hide the password packet in SSH-1, so that a listener cannot tell the length of the user's password; it also uses ignore messages for connection keepalives (see section 4.14.1). +An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages to hide the password packet in SSH-1, so that a listener cannot tell the length of the user's password; it also uses ignore messages for connection keepalives (see section 4.14.1).

            -If this bug is detected, PuTTY will stop using ignore messages. This means that keepalives will stop working, and PuTTY will have to fall back to a secondary defence against SSH-1 password-length eavesdropping. See section 4.28.12. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be more vulnerable to eavesdroppers than it could be. +If this bug is detected, PuTTY will stop using ignore messages. This means that keepalives will stop working, and PuTTY will have to fall back to a secondary defence against SSH-1 password-length eavesdropping. See section 4.26.12. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be more vulnerable to eavesdroppers than it could be.

            -

            4.28.12 ‘Refuses all SSH-1 password camouflage’

            +

            4.26.12 ‘Refuses all SSH-1 password camouflage’

            -When talking to an SSH-1 server which cannot deal with ignore messages (see section 4.28.11), PuTTY will attempt to disguise the length of the user's password by sending additional padding within the password packet. This is technically a violation of the SSH-1 specification, and so PuTTY will only do it when it cannot use standards-compliant ignore messages as camouflage. In this sense, for a server to refuse to accept a padded password packet is not really a bug, but it does make life inconvenient if the server can also not handle ignore messages. +When talking to an SSH-1 server which cannot deal with ignore messages (see section 4.26.11), PuTTY will attempt to disguise the length of the user's password by sending additional padding within the password packet. This is technically a violation of the SSH-1 specification, and so PuTTY will only do it when it cannot use standards-compliant ignore messages as camouflage. In this sense, for a server to refuse to accept a padded password packet is not really a bug, but it does make life inconvenient if the server can also not handle ignore messages.

            If this ‘bug’ is detected, PuTTY will assume that neither ignore messages nor padding are acceptable, and that it thus has no choice but to send the user's password with no form of camouflage, so that an eavesdropping user will be easily able to find out the exact length of the password. If this bug is enabled when talking to a correct server, the session will succeed, but will be more vulnerable to eavesdroppers than it could be. @@ -2334,9 +2330,9 @@

            4.28.12 ‘

            This is an SSH-1-specific bug. SSH-2 is secure against this type of attack.

            -

            4.28.13 ‘Chokes on SSH-1 RSA authentication’

            +

            4.26.13 ‘Chokes on SSH-1 RSA authentication’

            -Some SSH-1 servers cannot deal with RSA authentication messages at all. If Pageant is running and contains any SSH-1 keys, PuTTY will normally automatically try RSA authentication before falling back to passwords, so these servers will crash when they see the RSA attempt. +Some SSH-1 servers cannot deal with RSA authentication messages at all. If Pageant is running and contains any SSH-1 keys, PuTTY will normally automatically try RSA authentication before falling back to passwords, so these servers will crash when they see the RSA attempt.

            If this bug is detected, PuTTY will go straight to password authentication. If this bug is enabled when talking to a correct server, the session will succeed, but of course RSA authentication will be impossible. @@ -2344,36 +2340,61 @@

            4.28.13 ‘Chok

            This is an SSH-1-specific bug.

            -

            4.29 The Serial panel

            +

            4.27 The ‘Bare ssh-connection’ protocol

            +

            +In addition to SSH itself, PuTTY also supports a second protocol that is derived from SSH. It's listed in the PuTTY GUI under the name ‘Bare ssh-connection’. +

            +

            +This protocol consists of just the innermost of SSH-2's three layers: it leaves out the cryptography layer providing network security, and it leaves out the authentication layer where you provide a username and prove you're allowed to log in as that user. +

            +

            +It is therefore completely unsuited to any network connection. Don't try to use it over a network! +

            +

            +The purpose of this protocol is for various specialist circumstances in which the ‘connection’ is not over a real network, but is a pipe or IPC channel between different processes running on the same computer. In these contexts, the operating system will already have guaranteed that each of the two communicating processes is owned by the expected user (so that no authentication is necessary), and that the communications channel cannot be tapped by a hostile user on the same machine (so that no cryptography is necessary either). Examples of possible uses involve communicating with a strongly separated context such as the inside of a container, or a VM, or a different network namespace. +

            +

            +Explicit support for this protocol is new in PuTTY 0.75. As of 2021-04, the only known server for the bare ssh-connection protocol is the Unix program ‘psusan’ that is also part of the PuTTY tool suite. +

            +

            +(However, this protocol is also the same one used between instances of PuTTY to implement connection sharing: see section 4.17.5. In fact, in the Unix version of PuTTY, when a sharing upstream records ‘Sharing this connection at [pathname]’ in the Event Log, it's possible to connect another instance of PuTTY directly to that Unix socket, by entering its pathname in the host name box and selecting ‘Bare ssh-connection’ as the protocol!) +

            +

            +Many of the options under the SSH panel also affect this protocol, although options to do with cryptography and authentication do not, for obvious reasons. +

            +

            +I repeat, DON'T TRY TO USE THIS PROTOCOL FOR NETWORK CONNECTIONS! That's not what it's for, and it's not at all safe to do it. +

            +

            4.28 The Serial panel

            -The Serial panel allows you to configure options that only apply when PuTTY is connecting to a local serial line. +The Serial panel allows you to configure options that only apply when PuTTY is connecting to a local serial line.

            -

            4.29.1 Selecting a serial line to connect to

            +

            4.28.1 Selecting a serial line to connect to

            The ‘Serial line to connect to’ box allows you to choose which serial line you want PuTTY to talk to, if your computer has more than one serial port.

            -On Windows, the first serial line is called COM1, and if there is a second it is called COM2, and so on. +On Windows, the first serial line is called COM1, and if there is a second it is called COM2, and so on.

            This configuration setting is also visible on the Session panel, where it replaces the ‘Host Name’ box (see section 4.1.1) if the connection type is set to ‘Serial’.

            -

            4.29.2 Selecting the speed of your serial line

            +

            4.28.2 Selecting the speed of your serial line

            The ‘Speed’ box allows you to choose the speed (or ‘baud rate’) at which to talk to the serial line. Typical values might be 9600, 19200, 38400 or 57600. Which one you need will depend on the device at the other end of the serial cable; consult the manual for that device if you are in doubt.

            This configuration setting is also visible on the Session panel, where it replaces the ‘Port’ box (see section 4.1.1) if the connection type is set to ‘Serial’.

            -

            4.29.3 Selecting the number of data bits

            +

            4.28.3 Selecting the number of data bits

            The ‘Data bits’ box allows you to choose how many data bits are transmitted in each byte sent or received through the serial line. Typical values are 7 or 8.

            -

            4.29.4 Selecting the number of stop bits

            +

            4.28.4 Selecting the number of stop bits

            The ‘Stop bits’ box allows you to choose how many stop bits are used in the serial line protocol. Typical values are 1, 1.5 or 2.

            -

            4.29.5 Selecting the serial parity checking scheme

            +

            4.28.5 Selecting the serial parity checking scheme

            The ‘Parity’ box allows you to choose what type of parity checking is used on the serial line. The settings are:

            @@ -2393,7 +2414,7 @@

            4.29.5 Selecting th ‘Space’: an extra parity bit is sent alongside each byte, and always set to 0.

          -

          4.29.6 Selecting the serial flow control scheme

          +

          4.28.6 Selecting the serial flow control scheme

          The ‘Flow control’ box allows you to choose what type of flow control checking is used on the serial line. The settings are:

          @@ -2410,9 +2431,107 @@

          4.29.6 Selecting the ‘DSR/DTR’: flow control is done using the DSR and DTR wires on the serial line.

        -

        4.30 Storing configuration in a file

        +

        4.29 The Telnet panel

        +

        +The Telnet panel allows you to configure options that only apply to Telnet sessions. +

        +

        4.29.1 ‘Handling of OLD_ENVIRON ambiguity’

        +

        +The original Telnet mechanism for passing environment variables was badly specified. At the time the standard (RFC 1408) was written, BSD telnet implementations were already supporting the feature, and the intention of the standard was to describe the behaviour the BSD implementations were already using. +

        +

        +Sadly there was a typing error in the standard when it was issued, and two vital function codes were specified the wrong way round. BSD implementations did not change, and the standard was not corrected. Therefore, it's possible you might find either BSD or RFC-compliant implementations out there. This switch allows you to choose which one PuTTY claims to be. +

        +

        +The problem was solved by issuing a second standard, defining a new Telnet mechanism called NEW_ENVIRON, which behaved exactly like the original OLD_ENVIRON but was not encumbered by existing implementations. Most Telnet servers now support this, and it's unambiguous. This feature should only be needed if you have trouble passing environment variables to quite an old server. +

        +

        4.29.2 Passive and active Telnet negotiation modes

        +

        +In a Telnet connection, there are two types of data passed between the client and the server: actual text, and negotiations about which Telnet extra features to use. +

        +

        +PuTTY can use two different strategies for negotiation: +

        +
        • +In active mode, PuTTY starts to send negotiations as soon as the connection is opened. +
        • +
        • +In passive mode, PuTTY will wait to negotiate until it sees a negotiation from the server. +
        • +
        +

        +The obvious disadvantage of passive mode is that if the server is also operating in a passive mode, then negotiation will never begin at all. For this reason PuTTY defaults to active mode. +

        +

        +However, sometimes passive mode is required in order to successfully get through certain types of firewall and Telnet proxy server. If you have confusing trouble with a firewall, you could try enabling passive mode to see if it helps. +

        +

        4.29.3 ‘Keyboard sends Telnet special commands’

        +

        +If this box is checked, several key sequences will have their normal actions modified: +

        +
        • +the Backspace key on the keyboard will send the Telnet special backspace code; +
        • +
        • +Control-C will send the Telnet special Interrupt Process code; +
        • +
        • +Control-Z will send the Telnet special Suspend Process code. +
        • +
        +

        +You probably shouldn't enable this unless you know what you're doing. +

        +

        4.29.4 ‘Return key sends Telnet New Line instead of ^M’

        +

        +Unlike most other remote login protocols, the Telnet protocol has a special ‘new line’ code that is not the same as the usual line endings of Control-M or Control-J. By default, PuTTY sends the Telnet New Line code when you press Return, instead of sending Control-M as it does in most other protocols. +

        +

        +Most Unix-style Telnet servers don't mind whether they receive Telnet New Line or Control-M; some servers do expect New Line, and some servers prefer to see ^M. If you are seeing surprising behaviour when you press Return in a Telnet session, you might try turning this option off to see if it helps. +

        +

        4.30 The Rlogin panel

        +

        +The Rlogin panel allows you to configure options that only apply to Rlogin sessions. +

        +

        4.30.1 ‘Local username’

        +

        +Rlogin allows an automated (password-free) form of login by means of a file called .rhosts on the server. You put a line in your .rhosts file saying something like jbloggs@pc1.example.com, and then when you make an Rlogin connection the client transmits the username of the user running the Rlogin client. The server checks the username and hostname against .rhosts, and if they match it does not ask for a password. +

        +

        +This only works because Unix systems contain a safeguard to stop a user from pretending to be another user in an Rlogin connection. Rlogin connections have to come from port numbers below 1024, and Unix systems prohibit this to unprivileged processes; so when the server sees a connection from a low-numbered port, it assumes the client end of the connection is held by a privileged (and therefore trusted) process, so it believes the claim of who the user is. +

        +

        +Windows does not have this restriction: any user can initiate an outgoing connection from a low-numbered port. Hence, the Rlogin .rhosts mechanism is completely useless for securely distinguishing several different users on a Windows machine. If you have a .rhosts entry pointing at a Windows PC, you should assume that anyone using that PC can spoof your username in an Rlogin connection and access your account on the server. +

        +

        +The ‘Local username’ control allows you to specify what user name PuTTY should claim you have, in case it doesn't match your Windows user name (or in case you didn't bother to set up a Windows user name). +

        +

        4.31 The SUPDUP panel

        +

        +The SUPDUP panel allows you to configure options that only apply to SUPDUP sessions. See section 3.10 for more about the SUPDUP protocol. +

        +

        4.31.1 ‘Location string’

        +

        +In SUPDUP, the client sends a piece of text of its choice to the server giving the user's location. This is typically displayed in lists of logged-in users. +

        +

        +By default, PuTTY just defaults this to "The Internet". If you want your location to show up as something more specific, you can configure it here. +

        +

        4.31.2 ‘Extended ASCII Character set’

        +

        +This declares what kind of character set extension your terminal supports. If the server supports it, it will send text using that character set. ‘None’ means the standard 95 printable ASCII characters. ‘ITS’ means ASCII extended with printable characters in the control character range. This character set is documented in the SUPDUP protocol definition. ‘WAITS’ is similar to ‘ITS’ but uses some alternative characters in the extended set: most prominently, it will display arrows instead of ^ and _, and } instead of ~. ‘ITS’ extended ASCII is used by ITS and Lisp machines, whilst ‘WAITS’ is only used by the WAITS operating system from the Stanford AI Laboratory. +

        +

        4.31.3 ‘**MORE** processing’

        +

        +When **MORE** processing is enabled, the server causes output to pause at the bottom of the screen, until a space is typed. +

        +

        4.31.4 ‘Terminal scrolling’

        +

        +This controls whether the terminal will perform scrolling then the cursor goes below the last line, or if the cursor will return to the first line. +

        +

        4.32 Storing configuration in a file

        -PuTTY does not currently support storing its configuration in a file instead of the Registry. However, you can work around this with a couple of batch files. +PuTTY does not currently support storing its configuration in a file instead of the Registry. However, you can work around this with a couple of batch files.

        You will need a file called (say) PUTTY.BAT which imports the contents of a file into the Registry, then runs PuTTY, exports the contents of the Registry back into the file, and deletes the Registry entries. This can all be done using the Regedit command line options, so it's all automatic. Here is what you need in PUTTY.BAT: @@ -2449,5 +2568,5 @@

        4.30 Storin


        If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

        -[PuTTY release 0.73]
        +[PuTTY release 0.76] diff --git a/doc/Chapter5.html b/doc/Chapter5.html index 42bbbee..1696e77 100644 --- a/doc/Chapter5.html +++ b/doc/Chapter5.html @@ -49,9 +49,9 @@

        5.2 PSCP Usage

        Once you've got a console window to type into, you can just type pscp on its own to bring up a usage message. This tells you the version of PSCP you're using, and gives you a brief summary of how to use PSCP:

        -
        Z:\owendadmin>pscp
        +
        C:\>pscp
         PuTTY Secure Copy client
        -Release 0.73
        +Release 0.76
         Usage: pscp [options] [user@]host:source target
                pscp [options] source [source...] [user@]host:target
                pscp [options] -ls [user@]host:filespec
        @@ -67,12 +67,16 @@ 

        5.2 PSCP Usage

        -l user connect with specified username -pw passw login with specified password -1 -2 force use of particular SSH protocol version + -ssh -ssh-connection + force use of particular SSH protocol variant -4 -6 force use of IPv4 or IPv6 -C enable compression -i key private key file for user authentication -noagent disable use of Pageant -agent enable use of Pageant - -hostkey aa:bb:cc:... + -no-trivial-auth + disconnect if SSH authentication succeeds trivially + -hostkey keyid manually specify a host key (may be repeated) -batch disable all interactive prompts -no-sanitise-stderr don't strip control chars from standard error @@ -84,6 +88,9 @@

        5.2 PSCP Usage

        -sshlog file -sshrawlog file log protocol details to a file + -logoverwrite + -logappend + control what happens when a log file already exists

        (PSCP's interface is much like the Unix scp command, if you're familiar with that.) @@ -161,7 +168,7 @@

        5.2.1.4

        5.2.2 Options

        -PSCP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.8.3 for a description of these options. (The ones not supported by PSCP are clearly marked.) +PSCP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.11.3 for a description of these options. (The ones not supported by PSCP are clearly marked.)

        PSCP also supports some of its own options. The following sections describe PSCP's specific command-line options. @@ -202,7 +209,7 @@

        5.2.2.5 This may help PSCP's behaviour when it is used in automated scripts: using -batch, if something goes wrong at connection time, the batch job will fail rather than hang.

        -

        5.2.2.6 -sftp, -scp force use of particular protocol

        +

        5.2.2.6 -sftp, -scp force use of particular file transfer protocol

        As mentioned in section 5.2.1, there are two different file transfer protocols in use with SSH. Despite its name, PSCP (like many other ostensible scp clients) can use either of these protocols.

        @@ -240,14 +247,14 @@

        5.2.4 Using section 5.2.1.2). So you would do this:

        • -Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.23.8). You will probably also want to specify a username to log in as (see section 4.15.1). +Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1).
        • In PSCP, you can now use the name of the session instead of a hostname: type pscp sessionname:file localfile, where sessionname is replaced by the name of your saved session.

        -Secondly, you can supply the name of a private key file on the command line, with the -i option. See section 3.8.3.18 for more information. +Secondly, you can supply the name of a private key file on the command line, with the -i option. See section 3.11.3.18 for more information.

        Thirdly, PSCP will attempt to authenticate using Pageant if Pageant is running (see chapter 9). So you would do this: @@ -264,5 +271,5 @@

        5.2.4 Using

        If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

        -[PuTTY release 0.73]
        +[PuTTY release 0.76] diff --git a/doc/Chapter6.html b/doc/Chapter6.html index 9a02da3..397b1a0 100644 --- a/doc/Chapter6.html +++ b/doc/Chapter6.html @@ -89,7 +89,7 @@

        6.1 Starting PSFTP

        At this point you can type open server.example.com or open fred@server.example.com to start a session.

        -PSFTP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.8.3 for a description of these options. (The ones not supported by PSFTP are clearly marked.) +PSFTP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.11.3 for a description of these options. (The ones not supported by PSFTP are clearly marked.)

        PSFTP also supports some of its own options. The following sections describe PSFTP's specific command-line options. @@ -511,14 +511,14 @@

        6.3 Using pu Firstly, PSFTP can use PuTTY saved sessions in place of hostnames. So you might do this:

        • -Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.23.8). You will probably also want to specify a username to log in as (see section 4.15.1). +Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1).
        • In PSFTP, you can now use the name of the session instead of a hostname: type psftp sessionname, where sessionname is replaced by the name of your saved session.

        -Secondly, you can supply the name of a private key file on the command line, with the -i option. See section 3.8.3.18 for more information. +Secondly, you can supply the name of a private key file on the command line, with the -i option. See section 3.11.3.18 for more information.

        Thirdly, PSFTP will attempt to authenticate using Pageant if Pageant is running (see chapter 9). So you would do this: @@ -535,5 +535,5 @@

        6.3 Using pu


        If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

        -[PuTTY release 0.73]
        +[PuTTY release 0.76] diff --git a/doc/Chapter7.html b/doc/Chapter7.html index 8c91e36..d8fad1c 100644 --- a/doc/Chapter7.html +++ b/doc/Chapter7.html @@ -54,9 +54,9 @@

        7.2 Using Plink

        Once you've got a console window to type into, you can just type plink on its own to bring up a usage message. This tells you the version of Plink you're using, and gives you a brief summary of how to use Plink:

        -
        Z:\sysosd>plink
        +
        C:\>plink
         Plink: command-line connection utility
        -Release 0.73
        +Release 0.76
         Usage: plink [options] [user@]host [command]
                ("host" can also be a PuTTY saved session name)
         Options:
        @@ -66,6 +66,8 @@ 

        7.2 Using Plink

        -load sessname Load settings from saved session -ssh -telnet -rlogin -raw -serial force use of a particular protocol + -ssh-connection + force use of the bare ssh-connection protocol -P port connect to specified port -l user connect with specified username -batch disable all interactive prompts @@ -84,15 +86,17 @@

        7.2 Using Plink

        -X -x enable / disable X11 forwarding -A -a enable / disable agent forwarding -t -T enable / disable pty allocation - -1 -2 force use of particular protocol version + -1 -2 force use of particular SSH protocol version -4 -6 force use of IPv4 or IPv6 -C enable compression -i key private key file for user authentication -noagent disable use of Pageant -agent enable use of Pageant + -no-trivial-auth + disconnect if SSH authentication succeeds trivially -noshare disable use of connection sharing -share enable use of connection sharing - -hostkey aa:bb:cc:... + -hostkey keyid manually specify a host key (may be repeated) -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout do/don't strip control chars from standard output/error @@ -105,6 +109,9 @@

        7.2 Using Plink

        -sshlog file -sshrawlog file log protocol details to a file + -logoverwrite + -logappend + control what happens when a log file already exists -shareexists test whether a connection-sharing upstream exists
        @@ -115,7 +122,7 @@

        7.2.1 Using Plink

        To make a simple interactive connection to a remote server, just type plink and then the host name:

        -
        Z:\sysosd>plink login.example.com
        +
        C:\>plink login.example.com
         
         Debian GNU/Linux 2.2 flunky.example.com
         flunky login:
        @@ -124,22 +131,22 @@ 

        7.2.1 Using Plink You should then be able to log in as normal and run a session. The output sent by the server will be written straight to your command prompt window, which will most likely not interpret terminal control codes in the way the server expects it to. So if you run any full-screen applications, for example, you can expect to see strange characters appearing in your window. Interactive connections like this are not the main point of Plink.

        -In order to connect with a different protocol, you can give the command line options -ssh, -telnet, -rlogin or -raw. To make an SSH connection, for example: +In order to connect with a different protocol, you can give the command line options -ssh, -ssh-connection, -telnet, -rlogin, or -raw. To make an SSH connection, for example:

        -
        Z:\sysosd>plink -ssh login.example.com
        +
        C:\>plink -ssh login.example.com
         login as:
         

        If you have already set up a PuTTY saved session, then instead of supplying a host name, you can give the saved session name. This allows you to use public-key authentication, specify a user name, and use most of the other features of PuTTY:

        -
        Z:\sysosd>plink my-ssh-session
        +
        C:\>plink my-ssh-session
         Sent username "fred"
         Authenticating with public key "fred@winbox"
         Last login: Thu Dec  6 19:25:33 2001 from :0.0
         fred@flunky:~$
         

        -(You can also use the -load command-line option to load a saved session; see section 3.8.3.1. If you use -load, the saved session exists, and it specifies a hostname, you cannot also specify a host or user@host argument - it will be treated as part of the remote command.) +(You can also use the -load command-line option to load a saved session; see section 3.11.3.1. If you use -load, the saved session exists, and it specifies a hostname, you cannot also specify a host or user@host argument - it will be treated as part of the remote command.)

        7.2.2 Using Plink for automated connections

        @@ -162,7 +169,7 @@

        7.2.2 Using Plink for a Next, you are likely to need to avoid the various interactive prompts Plink can produce. You might be prompted to verify the host key of the server you're connecting to, to enter a user name, or to enter a password.

        -To avoid being prompted for the server host key when using Plink for an automated connection, you should first make a manual connection (using either of PuTTY or Plink) to the same server, verify the host key (see section 2.2 for more information), and select Yes to add the host key to the Registry. After that, Plink commands connecting to that server should not give a host key prompt unless the host key changes. +To avoid being prompted for the server host key when using Plink for an automated connection, you can first make a manual connection (using either of PuTTY or Plink) to the same server, verify the host key (see section 2.2 for more information), and select ‘Accept’ to add the host key to the Registry. After that, Plink commands connecting to that server should not give a host key prompt unless the host key changes. Alternatively, you can specify the appropriate host key(s) on Plink's command line every time you use it; see section 3.11.3.21.

        To avoid being prompted for a user name, you can: @@ -178,7 +185,7 @@

        7.2.2 Using Plink for a To avoid being prompted for a password, you should almost certainly set up public-key authentication. (See chapter 8 for a general introduction to public-key authentication.) Again, you can do this in two ways:

        • -Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies a private key file (see section 4.23.8). For this to work without prompting, your private key will need to have no passphrase. +Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies a private key file (see section 4.21.9). For this to work without prompting, your private key will need to have no passphrase.
        • Store the private key in Pageant. See chapter 9 for further information. @@ -187,25 +194,25 @@

          7.2.2 Using Plink for a

          Once you have done all this, you should be able to run a remote command on the SSH server machine and have it execute automatically with no prompting:

          -
          Z:\sysosd>plink login.example.com -l fred echo hello, world
          +
          C:\>plink login.example.com -l fred echo hello, world
           hello, world
           
          -Z:\sysosd>
          +C:\>
           

          Or, if you have set up a saved session with all the connection details:

          -
          Z:\sysosd>plink mysession echo hello, world
          +
          C:\>plink mysession echo hello, world
           hello, world
           
          -Z:\sysosd>
          +C:\>
           

          Then you can set up other programs to run this Plink command and talk to it as if it were a process on the server machine.

          7.2.3 Plink command line options

          -Plink accepts all the general command line options supported by the PuTTY tools. See section 3.8.3 for a description of these options. +Plink accepts all the general command line options supported by the PuTTY tools. See section 3.11.3 for a description of these options.

          Plink also supports some of its own options. The following sections describe Plink's specific command-line options. @@ -226,7 +233,7 @@

          7.2.3.2

          7.2.3.3 -share: Test and try to share an existing connection.

          -This option tris to detect if an existing connection can be shared (See section 4.19.5 for more information about SSH connection sharing.) and reuses that connection. +This option tris to detect if an existing connection can be shared (See section 4.17.5 for more information about SSH connection sharing.) and reuses that connection.

          A Plink invocation of the form: @@ -241,7 +248,7 @@

          7.2.3.3 7.2.3.4 -shareexists: test for connection-sharing upstream

          -This option does not make a new connection; instead it allows testing for the presence of an existing connection that can be shared. (See section 4.19.5 for more information about SSH connection sharing.) +This option does not make a new connection; instead it allows testing for the presence of an existing connection that can be shared. (See section 4.17.5 for more information about SSH connection sharing.)

          A Plink invocation of the form: @@ -262,37 +269,37 @@

          7.2.3.5 section 3.8.3.12 and section 4.25.1), on the basis that in that situation you often want escape sequences from the server to go to your terminal. +By default, this only happens for output channels which are sent to a Windows console device, or a Unix terminal device. (Any output stream going somewhere else is likely to be needed by an 8-bit protocol and must not be tampered with at all.) It also stops happening if you tell Plink to allocate a remote pseudo-terminal (see section 3.11.3.12 and section 4.23.1), on the basis that in that situation you often want escape sequences from the server to go to your terminal.

          But in case Plink guesses wrong about whether you want this sanitisation, you can override it in either direction, using one of these options:

          -
          +
          -sanitise-stderr -
          -
          -Sanitise server data written to Plink's standard error channel, regardless of terminals and consoles and remote ptys.
          --no-sanitise-stderr +Sanitise server data written to Plink's standard error channel, regardless of terminals and consoles and remote ptys.
          -Do not sanitise server data written to Plink's standard error channel. +-no-sanitise-stderr
          --sanitise-stdout +Do not sanitise server data written to Plink's standard error channel.
          -Sanitise server data written to Plink's standard output channel. +-sanitise-stdout
          --no-sanitise-stdout +Sanitise server data written to Plink's standard output channel.
          -Do not sanitise server data written to Plink's standard output channel. +-no-sanitise-stdout
          +
          +Do not sanitise server data written to Plink's standard output channel. +
          -

          7.2.3.6 : turn off authentication spoofing protection prompt

          +

          7.2.3.6 -no-antispoof: turn off authentication spoofing protection prompt

          In SSH, some possible server authentication methods require user input (for example, password authentication, or entering a private key passphrase), and others do not (e.g. a private key held in Pageant).

          @@ -375,5 +382,5 @@

          7.5 Using Plink with Feedback page.

          -[PuTTY release 0.73]
          +[PuTTY release 0.76] diff --git a/doc/Chapter8.html b/doc/Chapter8.html index 7cbc666..d38c7a1 100644 --- a/doc/Chapter8.html +++ b/doc/Chapter8.html @@ -22,15 +22,17 @@
        • 8.2.1 Generating a new key
        • 8.2.2 Selecting the type of key
        • 8.2.3 Selecting the size (strength) of the key
        • -
        • 8.2.4 The ‘Generate’ button
        • -
        • 8.2.5 The ‘Key fingerprint’ box
        • -
        • 8.2.6 Setting a comment for your key
        • -
        • 8.2.7 Setting a passphrase for your key
        • -
        • 8.2.8 Saving your private key to a disk file
        • -
        • 8.2.9 Saving your public key to a disk file
        • -
        • 8.2.10 ‘Public key for pasting into OpenSSH authorized_keys file’
        • -
        • 8.2.11 Reloading a private key
        • -
        • 8.2.12 Dealing with private keys in other formats
        • +
        • 8.2.4 Selecting the prime generation method
        • +
        • 8.2.5 The ‘Generate’ button
        • +
        • 8.2.6 The ‘Key fingerprint’ box
        • +
        • 8.2.7 Setting a comment for your key
        • +
        • 8.2.8 Setting a passphrase for your key
        • +
        • 8.2.9 Saving your private key to a disk file
        • +
        • 8.2.10 Saving your public key to a disk file
        • +
        • 8.2.11 ‘Public key for pasting into OpenSSH authorized_keys file’
        • +
        • 8.2.12 Parameters for saving key files
        • +
        • 8.2.13 Reloading a private key
        • +
        • 8.2.14 Dealing with private keys in other formats
      • 8.3 Getting ready for public key authentication

    • @@ -60,7 +62,7 @@

      8.1 Public ke

      8.2 Using PuTTYgen, the PuTTY key generator

      -PuTTYgen is a key generator. It generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see chapter 9). PuTTYgen generates RSA, DSA, ECDSA, and Ed25519 keys. +PuTTYgen is a key generator. It generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see chapter 9). PuTTYgen generates RSA, DSA, ECDSA, and EdDSA keys.

      When you run PuTTYgen you will see a window where you have two main choices: ‘Generate’, to generate a new public/private key pair, or ‘Load’ to load in an existing private key. @@ -73,17 +75,17 @@

      8.2.1 Generating a ne First, you need to select which type of key you want to generate, and also select the strength of the key. This is described in more detail in section 8.2.2 and section 8.2.3.
    • -Then press the ‘Generate’ button, to actually generate the key. Section 8.2.4 describes this step. +Then press the ‘Generate’ button, to actually generate the key. Section 8.2.5 describes this step.
    • -Once you have generated the key, select a comment field (section 8.2.6) and a passphrase (section 8.2.7). +Once you have generated the key, select a comment field (section 8.2.7) and a passphrase (section 8.2.8).
    • -Now you're ready to save the private key to disk; press the ‘Save private key’ button. (See section 8.2.8). +Now you're ready to save the private key to disk; press the ‘Save private key’ button. (See section 8.2.9).

    -Your key pair is now ready for use. You may also want to copy the public key to your server, either by copying it out of the ‘Public key for pasting into OpenSSH authorized_keys file’ box (see section 8.2.10), or by using the ‘Save public key’ button (section 8.2.9). However, you don't need to do this immediately; if you want, you can load the private key back into PuTTYgen later (see section 8.2.11) and the public key will be available for copying and pasting again. +Your key pair is now ready for use. You may also want to copy the public key to your server, either by copying it out of the ‘Public key for pasting into OpenSSH authorized_keys file’ box (see section 8.2.11), or by using the ‘Save public key’ button (section 8.2.10). However, you don't need to do this immediately; if you want, you can load the private key back into PuTTYgen later (see section 8.2.13) and the public key will be available for copying and pasting again.

    Section 8.3 describes the typical process of configuring PuTTY to attempt public-key authentication, and configuring your SSH server to accept it. @@ -93,7 +95,7 @@

    8.2.2 Selecting the type Before generating a key pair using PuTTYgen, you need to select which type of key you need.

    -The current version of the SSH protocol, SSH-2, supports several different key types. PuTTYgen can generate: +The current version of the SSH protocol, SSH-2, supports several different key types, although specific servers may not support all of them. PuTTYgen can generate:

    • An RSA key for use with the SSH-2 protocol. @@ -105,7 +107,7 @@

      8.2.2 Selecting the type An ECDSA (elliptic curve DSA) key for use with the SSH-2 protocol.

    • -An Ed25519 key (another elliptic curve algorithm) for use with the SSH-2 protocol. +An EdDSA key (Edwards-curve DSA, another elliptic curve algorithm) for use with the SSH-2 protocol.

    @@ -116,16 +118,51 @@

    8.2.3 Selecting the siz The ‘Number of bits’ input box allows you to choose the strength of the key PuTTYgen will generate.

    • -For RSA, 2048 bits should currently be sufficient for most purposes. +For RSA and DSA, 2048 bits should currently be sufficient for most purposes.
    • For ECDSA, only 256, 384, and 521 bits are supported. (ECDSA offers equivalent security to RSA with smaller key sizes.)
    • -For Ed25519, the only valid size is 256 bits. +For EdDSA, the only valid sizes are 255 bits (these keys are also known as ‘Ed25519’ and are commonly used) and 448 bits (‘Ed448’, which is much less common at the time of writing). (256 is also accepted for backward compatibility, but the effect is the same as 255.)
    -

    8.2.4 The ‘Generate’ button

    +

    8.2.4 Selecting the prime generation method

    +

    +On the ‘Key’ menu, you can also optionally change the method for generating the prime numbers used in the generated key. This is used for RSA and DSA keys only. (The other key types don't require generating prime numbers at all.) +

    +

    +The prime-generation method does not affect compatibility: a key generated with any of these methods will still work with all the same SSH servers. +

    +

    +If you don't care about this, it's entirely sensible to leave it on the default setting. +

    +

    +The available methods are: +

    +
    • +Use probable primes (fast) +
    • +
    • +Use proven primes (slower) +
    • +
    • +Use proven primes with even distribution (slowest) +
    • +
    +

    +The ‘probable primes’ method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much. +

    +

    +The other methods cause PuTTYgen to use numbers that it is sure are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method. +

    +

    +You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method. +

    +

    +For RSA keys, there's also an option on the ‘Key’ menu to use ‘strong’ primes as the prime factors of the public key. A ‘strong’ prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on unless you have a local standard that recommends it. +

    +

    8.2.5 The ‘Generate’ button

    Once you have chosen the type of key you want, and the strength of the key, press the ‘Generate’ button and PuTTYgen will begin the process of actually generating the key.

    @@ -138,14 +175,17 @@

    8.2.4 The ‘Genera

    When the key generation is complete, a new set of controls will appear in the window to indicate this.

    -

    8.2.5 The ‘Key fingerprint’ box

    +

    8.2.6 The ‘Key fingerprint’ box

    The ‘Key fingerprint’ box shows you a fingerprint value for the generated key. This is derived cryptographically from the public key value, so it doesn't need to be kept secret; it is supposed to be more manageable for human beings than the public key itself.

    The fingerprint value is intended to be cryptographically secure, in the sense that it is computationally infeasible for someone to invent a second key with the same fingerprint, or to find a key with a particular fingerprint. So some utilities, such as the Pageant key list box (see section 9.2.1) and the Unix ssh-add utility, will list key fingerprints rather than the whole public key.

    -

    8.2.6 Setting a comment for your key

    +

    +By default, PuTTYgen will display fingerprints in the ‘SHA256’ format. If you need to see the fingerprint in the older ‘MD5’ format (which looks like aa:bb:cc:...), you can choose ‘Show fingerprint as MD5’ from the ‘Key’ menu, but bear in mind that this is less cryptographically secure; it may be feasible for an attacker to create a key with the same fingerprint as yours. +

    +

    8.2.7 Setting a comment for your key

    If you have more than one key and use them for different purposes, you don't need to memorise the key fingerprints in order to tell them apart. PuTTYgen allows you to enter a comment for your key, which will be displayed whenever PuTTY or Pageant asks you for the passphrase.

    @@ -155,26 +195,26 @@

    8.2.6 Setting a comment

    To alter the key comment, just type your comment text into the ‘Key comment’ box before saving the private key. If you want to change the comment later, you can load the private key back into PuTTYgen, change the comment, and save it again.

    -

    8.2.7 Setting a passphrase for your key

    +

    8.2.8 Setting a passphrase for your key

    -The ‘Key passphrase’ and ‘Confirm passphrase’ boxes allow you to choose a passphrase for your key. The passphrase will be used to encrypt the key on disk, so you will not be able to use the key without first entering the passphrase. +The ‘Key passphrase’ and ‘Confirm passphrase’ boxes allow you to choose a passphrase for your key. The passphrase will be used to encrypt the key on disk, so you will not be able to use the key without first entering the passphrase.

    When you save the key, PuTTYgen will check that the ‘Key passphrase’ and ‘Confirm passphrase’ boxes both contain exactly the same passphrase, and will refuse to save the key otherwise.

    -If you leave the passphrase fields blank, the key will be saved unencrypted. You should not do this without good reason; if you do, your private key file on disk will be all an attacker needs to gain access to any machine configured to accept that key. If you want to be able to log in without having to type a passphrase every time, you should consider using Pageant (chapter 9) so that your decrypted key is only held in memory rather than on disk. +If you leave the passphrase fields blank, the key will be saved unencrypted. You should not do this without good reason; if you do, your private key file on disk will be all an attacker needs to gain access to any machine configured to accept that key. If you want to be able to log in without having to type a passphrase every time, you should consider using Pageant (chapter 9) so that your decrypted key is only held in memory rather than on disk.

    Under special circumstances you may genuinely need to use a key with no passphrase; for example, if you need to run an automated batch script that needs to make an SSH connection, you can't be there to type the passphrase. In this case we recommend you generate a special key for each specific batch script (or whatever) that needs one, and on the server side you should arrange that each key is restricted so that it can only be used for that specific purpose. The documentation for your SSH server should explain how to do this (it will probably vary between servers).

    -Choosing a good passphrase is difficult. Just as you shouldn't use a dictionary word as a password because it's easy for an attacker to run through a whole dictionary, you should not use a song lyric, quotation or other well-known sentence as a passphrase. DiceWare (www.diceware.com) recommends using at least five words each generated randomly by rolling five dice, which gives over 2^64 possible passphrases and is probably not a bad scheme. If you want your passphrase to make grammatical sense, this cuts down the possibilities a lot and you should use a longer one as a result. +Choosing a good passphrase is difficult. Just as you shouldn't use a dictionary word as a password because it's easy for an attacker to run through a whole dictionary, you should not use a song lyric, quotation or other well-known sentence as a passphrase. DiceWare (www.diceware.com) recommends using at least five words each generated randomly by rolling five dice, which gives over 2^64 possible passphrases and is probably not a bad scheme. If you want your passphrase to make grammatical sense, this cuts down the possibilities a lot and you should use a longer one as a result.

    Do not forget your passphrase. There is no way to recover it.

    -

    8.2.8 Saving your private key to a disk file

    +

    8.2.9 Saving your private key to a disk file

    Once you have generated a key, set a comment field and set a passphrase, you are ready to save your private key to disk.

    @@ -182,11 +222,14 @@

    8.2.8 Saving your priva Press the ‘Save private key’ button. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press ‘Save’.

    -This file is in PuTTY's native format (*.PPK); it is the one you will need to tell PuTTY to use for authentication (see section 4.23.8) or tell Pageant to load (see section 9.2.2). +This file is in PuTTY's native format (*.PPK); it is the one you will need to tell PuTTY to use for authentication (see section 4.21.9) or tell Pageant to load (see section 9.2.2). +

    +

    +(You can optionally change some details of the PPK format for your saved key files; see section 8.2.12. But The defaults should be fine for most purposes.)

    -

    8.2.9 Saving your public key to a disk file

    +

    8.2.10 Saving your public key to a disk file

    -RFC 4716 specifies a standard format for storing SSH-2 public keys on disk. Some SSH servers (such as ssh.com's) require a public key in this format in order to accept authentication with the corresponding private key. (Others, such as OpenSSH, use a different format; see section 8.2.10.) +RFC 4716 specifies a standard format for storing SSH-2 public keys on disk. Some SSH servers (such as ssh.com's) require a public key in this format in order to accept authentication with the corresponding private key. (Others, such as OpenSSH, use a different format; see section 8.2.11.)

    To save your public key in the SSH-2 standard format, press the ‘Save public key’ button in PuTTYgen. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press ‘Save’. @@ -197,9 +240,9 @@

    8.2.9 Saving your public

    If you use this option with an SSH-1 key, the file PuTTYgen saves will contain exactly the same text that appears in the ‘Public key for pasting’ box. This is the only existing standard for SSH-1 public keys.

    -

    8.2.10 ‘Public key for pasting into OpenSSH authorized_keys file’

    +

    8.2.11 ‘Public key for pasting into OpenSSH authorized_keys file’

    -The OpenSSH server, among others, requires your public key to be given to it in a one-line format before it will accept authentication with your private key. (SSH-1 servers also used this method.) +The OpenSSH server, among others, requires your public key to be given to it in a one-line format before it will accept authentication with your private key. (SSH-1 servers also used this method.)

    The ‘Public key for pasting into OpenSSH authorized_keys file’ gives the public-key data in the correct one-line format. Typically you will want to select the entire contents of the box using the mouse, press Ctrl+C to copy it to the clipboard, and then paste the data into a PuTTY session which is already connected to the server. @@ -207,7 +250,56 @@

    8.2.10 ‘Public k

    See section 8.3 for general instructions on configuring public-key authentication once you have generated a key.

    -

    8.2.11 Reloading a private key

    +

    8.2.12 Parameters for saving key files

    +

    +Selecting ‘Parameters for saving key files...’ from the ‘Key’ menu lets you adjust some aspects of PPK-format private key files stored on disk. None of these options affect compatibility with SSH servers. +

    +

    +In most cases, it's entirely sensible to leave all of these at their default settings. +

    +

    8.2.12.1 PPK file version

    +

    +This defaults to version 3, which is fine for most uses. +

    +

    +You might need to select PPK version 2 if you need your private key file to be loadable in older versions of PuTTY (0.74 and older), or in other tools which do not yet support the version 3 format (which was introduced in 2021). +

    +

    +The version 2 format is less resistant to brute-force decryption, and doesn't support any of the following options to control that. +

    +

    8.2.12.2 Options affecting passphrase hashing

    +

    +All of the following options only affect keys saved with passphrases. They control how much work is required to decrypt the key (which happens every time you type its passphrase). This allows you to trade off the cost of legitimate use of the key against the resistance of the encrypted key to password-guessing attacks. +

    +

    +These options only affect PPK version 3. +

    +
    +Key derivation function +
    +
    +The variant of the Argon2 key derivation function to use. You might change this if you consider your exposure to side-channel attacks to be different to the norm. +
    +
    +Memory to use for passphrase hash +
    +
    +The amount of memory needed to decrypt the key, in Kbyte. +
    +
    +Time to use for passphrase hash +
    +
    +Controls how much time is required to attempt decrypting the key. You can either specify an approximate time in milliseconds (on this machine), or explicitly specify a number of hash passes (which is what the time is turned into during encryption). +
    +
    +Parallelism for passphrase hash +
    +
    +Number of parallelisable threads that can be used to decrypt the key. The default, 1, forces the process to run single-threaded, even on machines with multiple cores. +
    +
    +

    8.2.13 Reloading a private key

    PuTTYgen allows you to load an existing private key file into memory. If you do this, you can then change the passphrase and comment before saving it again; you can also make extra copies of the public key.

    @@ -215,17 +307,17 @@

    8.2.11 Reloading a private To load an existing key, press the ‘Load’ button. PuTTYgen will put up a dialog box where you can browse around the file system and find your key file. Once you select the file, PuTTYgen will ask you for a passphrase (if necessary) and will then display the key details in the same way as if it had just generated the key.

    -If you use the Load command to load a foreign key format, it will work, but you will see a message box warning you that the key you have loaded is not a PuTTY native key. See section 8.2.12 for information about importing foreign key formats. +If you use the Load command to load a foreign key format, it will work, but you will see a message box warning you that the key you have loaded is not a PuTTY native key. See section 8.2.14 for information about importing foreign key formats.

    -

    8.2.12 Dealing with private keys in other formats

    +

    8.2.14 Dealing with private keys in other formats

    -SSH-2 private keys have no standard format. OpenSSH and ssh.com have different formats, and PuTTY's is different again. So a key generated with one client cannot immediately be used with another. +SSH-2 private keys have no standard format. OpenSSH and ssh.com have different formats, and PuTTY's is different again. So a key generated with one client cannot immediately be used with another.

    -Using the ‘Import’ command from the ‘Conversions’ menu, PuTTYgen can load SSH-2 private keys in OpenSSH's format and ssh.com's format. Once you have loaded one of these key types, you can then save it back out as a PuTTY-format key (*.PPK) so that you can use it with the PuTTY suite. The passphrase will be unchanged by this process (unless you deliberately change it). You may want to change the key comment before you save the key, since some OpenSSH key formats contained no space for a comment, and ssh.com's default comment format is long and verbose. +Using the ‘Import’ command from the ‘Conversions’ menu, PuTTYgen can load SSH-2 private keys in OpenSSH's format and ssh.com's format. Once you have loaded one of these key types, you can then save it back out as a PuTTY-format key (*.PPK) so that you can use it with the PuTTY suite. The passphrase will be unchanged by this process (unless you deliberately change it). You may want to change the key comment before you save the key, since some OpenSSH key formats contained no space for a comment, and ssh.com's default comment format is long and verbose.

    -PuTTYgen can also export private keys in OpenSSH format and in ssh.com format. To do so, select one of the ‘Export’ options from the ‘Conversions’ menu. Exporting a key works exactly like saving it (see section 8.2.8) - you need to have typed your passphrase in beforehand, and you will be warned if you are about to save a key without a passphrase. +PuTTYgen can also export private keys in OpenSSH format and in ssh.com format. To do so, select one of the ‘Export’ options from the ‘Conversions’ menu. Exporting a key works exactly like saving it (see section 8.2.9) - you need to have typed your passphrase in beforehand, and you will be warned if you are about to save a key without a passphrase.

    For OpenSSH there are two options. Modern OpenSSH actually has two formats it uses for storing private keys. ‘Export OpenSSH key’ will automatically choose the oldest format supported for the key type, for maximum backward compatibility with older versions of OpenSSH; for newer key types like Ed25519, it will use the newer format as that is the only legal option. If you have some specific reason for wanting to use OpenSSH's newer format even for RSA, DSA, or ECDSA keys, you can choose ‘Export OpenSSH key (force new file format)’. @@ -238,14 +330,14 @@

    8.3 Getting ready for p Connect to your SSH server using PuTTY with the SSH protocol. When the connection succeeds you will be prompted for your user name and password to login. Once logged in, you must configure the server to accept your public key for authentication:

    • -If your server is OpenSSH, you should change into the .ssh directory under your home directory, and open the file authorized_keys with your favourite editor. (You may have to create this file, if this is the first key you have put in it.) Then switch to the PuTTYgen window, select all of the text in the ‘Public key for pasting into OpenSSH authorized_keys file’ box (see section 8.2.10), and copy it to the clipboard (Ctrl+C). Then, switch back to the PuTTY window and insert the data into the open file, making sure it ends up all on one line. Save the file. +If your server is OpenSSH, you should change into the .ssh directory under your home directory, and open the file authorized_keys with your favourite editor. (You may have to create this file, if this is the first key you have put in it.) Then switch to the PuTTYgen window, select all of the text in the ‘Public key for pasting into OpenSSH authorized_keys file’ box (see section 8.2.11), and copy it to the clipboard (Ctrl+C). Then, switch back to the PuTTY window and insert the data into the open file, making sure it ends up all on one line. Save the file.

      (In very old versions of OpenSSH, SSH-2 keys had to be put in a separate file called authorized_keys2. In all current versions, the same authorized_keys file is used for both SSH-1 and SSH-2 keys.)

    • -If your server is ssh.com's product and is using SSH-2, you need to save a public key file from PuTTYgen (see section 8.2.9), and copy that into the .ssh2 directory on the server. Then you should go into that .ssh2 directory, and edit (or create) a file called authorization. In this file you should put a line like Key mykey.pub, with mykey.pub replaced by the name of your key file. +If your server is ssh.com's product and is using SSH-2, you need to save a public key file from PuTTYgen (see section 8.2.10), and copy that into the .ssh2 directory on the server. Then you should go into that .ssh2 directory, and edit (or create) a file called authorization. In this file you should put a line like Key mykey.pub, with mykey.pub replaced by the name of your key file.
    • For other SSH server software, you should refer to the manual for that server. @@ -260,10 +352,10 @@

      8.3 Getting ready for p Your server should now be configured to accept authentication using your private key. Now you need to configure PuTTY to attempt authentication using your private key. You can do this in any of three ways:

      • -Select the private key in PuTTY's configuration. See section 4.23.8 for details. +Select the private key in PuTTY's configuration. See section 4.21.9 for details.
      • -Specify the key file on the command line with the -i option. See section 3.8.3.18 for details. +Specify the key file on the command line with the -i option. See section 3.11.3.18 for details.
      • Load the private key into Pageant (see chapter 9). In this case PuTTY will automatically try to use it for authentication if it can. @@ -271,5 +363,5 @@

        8.3 Getting ready for p


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/Chapter9.html b/doc/Chapter9.html index 6ac5fb5..f02309c 100644 --- a/doc/Chapter9.html +++ b/doc/Chapter9.html @@ -27,10 +27,12 @@

    • 9.4 Using agent forwarding
    • -
    • 9.5 Security considerations
    • +
    • 9.5 Loading keys without decrypting them
    • +
    • 9.6 Security considerations

    Chapter 9: Using Pageant for authentication

    @@ -42,7 +44,7 @@

    9.1 Getting started with Page Before you run Pageant, you need to have a private key in *.PPK format. See chapter 8 to find out how to generate and use one.

    -When you run Pageant, it will put an icon of a computer wearing a hat into the System tray. It will then sit and do nothing, until you load a private key into it. +When you run Pageant, it will put an icon of a computer wearing a hat into the System tray. It will then sit and do nothing, until you load a private key into it. (You may need to use Windows' ‘Show hidden icons’ arrow to see the Pageant icon.)

    If you click the Pageant icon with the right mouse button, you will see a menu. Select ‘View Keys’ from this menu. The Pageant main window will appear. (You can also bring this window up by double-clicking on the Pageant icon.) @@ -60,11 +62,14 @@

    9.1 Getting started with Page Now start PuTTY and open an SSH session to a site that accepts your key. PuTTY will notice that Pageant is running, retrieve the key automatically from Pageant, and use it to authenticate. You can now open as many PuTTY sessions as you like without having to type your passphrase again.

    -(PuTTY can be configured not to try to use Pageant, but it will try by default. See section 4.23.3 and section 3.8.3.9 for more information.) +(PuTTY can be configured not to try to use Pageant, but it will try by default. See section 4.21.4 and section 3.11.3.9 for more information.)

    When you want to shut down Pageant, click the right button on the Pageant icon in the System tray, and select ‘Exit’ from the menu. Closing the Pageant main window does not shut down Pageant.

    +

    +If you want Pageant to stay running but forget all the keys it has acquired, select ‘Remove All Keys’ from the System tray menu. +

    9.2 The Pageant main window

    The Pageant main window appears when you left-click on the Pageant system tray icon, or alternatively right-click and select ‘View Keys’ from the menu. You can use it to keep track of what keys are currently loaded into Pageant, and to add new ones or remove the existing keys. @@ -73,24 +78,31 @@

    9.2.1 The key lis

    The large list box in the Pageant main window lists the private keys that are currently loaded into Pageant. The list might look something like this:

    -
    ssh-rsa 2048 22:d6:69:c9:22:51:ac:cb:b9:15:67:47:f7:65:6d:d7 k1
    -ssh-dss 2048 e4:6c:69:f3:4f:fc:cf:fc:96:c0:88:34:a7:1e:59:d7 k2
    +
    ssh-ed25519  SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w
    +ssh-rsa 2048 SHA256:8DFtyHm3kQihgy52nzX96qMcEVOq7/yJmmwQQhBWYFg
     

    For each key, the list box will tell you:

    • -The type of the key. Currently, this can be ssh-rsa (an RSA key for use with the SSH-2 protocol), ssh-dss (a DSA key for use with the SSH-2 protocol), ecdsa-sha2-* (an ECDSA key for use with the SSH-2 protocol), ssh-ed25519 (an Ed25519 key for use with the SSH-2 protocol), or ssh1 (an RSA key for use with the old SSH-1 protocol). +The type of the key. Currently, this can be ssh-rsa (an RSA key for use with the SSH-2 protocol), ssh-dss (a DSA key for use with the SSH-2 protocol), ecdsa-sha2-* (an ECDSA key for use with the SSH-2 protocol), ssh-ed25519 (an Ed25519 key for use with the SSH-2 protocol), ssh-ed448 (an Ed448 key for use with the SSH-2 protocol), or ssh1 (an RSA key for use with the old SSH-1 protocol).
    • -The size (in bits) of the key. +The size (in bits) of the key, for key types that come in different sizes.
    • The fingerprint for the public key. This should be the same fingerprint given by PuTTYgen, and (hopefully) also the same fingerprint shown by remote utilities such as ssh-keygen when applied to your authorized_keys file. +

      +By default this is shown in the ‘SHA256’ format. You can change to the older ‘MD5’ format (which looks like aa:bb:cc:...) with the ‘Fingerprint type’ drop-down, but bear in mind that this format is less secure and should be avoided for comparison purposes where possible. +

      +
    • The comment attached to the key.
    • +
    • +The state of deferred decryption, if enabled for this key. See section 9.5. +

    9.2.2 The ‘Add Key’ button

    @@ -131,6 +143,9 @@

    9.3.1 Making Page

    If Pageant is already running, this syntax loads keys into the existing Pageant.

    +

    +You can specify the ‘--encrypted’ option to defer decryption of these keys; see section 9.5. +

    9.3.2 Making Pageant run another program

    You can arrange for Pageant to start another program once it has initialised itself and loaded any keys specified on its command line. This program (perhaps a PuTTY, or a WinCVS making use of Plink, or whatever) will then be able to use the keys Pageant has loaded. @@ -140,22 +155,26 @@

    9.3.2 Making Page

    C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe
     
    -

    9.3.3 Restricting the Windows process ACL

    +

    9.3.3 Starting with the key list visible

    +

    +Start Pageant with the --keylist option to show the main window as soon as it starts up. +

    +

    9.3.4 Restricting the Windows process ACL

    -Pageant supports the same -restrict-acl option as the other PuTTY utilities to lock down the Pageant process's access control; see section 3.8.3.25 for why you might want to do this. +Pageant supports the same -restrict-acl option as the other PuTTY utilities to lock down the Pageant process's access control; see section 3.11.3.27 for why you might want to do this.

    -By default, if Pageant is started with -restrict-acl, it won't pass this to any PuTTY sessions started from its System Tray submenu. Use -restrict-putty-acl to change this. (Again, see section 3.8.3.25 for details.) +By default, if Pageant is started with -restrict-acl, it won't pass this to any PuTTY sessions started from its System Tray submenu. Use -restrict-putty-acl to change this. (Again, see section 3.11.3.27 for details.)

    -

    9.4 Using agent forwarding

    +

    9.4 Using agent forwarding

    Agent forwarding is a mechanism that allows applications on your SSH server machine to talk to the agent on your client machine.

    -Note that at present, whether agent forwarding in SSH-2 is available depends on your server. Pageant's protocol is compatible with the OpenSSH server, but the ssh.com server uses a different agent protocol, which PuTTY does not yet support. +Note that at present, whether agent forwarding in SSH-2 is available depends on your server. Pageant's protocol is compatible with the OpenSSH server, but the ssh.com server uses a different agent protocol, which PuTTY does not yet support.

    -To enable agent forwarding, first start Pageant. Then set up a PuTTY SSH session in which ‘Allow agent forwarding’ is enabled (see section 4.23.6). Open the session as normal. (Alternatively, you can use the -A command line option; see section 3.8.3.10 for details.) +To enable agent forwarding, first start Pageant. Then set up a PuTTY SSH session in which ‘Allow agent forwarding’ is enabled (see section 4.21.7). Open the session as normal. (Alternatively, you can use the -A command line option; see section 3.11.3.10 for details.)

    If this has worked, your applications on the server should now have access to a Unix domain socket which the SSH server will forward back to PuTTY, and PuTTY will forward on to the agent. To check that this has actually happened, you can try this command on Unix server machines: @@ -181,7 +200,7 @@

    9.4 Using that SSH connection as well (see the manual for your server-side SSH client to find out how to do this), your authentication keys will still be available on the next machine you connect to - two SSH connections away from where they're actually stored.

    -In addition, if you have a private key on one of the SSH servers, you can send it all the way back to Pageant using the local ssh-add command: +In addition, if you have a private key on one of the SSH servers, you can send it all the way back to Pageant using the local ssh-add command:

    unixbox:~$ ssh-add ~/.ssh/id_rsa
     Need passphrase for /home/fred/.ssh/id_rsa
    @@ -192,15 +211,39 @@ 

    9.4 Using and then it's available to every machine that has agent forwarding available (not just the ones downstream of the place you added it).

    -

    9.5 Security considerations

    +

    9.5 Loading keys without decrypting them

    +

    +You can add keys to Pageant without decrypting them. The key file will be held in Pageant's memory still encrypted, and when a client program first tries to use the key, Pageant will display a dialog box prompting for the passphrase so that the key can be decrypted. +

    +

    +This works the same way whether the key is used by an instance of PuTTY running locally, or a remote client connecting to Pageant through agent forwarding. +

    +

    +To add a key to Pageant in this encrypted form, press the ‘Add Key (encrypted)’ button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select ‘Add Key (encrypted)’ from there. Pageant will bring up a file dialog, in just the same way as it would for the plain ‘Add Key’ button. But it won't ask for a passphrase. Instead, the key will be listed in the main window with ‘(encrypted)’ after it. +

    +

    +To start Pageant up in the first place with encrypted keys loaded into it, you can use the ‘--encrypted’ option on the command line. For example: +

    +
    C:\PuTTY\pageant.exe --encrypted d:\main.ppk
    +
    +

    +After a key has been decrypted for the first use, it remains decrypted, so that it can be used again. The main window will list the key with ‘(re-encryptable)’ after it. You can revert it to the previous state, where a passphrase is required, using the ‘Re-encrypt’ button in the Pageant main window. +

    +

    +You can also ‘re-encrypt’ all keys that were added encrypted by choosing ‘Re-encrypt All Keys’ from the System tray menu. (Note that this does not discard cleartext keys that were not previously added encrypted!) +

    +

    +CAUTION: When Pageant displays a prompt to decrypt an already-loaded key, it cannot give keyboard focus to the prompt dialog box. As far as I know this is a deliberate defensive measure by Windows, against malicious software. So make sure you click in the prompt window before typing your passphrase, or else the passphrase might be sent to somewhere you didn't want to trust with it! +

    +

    9.6 Security considerations

    -Using Pageant for public-key authentication gives you the convenience of being able to open multiple SSH sessions without having to type a passphrase every time, but also gives you the security benefit of never storing a decrypted private key on disk. Many people feel this is a good compromise between security and convenience. +Using Pageant for public-key authentication gives you the convenience of being able to open multiple SSH sessions without having to type a passphrase every time, but also gives you the security benefit of never storing a decrypted private key on disk. Many people feel this is a good compromise between security and convenience.

    It is a compromise, however. Holding your decrypted private keys in Pageant is better than storing them in easy-to-find disk files, but still less secure than not storing them anywhere at all. This is for two reasons:

    • -Windows unfortunately provides no way to protect pieces of memory from being written to the system swap file. So if Pageant is holding your private keys for a long period of time, it's possible that decrypted private key data may be written to the system swap file, and an attacker who gained access to your hard disk later on might be able to recover that data. (However, if you stored an unencrypted key in a disk file they would certainly be able to recover it.) +Windows unfortunately provides no way to protect pieces of memory from being written to the system swap file. So if Pageant is holding your private keys for a long period of time, it's possible that decrypted private key data may be written to the system swap file, and an attacker who gained access to your hard disk later on might be able to recover that data. (However, if you stored an unencrypted key in a disk file they would certainly be able to recover it.)
    • Although, like most modern operating systems, Windows prevents programs from accidentally accessing one another's memory space, it does allow programs to access one another's memory space deliberately, for special purposes such as debugging. This means that if you allow a virus, trojan, or other malicious program on to your Windows system while Pageant is running, it could access the memory of the Pageant process, extract your decrypted authentication keys, and send them back to its master. @@ -217,5 +260,5 @@

      9.5 Security consideration


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/IndexPage.html b/doc/IndexPage.html index c065681..9806257 100644 --- a/doc/IndexPage.html +++ b/doc/IndexPage.html @@ -4,302 +4,306 @@ - + -

      Previous | Contents | Index | Next

      +

      Previous | Contents | Index | Next

      Index

      --4: Section 3.8.3.17
      --6: Section 3.8.3.17
      +-4: Section 3.11.3.17
      +-6: Section 3.11.3.17
      Abort Output, Telnet special command: Section 3.1.3.2
      Abort Process, Telnet special command: Section 3.1.3.2
      -accented characters: Section 3.3, Section 4.4.7
      -accessibility: Section 4.9.4
      +accented characters: Section 3.3, Section 4.4.7
      +accessibility: Section 4.9.4
      access to files, changing: Section 6.2.14
      -account name: Section 2.3, Section 3.8.3.4, Section 5.2.1.1
      -account name, for auto-login: Section 4.15.1
      -account name, for proxy: Section 4.16.4
      -account name, local, in Rlogin: Section 4.18.1
      -account name, local, in Windows: Section 4.18.1
      -account names, different: Section 2.3, Section 4.23.7
      -ACL, process (Windows): Section 3.8.3.25, Section 9.3.3
      --A command-line option: Section 3.8.3.10
      --a command-line option: Section 3.8.3.10
      -active Telnet negotiation: Section 4.17.2
      -address, IP: Section 3.5, Section 4.1.1, Section 4.14.4
      -ad-hoc proxy: Section 4.16.1, Section 4.16.5, Section 4.17.2
      -adjusting a selection: Section 3.1.1, Section 4.11.1
      -Advanced Encryption Standard: Section 4.22
      -AES: Section 4.22
      --agent: Section 3.8.3.9
      +account name: Section 2.3, Section 3.11.3.4, Section 5.2.1.1
      +account name, for auto-login: Section 4.15.1
      +account name, for proxy: Section 4.16.4
      +account name, local, in Rlogin: Section 4.30.1
      +account name, local, in Windows: Section 4.30.1
      +account names, different: Section 2.3, Section 4.21.8
      +ACL, process (Windows): Section 3.11.3.27, Section 9.3.4
      +-A command-line option: Section 3.11.3.10
      +-a command-line option: Section 3.11.3.10
      +active Telnet negotiation: Section 4.29.2
      +address, IP: Section 3.5, Section 4.1.1, Section 4.14.4
      +ad-hoc proxy: Section 4.16.1, Section 4.16.5, Section 4.29.2
      +adjusting a selection: Section 3.1.1, Section 4.11.1
      +Advanced Encryption Standard: Section 4.20
      +AES: Section 4.20
      +-agent: Section 3.11.3.9
      agent, authentication: Section 8.1, Chapter 9
      -agent forwarding: Section 3.8.3.10, Section 4.23.6, Section 9.4
      -algorithm, encryption: Section 4.22
      -algorithm, key exchange: Section 4.20.1
      +agent forwarding: Section 3.11.3.10, Section 4.21.7, Section 9.4
      +algorithm, encryption: Section 4.20
      +algorithm, key exchange: Section 4.18.1
      algorithm, public-key: Section 8.1
      -allocation, of pseudo-terminal: Section 3.8.3.12, Section 4.25.1
      -alternate screen: Section 4.6.4, Section 4.7.4, Question A.7.16
      -ALT-F4: Section 4.9.3
      -‘AltGr’ key: Section 4.4.7, Section 4.4.8
      -‘Alt’ key: Section 4.9.5
      -ALT-Space: Section 4.9.4
      -always on top: Section 4.9.6
      -ANSI colours: Section 4.13.1, Section 4.13.7
      -ANSI graphics: Section 3.3, Section 4.10.4
      -ANSI printing: Section 4.3.10
      -answerback string: Section 4.3.7
      -Application Cursor Keys: Section 4.4.4, Section 4.6.1
      -Application key: Section 4.4.7
      -Application Keypad: Section 4.4.5, Section 4.6.1
      -Arabic: Section 4.6.11
      -Arabic text shaping: Section 4.6.10
      -Arcfour: Section 4.22, Section 10.4
      +allocation, of pseudo-terminal: Section 3.11.3.12, Section 4.23.1
      +alternate screen: Section 4.6.4, Section 4.7.4, Question A.7.16
      +ALT-F4: Section 4.9.3
      +‘AltGr’ key: Section 4.4.7, Section 4.4.8
      +‘Alt’ key: Section 4.9.5
      +ALT-Space: Section 4.9.4
      +always on top: Section 4.9.6
      +ANSI colours: Section 4.13.1, Section 4.13.7
      +ANSI graphics: Section 3.3, Section 4.10.4
      +ANSI printing: Section 4.3.10
      +answerback string: Section 4.3.7
      +Application Cursor Keys: Section 4.4.4, Section 4.6.1
      +Application key: Section 4.4.7
      +Application Keypad: Section 4.4.5, Section 4.6.1
      +Arabic: Section 4.6.11
      +Arabic text shaping: Section 4.6.10
      +Arcfour: Section 4.20, Section 10.4
      Are You There, Telnet special command: Section 3.1.3.2
      -arguments, command-line: Section 3.8, Section 9.3
      -ASCII: Section 4.10.5, Question A.2.11
      +Argon2 passphrase hashing function: Section 8.2.12.2
      +arguments, command-line: Section 3.11, Section 9.3
      +ASCII: Section 4.10.5, Question A.2.11
      assertion failed: Section 10.7
      asymmetric key algorithm: Section 8.1
      -authentication: Section 4.23
      +authentication: Section 4.21
      authentication agent: Section 8.1, Chapter 9
      -authentication agent forwarding: Section 3.8.3.10, Section 4.23.6, Section 9.4
      -authentication, challenge/response: Section 4.23.4, Section 4.23.5
      -authentication, keyboard-interactive: Section 4.23.5
      -authentication, public key: Section 3.8.3.18, Section 4.23.8, Section 4.28.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      -authentication, to proxy: Section 4.16.4
      -authentication, X11: Section 4.26.1
      -Authenticode: Appendix E
      -authorized_keys file: Section 8.2.10, Section 8.3
      +authentication agent forwarding: Section 3.11.3.10, Section 4.21.7, Section 9.4
      +authentication, challenge/response: Section 4.21.5, Section 4.21.6
      +authentication, keyboard-interactive: Section 4.21.6
      +authentication, public key: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      +authentication, to proxy: Section 4.16.4
      +authentication, X11: Section 4.24.1
      +Authenticode: Appendix F
      +authorized_keys file: Section 8.2.11, Section 8.3
      AUTOEXEC.BAT: Section 5.1, Section 7.1
      automated operations: Chapter 7
      -auto wrap mode: Section 4.3.1, Question A.7.7
      -background colour: Section 4.3.5, Question A.7.6
      -background colour, bright: Section 4.3.6
      -background colour, default: Section 4.13.6, Section 4.13.7
      -backspace, destructive: Section 4.6.8
      -backspace key: Section 4.4.1
      -bandwidth: Section 4.14.2, Section 4.15.4, Section 4.19.3
      -banner: Section 4.23.1
      -‘basic’ authentication (HTTP): Section 4.16.4
      -batch files: Section 4.30, Section 5.2.3, Section 7.3
      +auto wrap mode: Section 4.3.1, Question A.7.7
      +background colour: Section 4.3.5, Question A.7.6
      +background colour, bright: Section 4.3.6
      +background colour, default: Section 4.13.6, Section 4.13.7
      +backspace, destructive: Section 4.6.8
      +backspace key: Section 4.4.1
      +bandwidth: Section 4.14.2, Section 4.15.4, Section 4.17.3
      +banner: Section 4.21.1
      +bare ssh-connection protocol: Section 4.27
      +‘basic’ authentication (HTTP): Section 4.16.4
      +batch files: Section 4.32, Section 5.2.3, Section 7.3
      -batch Plink command-line option: Section 7.2.3.1
      -batch PSCP command-line option: Section 5.2.2.5
      -batch PSFTP command-line option: Section 6.1.4
      batch scripts in PSFTP: Section 6.1.1
      -baud rate, of terminal: Section 4.15.4
      +baud rate, of terminal: Section 4.15.4
      -bc PSFTP command-line option: Section 6.1.2
      -beep, terminal: Section 4.5
      -beep, with PC speaker: Section 4.5.1
      -bell, disabling: Section 4.5.1, Section 4.5.3
      -bell overload mode: Section 4.5.3
      -bell, terminal: Section 4.5
      -bell, visual: Section 4.5.1
      +beep, terminal: Section 4.5
      +beep, with PC speaker: Section 4.5.1
      +bell, disabling: Section 4.5.1, Section 4.5.3
      +bell overload mode: Section 4.5.3
      +bell, terminal: Section 4.5
      +bell, visual: Section 4.5.1
      -be PSFTP command-line option: Section 6.1.3
      -bidirectional text: Section 4.6.11
      -bind address: Section 3.5, Section 3.8.3.5, Section 4.27
      -BitchX: Section 4.6.9
      -8-bit colour: Section 4.13.5
      +bidirectional text: Section 4.6.11
      +bind address: Section 3.5, Section 3.11.3.5, Section 4.25
      +BitchX: Section 4.6.9
      +8-bit colour: Section 4.13.5
      32-bit Windows: Question A.6.10
      64-bit Windows: Question A.6.10, Question A.7.22
      -black, bold: Section 4.13.4
      -blinking cursor: Section 4.8.1
      -blinking text: Section 4.3.6
      -Blowfish: Section 4.22, Section 10.4
      -bold black: Section 4.13.4
      -bold text: Section 4.13.4, Section 4.13.6, Section 4.13.7
      -box-drawing characters: Section 3.3, Section 4.10.4
      +black, bold: Section 4.13.4
      +blinking cursor: Section 4.8.1
      +blinking text: Section 4.3.6
      +Blowfish: Section 4.20, Section 10.4
      +bold black: Section 4.13.4
      +bold text: Section 4.13.4, Section 4.13.6, Section 4.13.7
      +box-drawing characters: Section 3.3, Section 4.10.4
      -b PSFTP command-line option: Section 6.1.1
      Break, serial special command: Section 3.1.3.2
      Break, SSH special command: Section 3.1.3.2
      Break, Telnet special command: Section 3.1.3.2
      -bright black: Section 4.13.4
      -BSD: Section 4.17.1
      +bright black: Section 4.13.4
      +BSD: Section 4.29.1
      bug reporting: Appendix B
      -bugs, in SSH servers: Section 4.28
      +bugs, in SSH servers: Section 4.26
      bulletin board system: Section 1.1
      -cache, of SSH host keys: Section 2.2, Section 3.1.3.2, Section 3.8.3.19, Section 3.8.3.20, Section 4.14.5, Section 4.21, Section 4.21.2
      -Caps Lock: Section 4.10.3
      -Carriage Return: Section 4.3.3, Section 4.3.4
      -cascading credentials: Section 4.20.1.1
      +cache, of SSH host keys: Section 2.2, Section 3.1.3.2, Section 3.11.3.20, Section 3.11.3.21, Section 4.14.5, Section 4.19, Section 4.19.3
      +Caps Lock: Section 4.10.3
      +Carriage Return: Section 4.3.3, Section 4.3.4
      +cascading credentials: Section 4.18.1.1
      cat: Question A.7.13
      --C command-line option: Section 3.8.3.15
      -ChaCha20-Poly1305: Section 4.22
      -challenge/response authentication: Section 4.23.4, Section 4.23.5
      +-C command-line option: Section 3.11.3.15
      +ChaCha20-Poly1305: Section 4.20
      +challenge/response authentication: Section 4.21.5, Section 4.21.6
      ‘Change Settings’: Section 3.1.3.4
      changing permissions on files: Section 6.2.14
      -changing user names: Section 2.3, Section 4.23.7
      -CHAP: Section 4.16.4
      -character classes: Section 4.12.1
      -characters, accented: Section 3.3, Section 4.4.7
      -character set: Section 3.3, Section 4.6.9, Section 4.10
      -characters, line-drawing: Section 3.3, Section 4.10.4
      -character width: Section 4.10.2
      -Chinese: Section 4.10.2
      -choosing a protocol: Section 1.2, Section 3.8.3.2
      -cipher algorithm: Section 4.22
      -Cisco: Section 3.8.3.6
      -CJK: Section 4.10.2
      -CJK ambiguous characters: Section 4.10.2
      +changing user names: Section 2.3, Section 4.21.8
      +CHAP: Section 4.16.4
      +character classes: Section 4.12.1
      +characters, accented: Section 3.3, Section 4.4.7
      +character set: Section 3.3, Section 4.6.9, Section 4.10
      +characters, line-drawing: Section 3.3, Section 4.10.4
      +character width: Section 4.10.2
      +Chinese: Section 4.10.2
      +choosing a protocol: Section 1.2, Section 3.11.3.2
      +cipher algorithm: Section 4.20
      +Cisco: Section 3.11.3.6
      +CJK: Section 4.10.2
      +CJK ambiguous characters: Section 4.10.2
      clean up after PuTTY: Question A.8.2
      --cleanup command-line option: Section 3.8.2
      -clear screen: Section 4.3.5
      +-cleanup command-line option: Section 3.11.2
      +clear screen: Section 4.3.5
      ‘Clear Scrollback’: Section 3.1.3.6
      client: Section 1.1
      clipboard: Section 3.1.1, Section 3.1.3.1, Section 3.1.3.5
      -CLIPBOARD selection: Section 4.11.4
      -clipboards, multiple: Section 4.11.4
      -Close button: Section 2.5, Section 4.9.2
      -closing window: Section 4.1.3, Section 4.9.2, Section 4.9.3
      -code page: Section 4.10.1
      -colour: Section 4.12.2, Section 4.13, Question A.7.2
      -colour, background, default: Section 4.13.6, Section 4.13.7
      -colour, 8-bit: Section 4.13.5
      -colour, foreground, default: Section 4.13.6, Section 4.13.7
      -256-colour mode: Section 4.13.2
      -colour, of cursor: Section 4.13.6, Section 4.13.7
      -colours, ANSI: Section 4.13.1, Section 4.13.7
      -colours, system: Section 4.13.6
      -columns, in terminal window: Section 4.7.1
      -COM1: Section 4.29.1
      -command-line arguments: Section 3.8, Section 9.3
      +CLIPBOARD selection: Section 4.11.4
      +clipboards, multiple: Section 4.11.4
      +Close button: Section 2.5, Section 4.9.2
      +closing window: Section 4.1.3, Section 4.9.2, Section 4.9.3
      +code page: Section 4.10.1
      +colour: Section 4.12.2, Section 4.13, Question A.7.2
      +colour, background, default: Section 4.13.6, Section 4.13.7
      +colour, 8-bit: Section 4.13.5
      +colour, foreground, default: Section 4.13.6, Section 4.13.7
      +256-colour mode: Section 4.13.2
      +colour, of cursor: Section 4.13.6, Section 4.13.7
      +colours, ANSI: Section 4.13.1, Section 4.13.7
      +colours, system: Section 4.13.6
      +columns, in terminal window: Section 4.7.1
      +COM1: Section 4.28.1
      +command-line arguments: Section 3.11, Section 9.3
      command-line interface: Section 1.1
      -command line, loading saved sessions from: Section 3.8.3.1
      --1 command-line option: Section 3.8.3.16
      --2 command-line option: Section 3.8.3.16
      -Command Prompt: Section 1.1, Section 1.1, Section 3.8, Section 5.1, Section 7.1
      -command, proxy: Section 3.8.3.24, Section 4.16.1, Section 4.16.5
      -commands on the server: Section 2.4, Section 3.8.3.6, Section 4.19.1, Section 4.19.2, Question A.6.2
      -commands, reading from a file: Section 3.8.3.6
      +command line, loading saved sessions from: Section 3.11.3.1
      +-1 command-line option: Section 3.11.3.16
      +-2 command-line option: Section 3.11.3.16
      +Command Prompt: Section 1.1, Section 1.1, Section 3.11, Section 5.1, Section 7.1
      +command, proxy: Section 3.11.3.26, Section 4.16.1, Section 4.16.5
      +commands on the server: Section 2.4, Section 3.11.3.6, Section 4.17.1, Section 4.17.2, Question A.6.2
      +commands, reading from a file: Section 3.11.3.6
      comment: Section 6.2
      -Compose key: Section 4.4.7
      -compression: Section 3.8.3.15, Section 4.19.3
      -confidentiality: Section 4.20.2
      -configuration: Section 3.8.3.22
      +Compose key: Section 4.4.7
      +compression: Section 3.11.3.15, Section 4.17.3
      +confidentiality: Section 4.18.2
      +configuration: Section 3.11.3.23
      configuration options: Chapter 4
      -connection, network: Section 3.5, Section 4.14, Section 4.27
      -connections, half-open: Section 4.14.3
      -connections, idle: Section 4.14.1, Question A.7.8
      -connections, interactive: Section 4.14.2, Section 4.25.1, Chapter 7
      -connectivity, breaks in: Section 4.14.1, Question A.7.9
      -CONNECT proxy (HTTP): Section 4.16.1
      -console window: Section 1.1, Section 1.1, Section 3.8, Section 5.1, Section 7.1
      -context menu: Section 3.1.1, Section 3.1.3, Section 4.11.1
      -Control-?: Section 4.4.1
      -Control-H: Section 4.4.1
      -control sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.5, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 4.15.3, Section 7.2.1, Question A.5.1
      -cookie, magic: Section 4.26.1
      -coordinates, cursor: Section 4.3.2
      +connection, network: Section 3.5, Section 4.14, Section 4.25
      +connections, half-open: Section 4.14.3
      +connections, idle: Section 4.14.1, Question A.7.8
      +connections, interactive: Section 4.14.2, Section 4.23.1, Chapter 7
      +connectivity, breaks in: Section 4.14.1, Question A.7.9
      +CONNECT proxy (HTTP): Section 4.16.1
      +console window: Section 1.1, Section 1.1, Section 3.11, Section 5.1, Section 7.1
      +context menu: Section 3.1.1, Section 3.1.3, Section 4.11.1
      +Control-?: Section 4.4.1
      +Control-H: Section 4.4.1
      +control sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.5, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 4.15.3, Section 7.2.1, Question A.5.1
      +cookie, magic: Section 4.24.1
      +coordinates, cursor: Section 4.3.2
      Copy All to Clipboard: Section 3.1.3.5
      -copy and paste: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
      -copyright: Appendix C
      -corruption, of display: Section 4.6.10, Section 4.6.11
      -CP437: Section 4.10.1
      -CP866: Section 4.10.1
      +copy and paste: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
      +copyright: Appendix D
      +corruption, of display: Section 4.6.10, Section 4.6.11
      +CP437: Section 4.10.1
      +CP866: Section 4.10.1
      -c Pageant command-line option: Section 9.3.2
      CRC: Section 10.12
      -CR (Carriage Return): Section 4.3.3, Section 4.3.4
      +CR (Carriage Return): Section 4.3.3, Section 4.3.4
      creating directories: Section 6.2.16
      creating key pairs: Section 8.2
      -credential delegation, GSSAPI: Section 4.24.1
      -credentials, cascading: Section 4.20.1.1
      -CryptoCard authentication: Section 4.23.4
      -Ctrl-?: Section 4.4.1
      +credential delegation, GSSAPI: Section 4.22.1
      +credentials, cascading: Section 4.18.1.1
      +CryptoCard authentication: Section 4.21.5
      +Ctrl-?: Section 4.4.1
      Ctrl-Break: Section 3.1.3.2
      -Ctrl-C: Section 3.1.1, Section 3.1.1, Section 4.11.4
      -Ctrl-H: Section 4.4.1
      -Ctrl-Ins: Section 3.1.1, Section 4.11.4.2
      +Ctrl-C: Section 3.1.1, Section 3.1.1, Section 4.11.4
      +Ctrl-H: Section 4.4.1
      +Ctrl-Ins: Section 3.1.1, Section 4.11.4.2
      Ctrl-PgDn: Section 3.1.2
      Ctrl-PgUp: Section 3.1.2
      -Ctrl-Shift-C: Section 4.11.4.2
      +Ctrl-Shift-C: Section 4.11.4.2
      Ctrl-Shift-PgDn: Section 3.1.2
      Ctrl-Shift-PgUp: Section 3.1.2
      -Ctrl-Shift-V: Section 4.11.4.2
      -Ctrl-V: Section 4.11.4
      +Ctrl-Shift-V: Section 4.11.4.2
      +Ctrl-V: Section 4.11.4
      Ctrl, with right mouse button: Section 3.1.1
      current working directory: Section 6.2.7, Section 6.2.8
      -cursor: Section 4.8.1
      -cursor, blinking: Section 4.8.1
      -cursor colour: Section 4.13.6, Section 4.13.7
      -cursor coordinates: Section 4.3.2
      -cursor keys, ‘Application’ mode: Section 4.4.4, Section 4.6.1
      -cut and paste: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
      +cursor: Section 4.8.1
      +cursor, blinking: Section 4.8.1
      +cursor colour: Section 4.13.6, Section 4.13.7
      +cursor coordinates: Section 4.3.2
      +cursor keys, ‘Application’ mode: Section 4.4.4, Section 4.6.1
      +cut and paste: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
      CVS: Section 7.4
      CVS_RSH environment variable: Section 7.4
      -Cyrillic: Section 4.10.3
      --D command-line option: Section 3.8.3.5
      -debugging Internet protocols: Section 3.6
      -DEC Compose key: Section 4.4.7
      -DEC Origin Mode: Section 4.3.2
      +Cyrillic: Section 4.10.3
      +-D command-line option: Section 3.11.3.5
      +debugging Internet protocols: Section 3.7
      +DEC Compose key: Section 4.4.7
      +DEC Origin Mode: Section 4.3.2
      DECterm: Question A.5.1
      -Default Beep sound, Windows: Section 4.5
      -Default Settings: Section 3.8.1, Section 4.1.2
      +Default Beep sound, Windows: Section 4.5
      +Default Settings: Section 3.11.1, Section 4.1.2
      delays, in SSH-2 sessions: Question A.7.20
      -delegation, of GSSAPI credentials: Section 4.24.1
      +delegation, of GSSAPI credentials: Section 4.22.1
      deleting directories: Section 6.2.17
      deleting files: Section 6.2.15
      -DES: Section 4.22, Section 10.4
      -destructive backspace: Section 4.6.8
      +DES: Section 4.20, Section 10.4
      +destructive backspace: Section 4.6.8
      development snapshots: Section B.2
      -diagnostic, proxy: Section 4.16.6
      +diagnostic, proxy: Section 4.16.6
      dialog box: Section 2.1
      -DiceWare: Section 8.2.7
      -differences between SSH, Telnet and Rlogin: Section 1.2
      -different user names: Section 2.3, Section 4.23.7
      -Diffie-Hellman group exchange: Section 4.20.1
      -Diffie-Hellman key exchange: Section 4.20.1
      -digital signature: Section 4.28.6, Section 8.1
      -Digital Signature Standard: Section 4.21.1, Section 4.21.1, Section 4.21.1, Section 8.1, Section 8.2.2, Question A.8.3
      +DiceWare: Section 8.2.8
      +differences between SSH, Telnet, Rlogin, and SUPDUP: Section 1.2
      +different user names: Section 2.3, Section 4.21.8
      +Diffie-Hellman group exchange: Section 4.18.1
      +Diffie-Hellman key exchange: Section 4.18.1
      +digital signature: Section 4.26.6, Section 8.1
      +Digital Signature Standard: Section 4.19.1, Section 4.19.1, Section 8.1, Section 8.2.2, Question A.8.3
      directories, creating: Section 6.2.16
      directories, removing: Section 6.2.17
      -display corruption: Section 4.6.10, Section 4.6.11
      +display corruption: Section 4.6.10, Section 4.6.11
      DISPLAY environment variable: Section 3.4
      -DLL: Section 4.24.2, Question A.6.10
      -DNS: Section 4.16.3
      -DNS name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
      -DNS resolution: Section 4.16.3
      -DNS, with proxy: Section 4.16.2, Section 4.16.3
      -Domain Name System: Section 4.16.3
      -double-click: Section 3.1.1, Section 4.12.1
      -double-width character: Section 4.10.2
      +DLL: Section 4.22.2, Question A.6.10
      +DNS: Section 4.16.3
      +DNS name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
      +DNS resolution: Section 4.16.3
      +DNS, with proxy: Section 4.16.2, Section 4.16.3
      +Domain Name System: Section 4.16.3
      +double-click: Section 3.1.1, Section 4.12.1
      +double-width character: Section 4.10.2
      downloading files: Section 5.2.1, Section 6.2.9
      -Dragon NaturallySpeaking: Section 4.9.4
      -DSA: Section 4.21.1, Section 4.21.1, Section 4.21.1, Section 8.1, Section 8.2.2, Question A.8.3
      -DSA authentication: Section 3.8.3.18, Section 4.23.8, Section 4.28.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      +Dragon NaturallySpeaking: Section 4.9.4
      +DSA: Section 4.19.1, Section 4.19.1, Section 8.1, Section 8.2.2, Question A.8.3
      +DSA authentication: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      ‘Duplicate Session’: Section 3.1.3.3, Question A.7.22
      -dynamic port forwarding: Section 3.5, Section 3.8.3.5, Section 4.27
      -East Asian Ambiguous characters: Section 4.10.2
      +dynamic port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
      +East Asian Ambiguous characters: Section 4.10.2
      ECDSA: Section 8.1, Section 8.2.2
      -echo, local: Section 4.3.8, Section 4.3.9, Question A.2.4
      -echo, remote: Section 4.3.8
      -Ed25519: Section 8.2.2
      -Edwards-curve: Section 4.21.1
      -elliptic curve: Section 4.20.1, Section 4.21.1, Section 8.2.2
      -emulation, terminal: Section 3.1.3.6, Section 4.3, Section 4.3.7, Section 4.4, Section 4.6, Section 4.15.3
      -encryption: Section 4.28.10, Section 8.1, Section 8.2.7
      -encryption algorithm: Section 4.22
      -End key: Section 4.4.2
      +echo, local: Section 4.3.8, Section 4.3.9, Question A.2.4
      +echo, remote: Section 4.3.8
      +Ed25519: Section 4.19.1, Section 8.2.3
      +Ed448: Section 4.19.1, Section 8.2.3
      +EdDSA: Section 4.19.1, Section 4.19.1, Section 8.2.2
      +Edwards-curve DSA: Section 4.19.1, Section 4.19.1, Section 8.2.2
      +elliptic curve: Section 4.18.1, Section 4.19.1, Section 8.2.2
      +emulation, terminal: Section 3.1.3.6, Section 4.3, Section 4.3.7, Section 4.4, Section 4.6, Section 4.15.3
      +encryption: Section 4.26.10, Section 8.1, Section 8.2.8
      +encryption algorithm: Section 4.20
      +End key: Section 4.4.2
      End Of File, Telnet special command: Section 3.1.3.2
      End Of Record, Telnet special command: Section 3.1.3.2
      -enquiry character: Section 4.3.7
      -environment variables: Section 4.15.5, Section 4.17.1
      -Erase Character, Telnet special command: Section 3.1.3.2, Section 4.17.3
      +enquiry character: Section 4.3.7
      +environment variables: Section 4.15.5, Section 4.29.1
      +Erase Character, Telnet special command: Section 3.1.3.2, Section 4.29.3
      Erase Line, Telnet special command: Section 3.1.3.2
      -erase screen: Section 4.3.5
      -ERASE, special character: Section 4.25.2
      +erase screen: Section 4.3.5
      +ERASE, special character: Section 4.23.2
      ERRORLEVEL: Section 5.2.3
      error messages: Chapter 10
      -escape sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 7.2.1
      -Event Log: Section 3.1.3.1, Section 4.2
      +escape sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 7.2.1
      +Event Log: Section 3.1.3.1, Section 4.2
      execute permission: Section 6.2.14
      exit value: Section 5.2.3
      -expiry, of passwords: Section 4.23.5
      -exporting private keys: Section 8.2.12
      -extending a selection: Section 3.1.1, Section 4.11.1
      +expiry, of passwords: Section 4.21.6
      +exporting private keys: Section 8.2.14
      +extending a selection: Section 3.1.1, Section 4.11.1
      FAQ: Appendix A
      features, supported: Section A.2
      feedback: Appendix B
      -feep: Section 4.5
      +feep: Section 4.5
      filenames containing spaces: Section 6.2.1, Question A.6.9
      files, changing permissions on: Section 6.2.14
      files, deleting: Section 6.2.15
      @@ -310,634 +314,660 @@

      Index

      files, sending: Section 5.2.1, Section 6.2.10
      files, transferring: Chapter 5, Chapter 6
      finger: Section 3.5
      -fingerprint, of PGP key: Section 3.8.3.21
      -fingerprint, of SSH authentication key: Section 8.2.5, Section 9.2.1
      -fingerprint, of SSH host key: Section 2.2
      -firewalls: Section 4.14.1, Section 4.17.2, Section 4.20.2, Section 10.16, Question A.7.8
      -flashing cursor: Section 4.8.1
      -flashing text: Section 4.3.6
      -flow-control window: Section 4.28.5
      -font: Section 4.8.2, Section 4.10.4, Section 4.12.2, Section 4.13.4
      -font size: Section 4.7.2, Section 4.8.2
      -foreground colour, default: Section 4.13.6, Section 4.13.7
      -forwarding, of X11: Section 3.4, Section 3.8.3.11, Section 4.26
      -forwarding ports in SSH: Section 3.5, Section 3.8.3.5, Section 4.16, Section 4.19.2, Section 4.27
      -forwarding ports in SSH, changing mid-session: Section 4.27
      -forwarding, SSH agent: Section 3.8.3.10, Section 4.23.6, Section 9.4
      +fingerprint, MD5, of SSH host key: Section 2.2, Section 4.19.3
      +fingerprint, of PGP key: Section 3.11.3.22
      +fingerprint, of SSH authentication key: Section 8.2.6, Section 9.2.1
      +fingerprint, of SSH host key: Section 2.2
      +fingerprint, SHA-256, of SSH host key: Section 2.2, Section 4.19.3
      +firewalls: Section 4.14.1, Section 4.18.2, Section 4.29.2, Section 10.16, Question A.7.8
      +flashing cursor: Section 4.8.1
      +flashing text: Section 4.3.6
      +flow-control window: Section 4.26.5
      +font: Section 4.8.2, Section 4.10.4, Section 4.12.2, Section 4.13.4
      +font size: Section 4.7.2, Section 4.8.2
      +foreground colour, default: Section 4.13.6, Section 4.13.7
      +forwarding, of X11: Section 3.4, Section 3.11.3.11, Section 4.24
      +forwarding ports in SSH: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
      +forwarding ports in SSH, changing mid-session: Section 4.25
      +forwarding, SSH agent: Section 3.11.3.10, Section 4.21.7, Section 9.4
      Frequently Asked Questions: Appendix A
      ftp: Chapter 6
      -full-screen mode: Section 3.1.3.7, Section 4.7.3, Section 4.9.7
      -function keys: Section 4.4.3, Question A.7.13
      +full-screen mode: Section 3.1.3.7, Section 4.7.3, Section 4.9.7
      +function keys: Section 4.4.3, Question A.7.13
      generating key pairs: Section 8.2
      glob (wildcard): Section 5.2.1, Section 5.2.1.3, Section 5.2.2.6, Section 6.2.2, Section 6.2.11
      Go Ahead, Telnet special command: Section 3.1.3.2
      -GPG signatures, of PuTTY binaries: Appendix E
      -graphical applications: Section 3.4, Section 4.26
      -group exchange, Diffie-Hellman: Section 4.20.1
      -GSSAPI: Section 4.15.2, Section 4.24
      -GSSAPI credential delegation: Section 4.24.1
      +GPG signatures, of PuTTY binaries: Appendix F
      +graphical applications: Section 3.4, Section 4.24
      +group exchange, Diffie-Hellman: Section 4.18.1
      +GSSAPI: Section 4.15.2, Section 4.22
      +GSSAPI credential delegation: Section 4.22.1
      Gtk: Question A.3.2
      -half-open connections: Section 4.14.3
      -Hebrew: Section 4.6.11
      +half-open connections: Section 4.14.3
      +Hebrew: Section 4.6.11
      history: Section 3.1.2
      -HMAC: Section 4.28.8
      +HMAC: Section 4.26.8
      home directory: Section 5.2.1.3
      -Home key: Section 4.4.2
      --hostkey: Section 3.8.3.20
      -host key fingerprint (SSH): Section 2.2
      -host key management: Section 2.2, Section 3.1.3.2, Section 3.8.3.19, Section 3.8.3.20, Section 4.14.5, Section 4.21, Section 4.21.2
      -host keys, manually configuring: Section 3.8.3.20, Section 4.21.2
      -host keys, trusting: Section 2.2
      +Home key: Section 4.4.2
      +-hostkey: Section 3.11.3.21
      +host key fingerprint (SSH): Section 2.2
      +host key management: Section 2.2, Section 3.1.3.2, Section 3.11.3.20, Section 3.11.3.21, Section 4.14.5, Section 4.19, Section 4.19.3
      +host keys, manually configuring: Section 3.11.3.21, Section 4.19.3
      +host keys, trusting: Section 2.2
      host keys, upgrading: Section 3.1.3.2
      -host key type: Section 4.21.1
      -host key, verifying: Section 2.2, Question A.2.9
      -host name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
      -host name, logical: Section 3.8.3.19, Section 4.14.5
      -host name resolution: Section 4.16.3
      -host name resolution, with proxy: Section 4.16.2, Section 4.16.3
      -HTTP: Section 3.6
      -HTTP ‘basic’ authentication: Section 4.16.4
      -HTTP proxy: Section 4.16.1
      --i command-line option: Section 3.8.3.18
      -icon, PuTTY's: Section 2.3, Section A.5.3
      -icon title: Section 4.9.1
      -idle connections: Section 4.14.1, Question A.7.8
      -‘ignore’ messages, in SSH: Section 3.1.3.2, Section 4.28.1, Section 4.28.11
      +host key type: Section 4.19.1
      +host key, verifying: Section 2.2, Question A.2.9
      +host name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
      +host name, logical: Section 3.11.3.20, Section 4.14.5
      +host name resolution: Section 4.16.3
      +host name resolution, with proxy: Section 4.16.2, Section 4.16.3
      +HTTP: Section 3.7
      +HTTP ‘basic’ authentication: Section 4.16.4
      +HTTP proxy: Section 4.16.1
      +-i command-line option: Section 3.11.3.18
      +icon, PuTTY's: Section 2.3, Section A.5.3
      +icon title: Section 4.9.1
      +idle connections: Section 4.14.1, Question A.7.8
      +‘ignore’ messages, in SSH: Section 3.1.3.2, Section 4.26.1, Section 4.26.11
      IGNORE message, SSH special command: Section 3.1.3.2
      -importing private keys: Section 8.2.12
      -inactive window: Section 4.1.3
      +importing private keys: Section 8.2.14
      +inactive window: Section 4.1.3
      indenting: Section 3.1.1
      -integrity: Section 4.20.2
      -interactive connections: Section 4.14.2, Section 4.25.1, Chapter 7
      -intermittent connectivity: Section 4.14.1, Question A.7.9
      +integrity: Section 4.18.2
      +interactive connections: Section 4.14.2, Section 4.23.1, Chapter 7
      +intermittent connectivity: Section 4.14.1, Question A.7.9
      internal error: Section 10.7
      internal fault: Section 10.7
      -Internet protocols, debugging: Section 3.6
      -Internet Protocol version: Section 3.8.3.17, Section 4.14.4, Section 4.27.2
      -Interrupt Process, Telnet special command: Section 3.1.3.2, Section 4.17.3
      -IP address: Section 3.5, Section 4.1.1, Section 4.14.4
      -IP address, loopback: Section 3.5, Section 4.16.2
      +Internet protocols, debugging: Section 3.7
      +Internet Protocol version: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
      +Interrupt Process, Telnet special command: Section 3.1.3.2, Section 4.29.3
      +IP address: Section 3.5, Section 4.1.1, Section 4.14.4
      +IP address, loopback: Section 3.5, Section 4.16.2
      IP masquerading: Section 10.16, Question A.7.8
      -IPv4: Section 3.8.3.17, Section 4.14.4, Section 4.27.2
      -IPv6: Section 3.8.3.17, Section 4.14.4, Section 4.27.2
      -IPv6 address: Section 4.27
      -ISO-8859: Section 4.10.1
      -ISO-10646 (Unicode): Section 4.6.10, Section 4.10.1, Section 4.10.4, Section 4.10.5
      -IUTF8 terminal mode: Section 4.25.2
      -Japanese: Section 4.10.2
      -keepalives, application: Section 4.14.1, Section 4.20.2, Section 4.28.1, Section 4.28.11
      -keepalives, TCP: Section 4.14.3
      -Kerberos: Section 4.24
      -kex: Section 4.20
      -keyboard: Section 4.4, Section 4.15.3, Question A.7.12, Question A.7.13
      -keyboard-interactive authentication: Section 4.23.5
      -key exchange: Section 4.20
      -key exchange algorithm: Section 4.20.1
      -key exchange, Diffie-Hellman: Section 4.20.1
      +IPv4: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
      +IPv6: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
      +IPv6 address: Section 4.25
      +ISO-8859: Section 4.10.1
      +ISO-10646 (Unicode): Section 4.6.10, Section 4.10.1, Section 4.10.4, Section 4.10.5
      +IUTF8 terminal mode: Section 4.23.2
      +Japanese: Section 4.10.2
      +keepalives, application: Section 4.14.1, Section 4.18.2, Section 4.26.1, Section 4.26.11
      +keepalives, TCP: Section 4.14.3
      +Kerberos: Section 4.22
      +kex: Section 4.18
      +keyboard: Section 4.4, Section 4.15.3, Question A.7.12, Question A.7.13
      +keyboard-interactive authentication: Section 4.21.6
      +key exchange: Section 4.18
      +key exchange algorithm: Section 4.18.1
      +key exchange, Diffie-Hellman: Section 4.18.1
      key exchange, forcing repeat: Section 3.1.3.2
      -key exchange, repeat: Section 3.1.3.2, Section 4.20.2, Section 4.28.2, Question A.7.20
      -keypad, ‘Application’ mode: Section 4.4.5, Section 4.6.1
      -keypad, NetHack mode: Section 4.4.6
      -keypad, numeric: Section 4.4.3
      +key exchange, repeat: Section 3.1.3.2, Section 4.18.2, Section 4.26.2, Question A.7.20
      +--keylist: Section 9.3.3
      +keypad, ‘Application’ mode: Section 4.4.5, Section 4.6.1
      +keypad, NetHack mode: Section 4.4.6
      +keypad, numeric: Section 4.4.3
      key pair: Section 8.1
      key pairs, generating: Section 8.2
      known_hosts: Question A.2.9
      -Korean: Section 4.10.2
      -last selected text: Section 4.11.4.2
      --L command-line option: Section 3.8.3.5
      --l command-line option: Section 3.8.3.4
      -left mouse button: Section 3.1.1, Section 4.11.1
      -LF (Line Feed): Section 4.3.3, Section 4.3.4
      -licence: Appendix C
      -line-drawing characters: Section 3.3, Section 4.10.4
      -line editing, local: Section 4.3.9
      -Line Feed: Section 4.3.3, Section 4.3.4
      +Korean: Section 4.10.2
      +last selected text: Section 4.11.4.2
      +-L command-line option: Section 3.11.3.5
      +-l command-line option: Section 3.11.3.4
      +left mouse button: Section 3.1.1, Section 4.11.1
      +LF (Line Feed): Section 4.3.3, Section 4.3.4
      +licence: Appendix D
      +line-drawing characters: Section 3.3, Section 4.10.4
      +line editing, local: Section 4.3.9
      +Line Feed: Section 4.3.3, Section 4.3.4
      lines, selecting: Section 3.1.1
      -line wrapping, automatic: Section 4.3.1, Question A.7.7
      -links (web browser): Section 4.6.2
      -Linux: Section 4.4, Section 4.4.1, Section 4.15.3, Section 4.25.1, Section 4.27.2, Section 6.2.14, Section 10.18, Question A.7.13
      +line wrapping, automatic: Section 4.3.1, Question A.7.7
      +links (web browser): Section 4.6.2
      +Linux: Section 4.4, Section 4.4.1, Section 4.15.3, Section 4.23.1, Section 4.25.2, Section 6.2.14, Section 10.18, Question A.7.13
      Linux, Red Hat: Question A.7.15
      Linux version of PuTTY tools: PuTTY User Manual, Question A.3.2
      -Linux virtual console: Section 4.4.3
      -listen address: Section 3.5, Section 3.8.3.5, Section 4.27
      +Linux virtual console: Section 4.4.3
      +listen address: Section 3.5, Section 3.11.3.5, Section 4.25
      listing files: Section 5.2.2.1, Section 6.2.13
      --load command-line option: Section 3.8.3.1
      -loading private keys: Section 8.2.12
      -loading saved sessions from command line: Section 3.8.3.1
      -loading settings: Section 4.1.2
      -loading settings from a file: Section 4.30
      -local echo: Section 4.3.8, Section 4.3.9, Question A.2.4
      -localhost: Section 3.5, Section 4.16.2, Section 4.27.1, Question A.7.17
      -local line editing: Section 4.3.9
      -local proxy: Section 3.8.3.24, Section 4.16.1, Section 4.16.5
      -local-to-remote port forwarding: Section 3.5, Section 3.8.3.5, Section 4.27
      -local user name, in Rlogin: Section 4.18.1
      -local user name, in Windows: Section 4.18.1
      +-load command-line option: Section 3.11.3.1
      +loading private keys: Section 8.2.14
      +loading saved sessions from command line: Section 3.11.3.1
      +loading settings: Section 4.1.2
      +loading settings from a file: Section 4.32
      +local echo: Section 4.3.8, Section 4.3.9, Question A.2.4
      +localhost: Section 3.5, Section 4.16.2, Section 4.25.1, Question A.7.17
      +local line editing: Section 4.3.9
      +local proxy: Section 3.11.3.26, Section 4.16.1, Section 4.16.5
      +local-to-remote port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
      +local user name, in Rlogin: Section 4.30.1
      +local user name, in Windows: Section 4.30.1
      local Windows command: Section 6.2.19
      locking up, SSH-2 sessions: Question A.7.20
      -Log, Event: Section 3.1.3.1, Section 4.2
      -log file: Section 3.2, Section 3.8.3.23, Section 4.2
      -log file, flushing: Section 4.2.3
      -log file, header: Section 4.2.4
      -logging in: Section 2.3
      -logging out: Section 2.5
      -logging, proxy: Section 4.16.6
      --loghost: Section 3.8.3.19
      -logical host name: Section 3.8.3.19, Section 4.14.5
      -logical palettes: Section 4.13.5
      -login name: Section 2.3, Section 3.8.3.4, Section 5.2.1.1
      -login name, for auto-login: Section 4.15.1
      -login name, for proxy: Section 4.16.4
      -login name, local, in Rlogin: Section 4.18.1
      -login name, local, in Windows: Section 4.18.1
      -login names, different: Section 2.3, Section 4.23.7
      -login, passwordless: Section 1.2, Section 4.18.1, Section 4.24, Section 8.2.7, Chapter 9
      +-logappend: Section 3.11.3.25
      +Log, Event: Section 3.1.3.1, Section 4.2
      +log file: Section 3.2, Section 3.11.3.24, Section 4.2
      +log file, flushing: Section 4.2.3
      +log file, header: Section 4.2.4
      +logging in: Section 2.3
      +logging out: Section 2.5
      +logging, proxy: Section 4.16.6
      +-loghost: Section 3.11.3.20
      +logical host name: Section 3.11.3.20, Section 4.14.5
      +logical palettes: Section 4.13.5
      +login name: Section 2.3, Section 3.11.3.4, Section 5.2.1.1
      +login name, for auto-login: Section 4.15.1
      +login name, for proxy: Section 4.16.4
      +login name, local, in Rlogin: Section 4.30.1
      +login name, local, in Windows: Section 4.30.1
      +login names, different: Section 2.3, Section 4.21.8
      +login, passwordless: Section 1.2, Section 4.22, Section 4.30.1, Section 8.2.8, Chapter 9
      login scripts: Section 10.6, Question A.7.1, Question A.7.4
      -logo, PuTTY's: Section 2.3, Section A.5.3
      -loopback IP address: Section 3.5, Section 4.16.2
      -low-numbered port: Section 3.5, Section 4.18.1, Section 4.27
      +logo, PuTTY's: Section 2.3, Section A.5.3
      +-logoverwrite: Section 3.11.3.25
      +loopback IP address: Section 3.5, Section 4.16.2
      +low-numbered port: Section 3.5, Section 4.25, Section 4.30.1
      -ls PSCP command-line option: Section 5.2.2.1
      -MAC (message authentication code): Section 4.22, Section 4.28.8, Section 10.12, Section 10.12
      +MAC (message authentication code): Section 4.20, Section 4.26.8, Section 10.12, Section 10.12
      Mac OS: Question A.3.6
      -magic cookie: Section 4.26.1
      +magic cookie: Section 4.24.1
      mailing list: Section B.1
      man pages for PuTTY tools: PuTTY User Manual
      -manually configuring host keys: Section 3.8.3.20, Section 4.21.2
      -maximise window: Section 4.7.2, Question A.6.3
      -maximum packet size: Section 4.28.5
      -mc: Section 4.6.2
      --m command-line option: Section 3.8.3.6, Section 3.8.3.6
      -menu, context: Section 3.1.1, Section 3.1.3, Section 4.11.1
      -menu, system: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
      -message authentication code (MAC): Section 4.22, Section 4.28.8, Section 10.12, Section 10.12
      -middle mouse button: Section 3.1.1, Section 4.11.1, Section 4.11.4
      -Midnight Commander: Section 4.6.2
      -minimise window: Section 4.9.1
      -mistyping a password: Section 2.3
      -MIT-MAGIC-COOKIE-1: Section 4.26.1
      +manually configuring host keys: Section 3.11.3.21, Section 4.19.3
      +maximise window: Section 4.7.2, Question A.6.3
      +maximum packet size: Section 4.26.5
      +mc: Section 4.6.2
      +-m command-line option: Section 3.11.3.6, Section 3.11.3.6
      +MD5 fingerprint, of SSH host key: Section 2.2, Section 4.19.3
      +menu, context: Section 3.1.1, Section 3.1.3, Section 4.11.1
      +menu, system: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
      +message authentication code (MAC): Section 4.20, Section 4.26.8, Section 10.12, Section 10.12
      +middle mouse button: Section 3.1.1, Section 4.11.1, Section 4.11.4
      +Midnight Commander: Section 4.6.2
      +minimise window: Section 4.9.1
      +mistyping a password: Section 2.3
      +MIT-MAGIC-COOKIE-1: Section 4.24.1
      modes of files, changing: Section 6.2.14
      mouse: Section 3.1.1
      -mouse pointer: Section 3.1.1, Section 4.8.3
      -mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2
      -mouse, three-button: Section 3.1.1, Section 4.11.1
      +mouse pointer: Section 3.1.1, Section 4.8.3
      +mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2
      +mouse, three-button: Section 3.1.1, Section 4.11.1
      moving files: Section 6.2.18
      -MS-DOS Prompt: Section 1.1, Section 1.1, Section 3.8, Section 5.1, Section 7.1
      -MUDs: Section 1.1, Section 2.1, Section 4.3.9
      -multi-user systems: Section 3.8.2, Question A.8.2
      -Nagle's algorithm: Section 4.14.2
      -name resolution: Section 4.16.3
      -name resolution, with proxy: Section 4.16.2, Section 4.16.3
      +MS-DOS Prompt: Section 1.1, Section 1.1, Section 3.11, Section 5.1, Section 7.1
      +MUDs: Section 1.1, Section 2.1, Section 4.3.9
      +multi-user systems: Section 3.11.2, Question A.8.2
      +Nagle's algorithm: Section 4.14.2
      +name resolution: Section 4.16.3
      +name resolution, with proxy: Section 4.16.2, Section 4.16.3
      NAT routers: Section 10.16, Question A.7.8
      -NaturallySpeaking: Section 4.9.4
      --nc: Section 3.8.3.14
      --N command-line option: Section 3.8.3.13
      -negotiation, of Telnet options: Section 4.17.2
      -NetHack keypad mode: Section 4.4.6
      +NaturallySpeaking: Section 4.9.4
      +-nc: Section 3.11.3.14
      +-N command-line option: Section 3.11.3.13
      +negotiation, of Telnet options: Section 4.29.2
      +NetHack keypad mode: Section 4.4.6
      Network Address Translation: Section 10.16, Question A.7.8
      -network connection: Section 3.5, Section 4.14, Section 4.27
      +network connection: Section 3.5, Section 4.14, Section 4.25
      network protocols: Section 1.1
      -NEW_ENVIRON: Section 4.17.1
      -new line: Section 4.3.3, Section 4.3.4, Section 4.17.4
      -new line, in Telnet: Section 4.17.4
      +NEW_ENVIRON: Section 4.29.1
      +new line: Section 4.3.3, Section 4.3.4, Section 4.29.4
      +new line, in Telnet: Section 4.29.4
      ‘New Session’: Section 3.1.3.3, Question A.7.22
      -new version, verifying: Section 3.8.3.21, Appendix E
      -NNTP: Section 3.6
      --noagent: Section 3.8.3.9
      +new version, verifying: Section 3.11.3.22, Appendix F
      +NNTP: Section 3.7
      +-noagent: Section 3.11.3.9
      -no-antispoof: Section 7.2.3.6
      -non-destructive backspace: Section 4.6.8
      +non-destructive backspace: Section 4.6.8
      No Operation, Telnet special command: Section 3.1.3.2
      No-op, in SSH: Section 3.1.3.2
      -no-sanitise-stderr: Section 5.2.2.7, Section 6.1.4.1, Section 7.2.3.5
      -no-sanitise-stdout: Section 7.2.3.5
      -numeric keypad: Section 4.4.3
      -numeric keypad, ‘Application’ mode: Section 4.4.5, Section 4.6.1
      -Num Lock: Section 4.4.5, Section 4.4.6
      -OLD_ENVIRON: Section 4.17.1
      -one-time passwords: Section 4.23.4
      -OpenSSH: Section 3.5, Section 4.23.7, Section 4.27, Section 4.27.1, Section 4.28.6, Section 4.28.9, Section 8.2.10, Section 8.3, Section 9.4, Section 10.5, Question A.7.18, Question A.10.1
      -OpenSSH private key file format: Section 8.2.12
      -option negotiation, Telnet: Section 4.17.2
      -options, command-line: Section 3.8, Section 9.3
      +notification area, Windows (aka system tray): Section 3.11.3.27, Section 9.1
      +-no-trivial-auth: Section 3.11.3.19
      +numeric keypad: Section 4.4.3
      +numeric keypad, ‘Application’ mode: Section 4.4.5, Section 4.6.1
      +Num Lock: Section 4.4.5, Section 4.4.6
      +OLD_ENVIRON: Section 4.29.1
      +one-time passwords: Section 4.21.5
      +OpenSSH: Section 3.5, Section 4.21.8, Section 4.25, Section 4.25.1, Section 4.26.6, Section 4.26.9, Section 8.2.11, Section 8.3, Section 9.4, Section 10.5, Question A.7.18, Question A.10.1
      +OpenSSH private key file format: Section 8.2.14
      +option negotiation, Telnet: Section 4.29.2
      +options, command-line: Section 3.11, Section 9.3
      out of memory: Section 10.6, Question A.7.3, Question A.7.4
      -overriding host keys: Section 3.8.3.20, Section 4.21.2
      -packet log, SSH: Section 4.2, Section 4.2.5
      -Pageant: Section 3.8.3.9, Section 3.8.3.10, Section 4.23.6, Section 4.23.8, Section 4.28.13, Section 8.1, Chapter 9
      -palettes, logical: Section 4.13.5
      -passive Telnet negotiation: Section 4.17.2
      -passphrase: Section 8.1, Section 8.2.7, Chapter 9
      -passthrough printing: Section 4.3.10
      -password: Section 2.3, Section 3.8.3.8
      -password camouflage: Section 4.28.11, Section 4.28.12
      -password expiry: Section 4.23.5
      -password, for proxy: Section 4.16.4
      -passwordless login: Section 1.2, Section 4.18.1, Section 4.24, Section 8.2.7, Chapter 9
      -password, mistyping: Section 2.3
      -password, one-time: Section 4.23.4
      -password, plain text: Section 4.16.4, Section 4.16.4
      +overriding host keys: Section 3.11.3.21, Section 4.19.3
      +packet log, SSH: Section 4.2, Section 4.2.5
      +Pageant: Section 3.11.3.9, Section 3.11.3.10, Section 4.21.7, Section 4.21.9, Section 4.26.13, Section 8.1, Chapter 9
      +palettes, logical: Section 4.13.5
      +passive Telnet negotiation: Section 4.29.2
      +passphrase: Section 8.1, Section 8.2.8, Chapter 9
      +passphrase hashing, for private key files: Section 8.2.12.2
      +passthrough printing: Section 4.3.10
      +password: Section 2.3, Section 3.11.3.8
      +password camouflage: Section 4.26.11, Section 4.26.12
      +password expiry: Section 4.21.6
      +password, for proxy: Section 4.16.4
      +password hashing, for private key files: Section 8.2.12.2
      +passwordless login: Section 1.2, Section 4.22, Section 4.30.1, Section 8.2.8, Chapter 9
      +password, mistyping: Section 2.3
      +password, one-time: Section 4.21.5
      +password, plain text: Section 4.16.4, Section 4.16.4
      password prompt: Question A.7.12
      password, storing: Question A.2.8
      -paste, copy and: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
      +paste, copy and: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
      patch: Section B.2
      PATH environment variable: Section 5.1, Section 6.1, Section 7.1
      --P command-line option: Section 3.8.3.7
      -PC speaker: Section 4.5.1
      +-P command-line option: Section 3.11.3.7
      +PC speaker: Section 4.5.1
      Pentium 4: Question A.3.1
      permissions on files, changing: Section 6.2.14
      --pgpfp command-line option: Section 3.8.3.21, Appendix E
      -PGP key fingerprint: Section 3.8.3.21
      -PGP signatures, of PuTTY binaries: Appendix E
      -plain text password: Section 4.16.4, Section 4.16.4
      +-pgpfp command-line option: Section 3.11.3.22, Appendix F
      +PGP key fingerprint: Section 3.11.3.22
      +PGP signatures, of PuTTY binaries: Appendix F
      +plain text password: Section 4.16.4, Section 4.16.4
      Plink: Chapter 7, Chapter 7
      PLINK_PROTOCOL environment variable: Section 7.2.2
      POP-3: Section 3.5
      -port forwarding in SSH: Section 3.5, Section 3.8.3.5, Section 4.16, Section 4.19.2, Section 4.27
      -port forwarding in SSH, changing mid-session: Section 4.27
      -port number: Section 3.5, Section 3.8.3.7, Section 4.1.1, Section 4.27
      -port, privileged: Section 3.5, Section 4.18.1, Section 4.27
      -POSIX: Section 4.25.2, Section 6.2.2
      -PPK file: Section 3.8.3.18, Section 4.23.8, Section 8.2.8, Section 8.2.12, Section 9.1
      +port forwarding in SSH: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
      +port forwarding in SSH, changing mid-session: Section 4.25
      +port number: Section 3.5, Section 3.11.3.7, Section 4.1.1, Section 4.25
      +port, privileged: Section 3.5, Section 4.25, Section 4.30.1
      +POSIX: Section 4.23.2, Section 6.2.2
      +PPK file: Section 3.11.3.18, Section 4.21.9, Section 8.2.9, Section 8.2.14, Section 9.1
      -p PSCP command-line option: Section 5.2.2.2
      preserve file attributes: Section 5.2.2.2
      -PRIMARY selection: Section 4.11.4
      -printing, remote-controlled: Section 4.3.10
      -private key: Section 3.8.3.18, Section 4.23.8, Section 8.1, Chapter 9
      -private key file, OpenSSH: Section 8.2.12
      -private key file, PuTTY: Section 3.8.3.18, Section 4.23.8, Section 8.2.8, Section 8.2.12, Section 9.1
      -private key file, ssh.com: Section 8.2.12
      +PRIMARY selection: Section 4.11.4
      +prime generation method: Section 8.2.4
      +primes, probable: Section 8.2.4
      +primes, proven: Section 8.2.4
      +printing, remote-controlled: Section 4.3.10
      +private key: Section 3.11.3.18, Section 4.21.9, Section 8.1, Chapter 9
      +private key file, OpenSSH: Section 8.2.14
      +private key file, PuTTY: Section 3.11.3.18, Section 4.21.9, Section 8.2.9, Section 8.2.14, Section 9.1
      +private key file, ssh.com: Section 8.2.14
      private keys, generating: Section 8.2
      -privileged port: Section 3.5, Section 4.18.1, Section 4.27
      -process ACL (Windows): Section 3.8.3.25, Section 9.3.3
      -prompt: Section 2.4
      +privileged port: Section 3.5, Section 4.25, Section 4.30.1
      +probable primes: Section 8.2.4
      +process ACL (Windows): Section 3.11.3.27, Section 9.3.4
      +prompt: Section 2.4
      protocol: Section 2.1
      -protocols, debugging: Section 3.6
      +protocols, debugging: Section 3.7
      protocols, differences between: Section 1.2
      -protocol selection: Section 3.8.3.2
      -protocol version, SSH: Section 3.8.3.16, Section 4.19.4
      -proxy authentication: Section 4.16.4
      --proxycmd: Section 3.8.3.24
      -proxy command: Section 3.8.3.24, Section 4.16.1, Section 4.16.5
      -proxy DNS: Section 4.16.2, Section 4.16.3
      -proxy, HTTP: Section 4.16.1
      -proxy logging: Section 4.16.6
      -proxy password: Section 4.16.4
      -proxy server: Section 4.14.4, Section 4.16
      -proxy, SOCKS: Section 4.16.1
      -proxy, Telnet: Section 4.16.1, Section 4.16.5, Section 4.17.2
      -proxy user name: Section 4.16.4
      +protocol selection: Section 3.11.3.2
      +protocol version, SSH: Section 3.11.3.16, Section 4.17.4
      +proven primes: Section 8.2.4
      +proxy authentication: Section 4.16.4
      +-proxycmd: Section 3.11.3.26
      +proxy command: Section 3.11.3.26, Section 4.16.1, Section 4.16.5
      +proxy DNS: Section 4.16.2, Section 4.16.3
      +proxy, HTTP: Section 4.16.1
      +proxy logging: Section 4.16.6
      +proxy password: Section 4.16.4
      +proxy server: Section 4.14.4, Section 4.16
      +proxy, SOCKS: Section 4.16.1
      +proxy, Telnet: Section 4.16.1, Section 4.16.5, Section 4.29.2
      +proxy user name: Section 4.16.4
      PSCP: Chapter 5, Chapter 5
      -pseudo-terminal allocation: Section 3.8.3.12, Section 4.25.1
      +pseudo-terminal allocation: Section 3.11.3.12, Section 4.23.1
      PSFTP: Chapter 6, Chapter 6
      +‘psusan’ program: Section 4.27
      pterm: PuTTY User Manual, Question A.3.2
      -pty allocation: Section 3.8.3.12, Section 4.25.1
      +pty allocation: Section 3.11.3.12, Section 4.23.1
      public key: Section 8.1
      public-key algorithm: Section 8.1
      -public key authentication: Section 3.8.3.18, Section 4.23.8, Section 4.28.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      -public key file, SSH-2: Section 8.2.9
      -public key fingerprint (SSH): Section 8.2.5, Section 9.2.1
      +public key authentication: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      +public key file, SSH-2: Section 8.2.10
      +public key fingerprint (SSH): Section 8.2.6, Section 9.2.1
      public keys, generating: Section 8.2
      -punctuation: Section 4.12.1
      -PuTTY Event Log: Section 3.1.3.1, Section 4.2
      +punctuation: Section 4.12.1
      +PuTTY Event Log: Section 3.1.3.1, Section 4.2
      PuTTYgen: Section 8.2
      -PuTTY icon: Section 2.3, Section A.5.3
      -putty.rnd (random seed file): Section 3.8.2, Question A.5.2
      -putty @sessionname: Section 3.8.3.1
      -PuTTYtel: Section 4.16.4
      -PuTTY terminal window: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
      --pw command-line option: Section 3.8.3.8
      +PuTTY icon: Section 2.3, Section A.5.3
      +putty.rnd (random seed file): Section 3.11.2, Question A.5.2
      +putty @sessionname: Section 3.11.3.1
      +PuTTYtel: Section 4.16.4
      +PuTTY terminal window: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
      +-pw command-line option: Section 3.11.3.8
      -q PSCP command-line option: Section 5.2.2.3
      -QUIT, special character: Section 4.25.2
      +QUIT, special character: Section 4.23.2
      quoting, in PSFTP: Section 6.2.1
      -random seed file: Section 3.8.2
      --raw command-line option: Section 3.8.1, Section 3.8.3.2
      -raw protocol: Section 2.1
      -‘Raw’ protocol: Section 3.6
      -raw TCP connections: Section 3.6, Section 4.1.1
      -RC4: Section 4.22, Section 10.4
      --R command-line option: Section 3.8.3.5
      -reading commands from a file: Section 3.8.3.6
      +random seed file: Section 3.11.2
      +-raw command-line option: Section 3.11.1, Section 3.11.3.2
      +raw protocol: Section 2.1
      +‘Raw’ protocol: Section 3.7
      +raw TCP connections: Section 3.7
      +RC4: Section 4.20, Section 10.4
      +-R command-line option: Section 3.11.3.5
      +reading commands from a file: Section 3.11.3.6
      read permission: Section 6.2.14
      receiving files: Section 5.2.1, Section 6.2.9
      -rectangular selection: Section 3.1.1, Section 4.11.3
      +rectangular selection: Section 3.1.1, Section 4.11.3
      recursive: Section 5.2.2.4, Section 6.2.9, Section 6.2.10
      Red Hat Linux: Question A.7.15
      -registry entries, removing: Section 3.8.2
      -Registry (Windows): Section 2.2, Section 4.1.2, Section 4.30, Question A.5.2
      -remote commands: Section 2.4, Section 3.8.3.6, Section 4.19.1, Section 4.19.2, Question A.6.2
      -remote-controlled printing: Section 4.3.10
      -remote echo: Section 4.3.8
      -remote network connection: Section 3.8.3.14
      -remote shell: Section 4.19.2
      -remote shell, suppressing: Section 3.8.3.13
      -remote-to-local port forwarding: Section 3.5, Section 3.8.3.5, Section 4.27
      +Re-encrypt: Section 9.5
      +re-encryptable: Section 9.5
      +registry entries, removing: Section 3.11.2
      +Registry (Windows): Section 2.2, Section 4.1.2, Section 4.32, Question A.5.2
      +remote commands: Section 2.4, Section 3.11.3.6, Section 4.17.1, Section 4.17.2, Question A.6.2
      +remote-controlled printing: Section 4.3.10
      +remote echo: Section 4.3.8
      +remote network connection: Section 3.11.3.14
      +remote shell: Section 4.17.2
      +remote shell, suppressing: Section 3.11.3.13
      +remote-to-local port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
      removing directories: Section 6.2.17
      removing files: Section 6.2.15
      -removing registry entries: Section 3.8.2
      +removing registry entries: Section 3.11.2
      renaming files: Section 6.2.18
      -repeat key exchange: Section 3.1.3.2, Section 4.20.2, Section 4.28.2, Question A.7.20
      +repeat key exchange: Section 3.1.3.2, Section 4.18.2, Section 4.26.2, Question A.7.20
      Repeat key exchange, SSH special command: Section 3.1.3.2
      ‘Reset Terminal’: Section 3.1.3.6
      -resizing, terminal: Section 4.6.3, Section 4.7.1, Section 4.7.2
      +resizing, terminal: Section 4.6.3, Section 4.7.1, Section 4.7.2
      ‘Restart Session’: Section 3.1.3.3
      --restrict-acl: Section 3.8.3.25, Section 9.3.3
      --restrict-putty-acl: Section 3.8.3.25
      +-restrict-acl: Section 3.11.3.27, Section 9.3.4
      +-restrict-putty-acl: Section 3.11.3.27
      resuming file transfers: Section 6.2.12
      return value: Section 5.2.3
      -RFC: Section 4.17.1
      -RGB values: Section 4.13.7
      -.rhosts file: Section 4.18.1
      -‘rhosts’ file: Section 4.18.1
      -Rich Text Format: Section 4.12.2
      -right mouse button: Section 3.1.1, Section 4.11.1
      -right mouse button menu: Section 3.1.1, Section 3.1.3, Section 4.11.1
      +RFC: Section 4.29.1
      +RGB values: Section 4.13.7
      +.rhosts file: Section 4.30.1
      +‘rhosts’ file: Section 4.30.1
      +Rich Text Format: Section 4.12.2
      +right mouse button: Section 3.1.1, Section 4.11.1
      +right mouse button menu: Section 3.1.1, Section 3.1.3, Section 4.11.1
      right mouse button, with Ctrl: Section 3.1.1
      -right-to-left text: Section 4.6.11
      -Rijndael: Section 4.22
      -Rlogin: Section 2.1, Section 4.1.1, Section 4.18
      --rlogin command-line option: Section 3.8.1, Section 3.8.3.2
      -Rlogin, differences from SSH and Telnet: Section 1.2
      -routers: Section 4.14.1
      +right-to-left text: Section 4.6.11
      +Rijndael: Section 4.20
      +Rlogin: Section 2.1, Section 3.9, Section 4.30
      +-rlogin command-line option: Section 3.11.1, Section 3.11.3.2
      +Rlogin, differences from other protocols: Section 1.2
      +routers: Section 4.14.1
      routers, NAT: Section 10.16, Question A.7.8
      -rows, in terminal window: Section 4.7.1
      +rows, in terminal window: Section 4.7.1
      -r PSCP command-line option: Section 5.2.2.4
      -RSA: Section 4.21.1, Section 4.28.6, Section 4.28.13, Section 8.1, Section 8.2.2
      -RSA authentication: Section 3.8.3.18, Section 4.23.8, Section 4.28.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      -RSA key exchange: Section 4.20.1
      -RTF: Section 4.12.2
      -Russian: Section 4.10.3
      -rxvt: Section 4.4.2
      +RSA: Section 4.19.1, Section 4.26.6, Section 4.26.13, Section 8.1, Section 8.2.2
      +RSA authentication: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
      +RSA key exchange: Section 4.18.1
      +RTF: Section 4.12.2
      +Russian: Section 4.10.3
      +rxvt: Section 4.4.2
      -sanitise-stderr: Section 5.2.2.7, Section 6.1.4.1, Section 7.2.3.5
      -sanitise-stdout: Section 7.2.3.5
      -saved sessions, loading from command line: Section 3.8.3.1
      +saved sessions, loading from command line: Section 3.11.3.1
      ‘Saved Sessions’ submenu: Section 3.1.3.3
      -saving private keys: Section 8.2.12
      -saving settings: Section 4.1.2
      -saving settings in a file: Section 4.30
      -SCO: Section 4.4.3
      +saving private keys: Section 8.2.14
      +saving settings: Section 4.1.2
      +saving settings in a file: Section 4.32
      +SCO: Section 4.4.3
      SCP protocol: Section 5.2.1, Section 5.2.2.6
      -scp PSCP command-line option: Section 5.2.2.6
      -screen, clearing: Section 4.3.5
      +screen, clearing: Section 4.3.5
      scripts: Section 7.3
      -scrollback: Section 3.1.2, Section 4.7.3
      -scrollback clearing: Section 4.6.7
      +scrollback: Section 3.1.2, Section 4.7.3
      +scrollback clearing: Section 4.6.7
      scrollback, clearing: Section 3.1.3.6
      -scrollbar: Section 3.1.2, Section 4.7.3
      -scrolling region: Section 4.3.2
      -SECONDARY selection: Section 4.11.4.2
      -secret, shared: Section 4.20
      +scrollbar: Section 3.1.2, Section 4.7.3
      +scrolling region: Section 4.3.2
      +SECONDARY selection: Section 4.11.4.2
      +secret, shared: Section 4.18
      secure shell: Section 1.2
      -security hazard: Section 4.6.6, Section 4.16.4, Section 5.2.1, Section 9.5
      -security token: Section 4.23.4
      -selecting a protocol: Section 1.2, Section 3.8.3.2
      -selecting text: Section 3.1.1, Section 4.11.1
      +security hazard: Section 4.6.6, Section 4.16.4, Section 5.2.1, Section 9.6
      +security token: Section 4.21.5
      +selecting a protocol: Section 1.2, Section 3.11.3.2
      +selecting text: Section 3.1.1, Section 4.11.1
      selecting whole lines: Section 3.1.1
      -selecting whole words: Section 3.1.1, Section 4.12.1
      -selection, adjusting: Section 3.1.1, Section 4.11.1
      -selection, CLIPBOARD: Section 4.11.4
      -selection, PRIMARY: Section 4.11.4
      -selection, rectangular: Section 3.1.1, Section 4.11.3
      -selection, SECONDARY: Section 4.11.4.2
      -selections, multiple: Section 4.11.4
      +selecting whole words: Section 3.1.1, Section 4.12.1
      +selection, adjusting: Section 3.1.1, Section 4.11.1
      +selection, CLIPBOARD: Section 4.11.4
      +selection, PRIMARY: Section 4.11.4
      +selection, rectangular: Section 3.1.1, Section 4.11.3
      +selection, SECONDARY: Section 4.11.4.2
      +selections, multiple: Section 4.11.4
      sending files: Section 5.2.1, Section 6.2.10
      --sercfg command-line option: Section 3.8.3.22
      -Serial: Section 4.29
      --serial command-line option: Section 3.8.1, Section 3.8.3.2
      -serial line: Section 4.1.1, Section 4.29
      -serial port: Section 4.29
      +-sercfg command-line option: Section 3.11.3.23
      +Serial: Section 4.28
      +-serial command-line option: Section 3.11.1, Section 3.11.3.2
      +serial line: Section 4.1.1, Section 4.28
      +serial port: Section 4.28
      server: Section 1.1
      -server, commands on: Section 2.4, Section 3.8.3.6, Section 4.19.1, Section 4.19.2, Question A.6.2
      -server, HTTP: Section 4.16.1
      -server name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
      -server name resolution: Section 4.16.3
      -server name resolution, with proxy: Section 4.16.2, Section 4.16.3
      -server, proxy: Section 4.14.4, Section 4.16
      -server, SOCKS: Section 4.16.1
      -service names: Section 4.27
      -session ID: Section 4.28.9
      +server, commands on: Section 2.4, Section 3.11.3.6, Section 4.17.1, Section 4.17.2, Question A.6.2
      +server, HTTP: Section 4.16.1
      +server name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
      +server name resolution: Section 4.16.3
      +server name resolution, with proxy: Section 4.16.2, Section 4.16.3
      +server, proxy: Section 4.14.4, Section 4.16
      +server, SOCKS: Section 4.16.1
      +service names: Section 4.25
      +session ID: Section 4.26.9
      session log: Section 3.2
      --sessionlog: Section 3.8.3.23
      -@sessionname command-line argument: Section 3.8.3.1
      -sessions, loading and storing: Section 4.1.2
      +-sessionlog: Section 3.11.3.24
      +@sessionname command-line argument: Section 3.11.3.1
      +sessions, loading and storing: Section 4.1.2
      session, starting: Section 2.1
      set-group-ID bit: Section 6.2.14
      settings, changing: Section 3.1.3.4
      -settings, default: Section 3.8.1, Section 4.1.2
      -settings, loading and storing: Section 4.1.2
      +settings, default: Section 3.11.1, Section 4.1.2
      +settings, loading and storing: Section 4.1.2
      set-user-ID bit: Section 6.2.14
      SFTP: Section 5.2.1, Section 5.2.2.6, Chapter 6
      -sftp PSCP command-line option: Section 5.2.2.6
      -shaping, of Arabic text: Section 4.6.10
      -shared secret: Section 4.20
      +SHA-256 fingerprint, of SSH host key: Section 2.2, Section 4.19.3
      +shaping, of Arabic text: Section 4.6.10
      +shared secret: Section 4.18
      -shareexists Plink command-line option: Section 7.2.3.4
      -share-plink: Section 7.2.3.3
      shell account: Section 1.1
      -shell, remote: Section 4.19.2
      -shell, remote, suppressing: Section 3.8.3.13
      -Shift-Backspace: Section 4.4.1
      -Shift-Ins: Section 3.1.1, Section 4.11.4.2
      +shell, remote: Section 4.17.2
      +shell, remote, suppressing: Section 3.11.3.13
      +Shift-Backspace: Section 4.4.1
      +Shift-Ins: Section 3.1.1, Section 4.11.4.2
      Shift-PgDn: Section 3.1.2
      Shift-PgUp: Section 3.1.2
      -shortcut, Windows: Section 3.8, Section 3.8.3.1, Section 9.3, Question A.6.4
      +shortcut, Windows: Section 3.11, Section 3.11.3.1, Section 9.3, Question A.6.4
      Signal, SSH special command: Section 3.1.3.2
      -signature: Section 4.28.6, Section 8.1
      -signatures, of PuTTY binaries: Appendix E
      -single-DES: Section 4.22, Section 10.4
      -single sign-on: Section 4.15.2, Section 4.24
      -single-width character: Section 4.10.2
      -size, of font: Section 4.7.2, Section 4.8.2
      -size, of window: Section 4.7.1
      -S/Key: Section 4.23.4, Section 4.23.5
      +signature: Section 4.26.6, Section 8.1
      +signatures, of PuTTY binaries: Appendix F
      +single-DES: Section 4.20, Section 10.4
      +single sign-on: Section 4.15.2, Section 4.22
      +single-width character: Section 4.10.2
      +size, of font: Section 4.7.2, Section 4.8.2
      +size, of window: Section 4.7.1
      +S/Key: Section 4.21.5, Section 4.21.6
      SMB: Question A.7.17
      -SMTP: Section 3.6
      -SOCKS port forwarding: Section 3.5, Section 3.8.3.5, Section 4.27
      -SOCKS proxy: Section 4.16.1
      -SO_KEEPALIVE: Section 4.14.3
      -sound file: Section 4.5.1
      +SMTP: Section 3.7
      +SOCKS port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
      +SOCKS proxy: Section 4.16.1
      +SO_KEEPALIVE: Section 4.14.3
      +sound file: Section 4.5.1
      spaces in filenames: Section 6.2.1, Question A.6.9
      -special character: Section 4.25.2
      +special character: Section 4.23.2
      special commands: Section 3.1.3.2
      special commands, in SSH: Section 3.1.3.2
      -special commands, in Telnet: Section 3.1.3.2, Section 4.17.3
      -speed, terminal: Section 4.15.4
      +special commands, in Telnet: Section 3.1.3.2, Section 4.29.3
      +speed, terminal: Section 4.15.4
      -s Plink command-line option: Section 7.2.3.2
      -spoofing: Section 2.2, Section 4.18.1, Section 8.1
      -SSH: Section 2.1, Section 2.2, Section 4.1.1, Section 4.19
      -SSH-1: Section 3.8.3.16, Section 4.19.4
      -SSH-2: Section 3.8.3.16, Section 4.15.5, Section 4.19.2, Section 4.19.4, Section 10.1, Section 10.3
      -ssh-add: Section 9.4
      -SSH agent forwarding: Section 3.8.3.10, Section 4.23.6, Section 9.4
      -ssh.com: Section 4.28.10, Section 8.2.9, Section 8.3, Section 9.4
      --ssh command-line option: Section 3.8.1, Section 3.8.3.2
      -ssh.com private key file format: Section 8.2.12
      -SSH, differences from Telnet and Rlogin: Section 1.2
      -.ssh directory: Section 8.3
      -.ssh2 directory: Section 8.3
      +spoofing: Section 2.2, Section 4.30.1, Section 8.1
      +SSH: Section 2.1, Section 2.2, Section 4.1.1, Section 4.17
      +SSH-1: Section 3.11.3.16, Section 4.17.4
      +SSH-2: Section 3.11.3.16, Section 4.15.5, Section 4.17.2, Section 4.17.4, Section 10.1, Section 10.3
      +ssh-add: Section 9.4
      +SSH agent forwarding: Section 3.11.3.10, Section 4.21.7, Section 9.4
      +ssh.com: Section 4.26.10, Section 8.2.10, Section 8.3, Section 9.4
      +-ssh command-line option: Section 3.11.1, Section 3.11.3.2
      +ssh.com private key file format: Section 8.2.14
      +-ssh-connection command-line option: Section 3.11.1, Section 3.11.3.2
      +ssh-connection protocol, bare: Section 4.27
      +SSH, differences from other protocols: Section 1.2
      +.ssh directory: Section 8.3
      +.ssh2 directory: Section 8.3
      SSH file transfer protocol: Section 5.2.1, Section 5.2.2.6, Chapter 6
      -SSH host key fingerprint: Section 2.2
      -SSH ‘ignore’ messages: Section 3.1.3.2, Section 4.28.1, Section 4.28.11
      +SSH host key fingerprint: Section 2.2
      +SSH ‘ignore’ messages: Section 3.1.3.2, Section 4.26.1, Section 4.26.11
      SSH key exchange, forcing repeat: Section 3.1.3.2
      ssh-keygen: Section 9.2.1
      --sshlog: Section 3.8.3.23
      -SSH packet log: Section 4.2, Section 4.2.5
      -SSH port forwarding: Section 3.5, Section 3.8.3.5, Section 4.16, Section 4.19.2, Section 4.27
      -SSH port forwarding, changing mid-session: Section 4.27
      -SSH protocol version: Section 3.8.3.16, Section 4.19.4
      -SSH-2 public key file format: Section 8.2.9
      -SSH public key fingerprint: Section 8.2.5, Section 9.2.1
      --sshrawlog: Section 3.8.3.23
      -SSH server bugs: Section 4.28
      +-sshlog: Section 3.11.3.24
      +SSH packet log: Section 4.2, Section 4.2.5
      +SSH port forwarding: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
      +SSH port forwarding, changing mid-session: Section 4.25
      +SSH protocol version: Section 3.11.3.16, Section 4.17.4
      +SSH-2 public key file format: Section 8.2.10
      +SSH public key fingerprint: Section 8.2.6, Section 9.2.1
      +-sshrawlog: Section 3.11.3.24
      +SSH server bugs: Section 4.26
      SSH special commands: Section 3.1.3.2
      SSH subsystem: Section 7.2.3.2
      -SSH tunnelling: Section 3.5, Section 3.8.3.5, Section 4.16, Section 4.19.2, Section 4.27
      -SSH tunnelling, changing mid-session: Section 4.27
      -SSH X11 forwarding: Section 3.4, Section 3.8.3.11, Section 4.26
      -SSPI: Section 4.24.2
      -stair-stepping: Section 4.3.3
      -standard error, proxy: Section 4.16.6
      +SSH tunnelling: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
      +SSH tunnelling, changing mid-session: Section 4.25
      +SSH X11 forwarding: Section 3.4, Section 3.11.3.11, Section 4.24
      +SSPI: Section 4.22.2
      +stair-stepping: Section 4.3.3
      +standard error, proxy: Section 4.16.6
      starting a session: Section 2.1
      Start Menu: Section 5.1
      startup scripts: Section 10.6, Question A.7.1, Question A.7.4
      statistics: Section 5.2.2.3
      sticky bit: Section 6.2.14
      storing passwords: Question A.2.8
      -storing settings: Section 4.1.2
      -storing settings in a file: Section 4.30
      -stty: Section 4.4.1, Section 4.25.2
      +storing settings: Section 4.1.2
      +storing settings in a file: Section 4.32
      +‘strong’ primes: Section 8.2.4
      +stty: Section 4.4.1, Section 4.23.2
      subsystem, SSH: Section 7.2.3.2
      Sun SSH: Section 10.5
      +SUPDUP: Section 2.1, Section 3.10, Section 4.31
      +-supdup command-line option: Section 3.11.1, Section 3.11.3.2
      +SUPDUP, differences from other protocols: Section 1.2
      supported features: Section A.2
      support requests: Section B.6
      -Suspend Process, Telnet special command: Section 3.1.3.2, Section 4.17.3
      -swap file: Section 9.5
      -switches, command-line: Section 3.8, Section 9.3
      -symmetric-key algorithm: Section 4.22
      +Suspend Process, Telnet special command: Section 3.1.3.2, Section 4.29.3
      +swap file: Section 9.6
      +switches, command-line: Section 3.11, Section 9.3
      +symmetric-key algorithm: Section 4.20
      Synch, Telnet special command: Section 3.1.3.2
      -system colours: Section 4.13.6
      +system colours: Section 4.13.6
      SYSTEM32 directory, on Windows: Question A.7.22
      -system menu: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
      -system tray: Section 9.1
      -talker systems: Section 1.1, Section 4.3.9
      -taskbar: Section 4.5.2
      --T command-line option: Section 3.8.3.12
      --t command-line option: Section 3.8.3.12
      -TCP connections, raw: Section 3.6, Section 4.1.1
      -TCP keepalives: Section 4.14.3
      -TCP_NODELAY: Section 4.14.2
      -TCP proxy: Section 4.16.1, Section 4.16.5, Section 4.17.2
      -Telnet: Section 2.1, Section 4.1.1, Section 4.17
      --telnet command-line option: Section 3.8.1, Section 3.8.3.2
      -Telnet, differences from SSH and Rlogin: Section 1.2
      -Telnet New Line: Section 4.17.4
      -Telnet option negotiation: Section 4.17.2
      -Telnet proxy: Section 4.16.1, Section 4.16.5, Section 4.17.2
      -Telnet special commands: Section 3.1.3.2, Section 4.17.3
      -Telnet URLs: Section 3.8.1
      -termcap: Section 4.4, Section 4.15.3
      -TERM environment variable: Section 4.13.2
      -terminal bell: Section 4.5
      -terminal bell, disabling: Section 4.5.1, Section 4.5.3
      -terminal bell overload mode: Section 4.5.3
      -terminal control sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 7.2.1
      -terminal emulation: Section 3.1.3.6, Section 4.3, Section 4.3.7, Section 4.4, Section 4.6, Section 4.15.3
      -terminal modes: Section 4.25.2
      +system menu: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
      +system tray, Windows: Section 3.11.3.27, Section 9.1
      +talker systems: Section 1.1, Section 4.3.9
      +taskbar: Section 4.5.2
      +taskbar notification area, Windows (aka system tray): Section 3.11.3.27, Section 9.1
      +-T command-line option: Section 3.11.3.12
      +-t command-line option: Section 3.11.3.12
      +TCP connections, raw: Section 3.7
      +TCP keepalives: Section 4.14.3
      +TCP_NODELAY: Section 4.14.2
      +TCP proxy: Section 4.16.1, Section 4.16.5, Section 4.29.2
      +Telnet: Section 2.1, Section 3.8, Section 4.29
      +-telnet command-line option: Section 3.11.1, Section 3.11.3.2
      +Telnet, differences from other protocols: Section 1.2
      +Telnet New Line: Section 4.29.4
      +Telnet option negotiation: Section 4.29.2
      +Telnet proxy: Section 4.16.1, Section 4.16.5, Section 4.29.2
      +Telnet special commands: Section 3.1.3.2, Section 4.29.3
      +Telnet URLs: Section 3.11.1
      +termcap: Section 4.4, Section 4.15.3
      +TERM environment variable: Section 4.13.2
      +terminal bell: Section 4.5
      +terminal bell, disabling: Section 4.5.1, Section 4.5.3
      +terminal bell overload mode: Section 4.5.3
      +terminal control sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 7.2.1
      +terminal emulation: Section 3.1.3.6, Section 4.3, Section 4.3.7, Section 4.4, Section 4.6, Section 4.15.3
      +terminal modes: Section 4.23.2
      terminal, resetting: Section 3.1.3.6
      -terminal resizing: Section 4.6.3, Section 4.7.1, Section 4.7.2
      -terminal speed: Section 4.15.4
      +terminal resizing: Section 4.6.3, Section 4.7.1, Section 4.7.2
      +terminal speed: Section 4.15.4
      terminal type: Question A.5.1
      -terminal window: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
      -terminal window, inactive: Section 4.1.3
      -terminfo: Section 4.4, Section 4.15.3
      -three-button mouse: Section 3.1.1, Section 4.11.1
      -timeout, of connections: Section 4.14.1, Question A.7.8
      +terminal window: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
      +terminal window, inactive: Section 4.1.3
      +terminfo: Section 4.4, Section 4.15.3
      +three-button mouse: Section 3.1.1, Section 4.11.1
      +timeout, of connections: Section 4.14.1, Question A.7.8
      timestamp: Section 5.2.2.2
      -TIS authentication: Section 4.23.4
      -token, security: Section 4.23.4
      +TIS authentication: Section 4.21.5
      +token, security: Section 4.21.5
      transferring files: Chapter 5, Chapter 6
      triple-click: Section 3.1.1
      -triple-DES: Section 4.22
      -trn: Section 4.6.2
      -trusting host keys: Section 2.2
      -tunnelling using SSH: Section 3.5, Section 3.8.3.5, Section 4.16, Section 4.19.2, Section 4.27
      -tunnelling using SSH, changing mid-session: Section 4.27
      -typeface: Section 4.8.2, Section 4.10.4, Section 4.12.2, Section 4.13.4
      -UDP: Section 4.27
      -Unicode: Section 4.6.10, Section 4.10.1, Section 4.10.4, Section 4.10.5
      +triple-DES: Section 4.20
      +trn: Section 4.6.2
      +trusting host keys: Section 2.2
      +tunnelling using SSH: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
      +tunnelling using SSH, changing mid-session: Section 4.25
      +typeface: Section 4.8.2, Section 4.10.4, Section 4.12.2, Section 4.13.4
      +UDP: Section 4.25
      +Unicode: Section 4.6.10, Section 4.10.1, Section 4.10.4, Section 4.10.5
      uninstalling: Question A.8.2
      -Unix: Section 4.4, Section 4.4.1, Section 4.15.3, Section 4.25.1, Section 6.2.14, Section 10.18, Question A.7.13
      +Unix: Section 4.4, Section 4.4.1, Section 4.15.3, Section 4.23.1, Section 6.2.14, Section 10.18, Question A.7.13
      Unix version of PuTTY tools: PuTTY User Manual, Question A.3.2
      -unsafe PSCP command-line option: Section 5.2.1
      -upgraded version, verifying: Section 3.8.3.21, Appendix E
      +upgraded version, verifying: Section 3.11.3.22, Appendix F
      uploading files: Section 5.2.1, Section 6.2.10
      Uppity: Question A.2.10
      -URLs, Telnet: Section 3.8.1
      -US-ASCII: Section 4.10.5, Question A.2.11
      -user name: Section 2.3, Section 3.8.3.4, Section 5.2.1.1
      -user name, for auto-login: Section 4.15.1
      -user name, for proxy: Section 4.16.4
      -user name, local, in Rlogin: Section 4.18.1
      -user name, local, in Windows: Section 4.18.1
      -user names, different: Section 2.3, Section 4.23.7
      -UTF-8: Section 4.10.1, Section 4.10.2, Section 4.25.2, Question A.7.15
      -variables, environment: Section 4.15.5, Section 4.17.1
      --v command-line option: Section 3.8.3.3
      -VERASE, special character: Section 4.25.2
      -verbose mode: Section 3.8.3.3
      -verifying new versions of PuTTY: Section 3.8.3.21, Appendix E
      -verifying the host key: Section 2.2, Question A.2.9
      -version, of Internet Protocol: Section 3.8.3.17, Section 4.14.4, Section 4.27.2
      +URLs, Telnet: Section 3.11.1
      +US-ASCII: Section 4.10.5, Question A.2.11
      +user name: Section 2.3, Section 3.11.3.4, Section 5.2.1.1
      +user name, for auto-login: Section 4.15.1
      +user name, for proxy: Section 4.16.4
      +user name, local, in Rlogin: Section 4.30.1
      +user name, local, in Windows: Section 4.30.1
      +user names, different: Section 2.3, Section 4.21.8
      +UTF-8: Section 4.10.1, Section 4.10.2, Section 4.23.2, Question A.7.15
      +variables, environment: Section 4.15.5, Section 4.29.1
      +-v command-line option: Section 3.11.3.3
      +VERASE, special character: Section 4.23.2
      +verbose mode: Section 3.11.3.3
      +verifying new versions of PuTTY: Section 3.11.3.22, Appendix F
      +verifying the host key: Section 2.2, Question A.2.9
      +version, of Internet Protocol: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
      version, of PuTTY: Section B.2
      -version, of SSH protocol: Section 3.8.3.16, Section 4.19.4
      -visual bell: Section 4.5.1
      -VQUIT, special character: Section 4.25.2
      -VT100+: Section 4.4.3
      -VT400: Section 4.4.3
      -vt220: Section 4.15.3
      -WAV file: Section 4.5.1
      -web browser: Section 3.5, Section 3.8.1
      +version, of SSH protocol: Section 3.11.3.16, Section 4.17.4
      +visual bell: Section 4.5.1
      +VQUIT, special character: Section 4.23.2
      +VT100+: Section 4.4.3
      +VT400: Section 4.4.3
      +vt220: Section 4.15.3
      +WAV file: Section 4.5.1
      +web browser: Section 3.5, Section 3.11.1
      web server: Section 1.1
      web site: Section B.7
      -white space: Section 4.12.1
      +white space: Section 4.12.1
      wildcards: Section 5.2.1, Section 5.2.1.3, Section 5.2.2.6, Section 6.2.2, Section 6.2.11
      WinCVS: Section 7.5
      -window border: Section 4.8.4
      -window caption: Section 4.5.2, Section 4.9.1
      -window, closing: Section 4.1.3, Section 4.9.2, Section 4.9.3
      -window, inactive: Section 4.1.3
      -window, maximising: Section 4.7.2, Question A.6.3
      -window menu: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
      -window, minimising: Section 4.9.1
      -window resizing: Section 4.6.3, Section 4.7.1, Section 4.7.2
      +window border: Section 4.8.4
      +window caption: Section 4.5.2, Section 4.9.1
      +window, closing: Section 4.1.3, Section 4.9.2, Section 4.9.3
      +window, inactive: Section 4.1.3
      +window, maximising: Section 4.7.2, Question A.6.3
      +window menu: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
      +window, minimising: Section 4.9.1
      +window resizing: Section 4.6.3, Section 4.7.1, Section 4.7.2
      Windows 3.1: Question A.3.5
      Windows, 32-bit: Question A.6.10
      Windows, 64-bit: Question A.6.10, Question A.7.22
      Windows clipboard: Section 3.1.1
      Windows command: Section 6.2.19
      -Windows Default Beep sound: Section 4.5
      +Windows Default Beep sound: Section 4.5
      Windows file sharing: Question A.7.17
      -window size: Section 4.7.1
      -Windows process ACL: Section 3.8.3.25, Section 9.3.3
      -Windows Registry: Section 2.2, Section 4.1.2, Section 4.30, Question A.5.2
      -Windows shortcut: Section 3.8, Section 3.8.3.1, Section 9.3, Question A.6.4
      +window size: Section 4.7.1
      +Windows process ACL: Section 3.11.3.27, Section 9.3.4
      +Windows Registry: Section 2.2, Section 4.1.2, Section 4.32, Question A.5.2
      +Windows shortcut: Section 3.11, Section 3.11.3.1, Section 9.3, Question A.6.4
      Windows Terminal Services: Question A.7.17
      Windows XP: Question A.7.17, Question A.7.21
      -window, terminal: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
      -window title: Section 4.6.5, Section 4.6.6, Section 4.9.1, Question A.7.11
      +window, terminal: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
      +window title: Section 4.6.5, Section 4.6.6, Section 4.9.1, Question A.7.11
      Win32s: Question A.3.5
      -Win125x: Section 4.10.1
      -words, selecting: Section 3.1.1, Section 4.12.1
      +Win125x: Section 4.10.1
      +words, selecting: Section 3.1.1, Section 4.12.1
      working directory: Section 6.2.7, Section 6.2.8
      -wrapping, automatic: Section 4.3.1, Question A.7.7
      -wrapping, terminal: Section 4.10.2
      +wrapping, automatic: Section 4.3.1, Question A.7.7
      +wrapping, terminal: Section 4.10.2
      write permission: Section 6.2.14
      -X11 authentication: Section 4.26.1
      +X11 authentication: Section 4.24.1
      x86 (32-bit processor architecture): Question A.6.10
      --X command-line option: Section 3.8.3.11
      --x command-line option: Section 3.8.3.11
      +-X command-line option: Section 3.11.3.11
      +-x command-line option: Section 3.11.3.11
      ‘X display location’: Section 3.4
      -XDM-AUTHORIZATION-1: Section 4.26.1
      -X11 forwarding: Section 3.4, Section 3.8.3.11, Section 4.26
      +XDM-AUTHORIZATION-1: Section 4.24.1
      +X11 forwarding: Section 3.4, Section 3.11.3.11, Section 4.24
      X server: Section 3.4
      -xterm: Section 3.1.1, Section 4.4.2, Section 4.4.3, Section 4.11.1, Section 4.15.3
      -xterm mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2

      +xterm: Section 3.1.1, Section 4.4.2, Section 4.4.3, Section 4.11.1, Section 4.15.3
      +xterm mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/Makefile b/doc/Makefile index fd5ca53..5ff25d5 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -35,7 +35,8 @@ VERSIONIDS=vids endif CHAPTERS := $(SITE) copy blurb intro gs using config pscp psftp plink -CHAPTERS += pubkey pageant errors faq feedback licence udp pgpkeys sshnames +CHAPTERS += pubkey pageant errors faq feedback pubkeyfmt licence udp +CHAPTERS += pgpkeys sshnames CHAPTERS += index $(VERSIONIDS) INPUTS = $(patsubst %,%.but,$(CHAPTERS)) @@ -59,17 +60,19 @@ putty.info: $(INPUTS) MKMAN = $(HALIBUT) --man=$@ mancfg.but $< MANPAGES = putty.1 puttygen.1 plink.1 pscp.1 psftp.1 puttytel.1 pterm.1 \ - pageant.1 + pageant.1 psocks.1 psusan.1 man: $(MANPAGES) -putty.1: man-putt.but mancfg.but; $(MKMAN) -puttygen.1: man-pg.but mancfg.but; $(MKMAN) -plink.1: man-pl.but mancfg.but; $(MKMAN) +putty.1: man-putty.but mancfg.but; $(MKMAN) +puttygen.1: man-puttygen.but mancfg.but; $(MKMAN) +plink.1: man-plink.but mancfg.but; $(MKMAN) pscp.1: man-pscp.but mancfg.but; $(MKMAN) -psftp.1: man-psft.but mancfg.but; $(MKMAN) -puttytel.1: man-ptel.but mancfg.but; $(MKMAN) -pterm.1: man-pter.but mancfg.but; $(MKMAN) -pageant.1: man-pag.but mancfg.but; $(MKMAN) +psftp.1: man-psftp.but mancfg.but; $(MKMAN) +puttytel.1: man-puttytel.but mancfg.but; $(MKMAN) +pterm.1: man-pterm.but mancfg.but; $(MKMAN) +pageant.1: man-pageant.but mancfg.but; $(MKMAN) +psocks.1: man-psocks.but mancfg.but; $(MKMAN) +psusan.1: man-psusan.but mancfg.but; $(MKMAN) mostlyclean: rm -f *.html *.txt *.hlp *.cnt *.1 *.info vstr.but *.hh[pck] diff --git a/doc/config.but b/doc/config.but index ee17966..7731328 100644 --- a/doc/config.but +++ b/doc/config.but @@ -14,28 +14,47 @@ save your settings to be reloaded later. \S{config-hostname} The \i{host name} section -The top box on the Session panel, labelled \q{Specify your -connection by host name}, contains the details that need to be -filled in before PuTTY can open a session at all. +The top box on the Session panel, labelled \q{Specify the destination +you want to connect to}, contains the details that need to be filled +in before PuTTY can open a session at all. \b The \q{Host Name} box is where you type the name, or the \i{IP address}, of the server you want to connect to. -\b The \q{Connection type} radio buttons let you choose what type of -connection you want to make: a \I{raw TCP connections}raw -connection, a \i{Telnet} connection, an \i{Rlogin} connection, an -\i{SSH} connection, or a connection to a local \i{serial line}. (See -\k{which-one} for a summary of the differences between SSH, Telnet -and rlogin; see \k{using-rawprot} for an explanation of \q{raw} -connections; see \k{using-serial} for information about using a -serial line.) +\b The \q{Connection type} controls let you choose what type of +connection you want to make: an \i{SSH} network connection, a +connection to a local \i{serial line}, or various other kinds of +network connection. + +\lcont{ + +\b See \k{which-one} for a summary of the +differences between the network remote login protocols SSH, Telnet, +Rlogin, and SUPDUP. + +\b See \k{using-serial} for information about using a serial line. + +\b See \k{using-rawprot} for an explanation of \q{raw} +connections. + +\b See \k{using-telnet} for a little information about Telnet. + +\b See \k{using-rlogin} for information about using Rlogin. + +\b See \k{using-supdup} for information about using SUPDUP. + +\b The \q{Bare ssh-connection} option in the \q{Connection type} +control is intended for specialist uses not involving network +connections. See \k{config-psusan} for some information about it. + +} \b The \q{Port} box lets you specify which \i{port number} on the -server to connect to. If you select Telnet, Rlogin, or SSH, this box -will be filled in automatically to the usual value, and you will -only need to change it if you have an unusual server. If you select -Raw mode, you will almost certainly need to fill in the \q{Port} box -yourself. +server to connect to. If you select Telnet, Rlogin, SUPDUP, or SSH, +this box will be filled in automatically to the usual value, and you +will only need to change it if you have an unusual server. If you +select Raw mode, you will almost certainly need to fill in the +\q{Port} box yourself. If you select \q{Serial} from the \q{Connection type} radio buttons, the \q{Host Name} and \q{Port} boxes are replaced by \q{Serial line} @@ -1668,8 +1687,8 @@ connection loss, or you might find they make it worse, depending on what \e{kind} of network problems you have between you and the server. -Keepalives are only supported in Telnet and SSH; the Rlogin and Raw -protocols offer no way of implementing them. (For an alternative, see +Keepalives are only supported in Telnet and SSH; the Rlogin, SUPDUP, and +Raw protocols offer no way of implementing them. (For an alternative, see \k{config-tcp-keepalives}.) Note that if you are using SSH-1 and the server has a bug that makes @@ -1697,8 +1716,8 @@ are provided for completeness. The idea of TCP keepalives is similar to application-level keepalives, and the same caveats apply. The main differences are: -\b TCP keepalives are available on \e{all} connection types, including -Raw and Rlogin. +\b TCP keepalives are available on \e{all} network connection types, +including Raw, Rlogin, and SUPDUP. \b The interval between TCP keepalives is usually much longer, typically two hours; this is set by the operating system, and cannot @@ -1791,7 +1810,7 @@ configuration panels. \S{config-username} \q{\ii{Auto-login username}} -All three of the SSH, Telnet and Rlogin protocols allow you to +All three of the SSH, Telnet, and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.) @@ -1820,7 +1839,7 @@ Most servers you might connect to with PuTTY are designed to be connected to from lots of different types of terminal. In order to send the right \i{control sequence}s to each one, the server will need to know what type of terminal it is dealing with. Therefore, each of -the SSH, Telnet and Rlogin protocols allow a text string to be sent +the SSH, Telnet, and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a \i{Unix} server, this selects an entry from the \i\c{termcap} or \i\c{terminfo} database that tells applications what \i{control sequences} to send to the @@ -2087,123 +2106,6 @@ session is deemed to have started (in a protocol-dependent way), which is when they're most likely to be interesting; any further proxy-related messages during the session will only go to the Event Log. -\H{config-telnet} The \i{Telnet} panel - -The Telnet panel allows you to configure options that only apply to -Telnet sessions. - -\S{config-oldenviron} \q{Handling of OLD_ENVIRON ambiguity} - -The original Telnet mechanism for passing \i{environment variables} was -badly specified. At the time the standard (RFC 1408) was written, -BSD telnet implementations were already supporting the feature, and -the intention of the standard was to describe the behaviour the BSD -implementations were already using. - -Sadly there was a typing error in the standard when it was issued, -and two vital function codes were specified the wrong way round. BSD -implementations did not change, and the standard was not corrected. -Therefore, it's possible you might find either \i{BSD} or \i{RFC}-compliant -implementations out there. This switch allows you to choose which -one PuTTY claims to be. - -The problem was solved by issuing a second standard, defining a new -Telnet mechanism called \i\cw{NEW_ENVIRON}, which behaved exactly like -the original \i\cw{OLD_ENVIRON} but was not encumbered by existing -implementations. Most Telnet servers now support this, and it's -unambiguous. This feature should only be needed if you have trouble -passing environment variables to quite an old server. - -\S{config-ptelnet} Passive and active \i{Telnet negotiation} modes - -In a Telnet connection, there are two types of data passed between -the client and the server: actual text, and \e{negotiations} about -which Telnet extra features to use. - -PuTTY can use two different strategies for negotiation: - -\b In \I{active Telnet negotiation}\e{active} mode, PuTTY starts to send -negotiations as soon as the connection is opened. - -\b In \I{passive Telnet negotiation}\e{passive} mode, PuTTY will wait to -negotiate until it sees a negotiation from the server. - -The obvious disadvantage of passive mode is that if the server is -also operating in a passive mode, then negotiation will never begin -at all. For this reason PuTTY defaults to active mode. - -However, sometimes passive mode is required in order to successfully -get through certain types of firewall and \i{Telnet proxy} server. If -you have confusing trouble with a \i{firewall}, you could try enabling -passive mode to see if it helps. - -\S{config-telnetkey} \q{Keyboard sends \i{Telnet special commands}} - -If this box is checked, several key sequences will have their normal -actions modified: - -\b the Backspace key on the keyboard will send the \I{Erase Character, -Telnet special command}Telnet special backspace code; - -\b Control-C will send the Telnet special \I{Interrupt Process, Telnet -special command}Interrupt Process code; - -\b Control-Z will send the Telnet special \I{Suspend Process, Telnet -special command}Suspend Process code. - -You probably shouldn't enable this -unless you know what you're doing. - -\S{config-telnetnl} \q{Return key sends \i{Telnet New Line} instead of ^M} - -Unlike most other remote login protocols, the Telnet protocol has a -special \q{\i{new line}} code that is not the same as the usual line -endings of Control-M or Control-J. By default, PuTTY sends the -Telnet New Line code when you press Return, instead of sending -Control-M as it does in most other protocols. - -Most Unix-style Telnet servers don't mind whether they receive -Telnet New Line or Control-M; some servers do expect New Line, and -some servers prefer to see ^M. If you are seeing surprising -behaviour when you press Return in a Telnet session, you might try -turning this option off to see if it helps. - -\H{config-rlogin} The Rlogin panel - -The \i{Rlogin} panel allows you to configure options that only apply to -Rlogin sessions. - -\S{config-rlogin-localuser} \I{local username in Rlogin}\q{Local username} - -Rlogin allows an automated (password-free) form of login by means of -a file called \i\c{.rhosts} on the server. You put a line in your -\c{.rhosts} file saying something like \c{jbloggs@pc1.example.com}, -and then when you make an Rlogin connection the client transmits the -username of the user running the Rlogin client. The server checks -the username and hostname against \c{.rhosts}, and if they match it -\I{passwordless login}does not ask for a password. - -This only works because Unix systems contain a safeguard to stop a -user from pretending to be another user in an Rlogin connection. -Rlogin connections have to come from \I{privileged port}port numbers below -1024, and Unix systems prohibit this to unprivileged processes; so when the -server sees a connection from a low-numbered port, it assumes the -client end of the connection is held by a privileged (and therefore -trusted) process, so it believes the claim of who the user is. - -Windows does not have this restriction: \e{any} user can initiate an -outgoing connection from a low-numbered port. Hence, the Rlogin -\c{.rhosts} mechanism is completely useless for securely -distinguishing several different users on a Windows machine. If you -have a \c{.rhosts} entry pointing at a Windows PC, you should assume -that \e{anyone} using that PC can \i{spoof} your username in -an Rlogin connection and access your account on the server. - -The \q{Local username} control allows you to specify what user name -PuTTY should claim you have, in case it doesn't match your \i{Windows -user name} (or in case you didn't bother to set up a Windows user -name). - \H{config-ssh} The SSH panel The \i{SSH} panel allows you to configure options that only apply to @@ -2521,9 +2423,13 @@ Configuration is similar to cipher selection (see PuTTY currently supports the following host key types: -\b \q{Ed25519}: \i{Edwards-curve} \i{DSA} using a twisted Edwards +\b \q{\i{Ed25519}}: \I{EdDSA}Edwards-curve DSA using a twisted Edwards curve with modulus \cw{2^255-19}. +\b \q{\i{Ed448}}: another \I{EdDSA}Edwards-curve DSA type, using a +larger elliptic curve with a 448-bit instead of 255-bit modulus (so it +has a higher security level than Ed25519). + \b \q{ECDSA}: \i{elliptic curve} \i{DSA} using one of the NIST-standardised elliptic curves. @@ -2532,8 +2438,8 @@ NIST-standardised elliptic curves. \b \q{RSA}: the ordinary \i{RSA} algorithm. If PuTTY already has one or more host keys stored for the server, -it will prefer to use one of those, even if the server has a key -type that is higher in the preference order. You can add such a +it will by default prefer to use one of those, even if the server has +a key type that is higher in the preference order. You can add such a key to PuTTY's cache from within an existing session using the \q{Special Commands} menu; see \k{using-specials}. @@ -2544,6 +2450,27 @@ If the first key type PuTTY finds is below the \q{warn below here} line, you will see a warning box when you make the connection, similar to that for cipher selection (see \k{config-ssh-encryption}). +\S{config-ssh-prefer-known-hostkeys} Preferring known host keys + +By default, PuTTY will adjust the preference order for host key +algorithms so that any host keys it already knows are moved to the top +of the list. + +This prevents you from having to check and confirm a new host key for +a server you already had one for (e.g. because the server has +generated an alternative key of a type higher in PuTTY's preference +order, or because you changed the preference order itself). + +However, on the other hand, it can leak information to a listener in +the network about \e{whether} you already know a host key for this +server. + +For this reason, this policy is configurable. By turning this checkbox +off, you can reset PuTTY to always use the exact order of host key +algorithms configured in the preference list described in +\k{config-ssh-hostkey-order}, so that a listener will find out nothing +about what keys you had stored. + \S{config-ssh-kex-manual-hostkeys} \ii{Manually configuring host keys} In some situations, if PuTTY's automated host key management is not @@ -2577,9 +2504,13 @@ You can remove keys again with the \q{Remove} button. The text describing a host key can be in one of the following formats: -\b An MD5-based host key fingerprint of the form displayed in PuTTY's -Event Log and host key dialog boxes, i.e. sixteen 2-digit hex numbers -separated by colons. +\b An \I{SHA256 fingerprint}SHA-256-based host key fingerprint of the +form displayed in PuTTY's Event Log and host key dialog boxes, +i.e. \cq{SHA256:} followed by 43 case-sensitive characters. + +\b An \I{MD5 fingerprint}MD5-based host key fingerprint, i.e. sixteen +2-digit hex numbers separated by colons, optionally preceded by the +prefix \cq{MD5:}. (The case of the characters does not matter.) \b A base64-encoded blob describing an SSH-2 public key in OpenSSH's one-line public key format. How you acquire a public key in @@ -2692,6 +2623,49 @@ interact with them.) This option only affects SSH-2 connections. SSH-1 connections always require an authentication step. +\S{config-ssh-notrivialauth} \q{Disconnect if authentication succeeds +trivially} + +This option causes PuTTY to abandon an SSH session and disconnect from +the server, if the server accepted authentication without ever having +asked for any kind of password or signature or token. + +This might be used as a security measure. There are some forms of +attack against an SSH client user which work by terminating the SSH +authentication stage early, and then doing something in the main part +of the SSH session which \e{looks} like part of the authentication, +but isn't really. + +For example, instead of demanding a signature from your public key, +for which PuTTY would ask for your key's passphrase, a compromised or +malicious server might allow you to log in with no signature or +password at all, and then print a message that \e{imitates} PuTTY's +request for your passphrase, in the hope that you would type it in. +(In fact, the passphrase for your public key should not be sent to any +server.) + +PuTTY's main defence against attacks of this type is the \q{trust +sigil} system: messages in the PuTTY window that are truly originated +by PuTTY itself are shown next to a small copy of the PuTTY icon, +which the server cannot fake when it tries to imitate the same message +using terminal output. + +However, if you think you might be at risk of this kind of thing +anyway (if you don't watch closely for the trust sigils, or if you +think you're at extra risk of one of your servers being malicious), +then you could enable this option as an extra defence. Then, if the +server tries any of these attacks involving letting you through the +authentication stage, PuTTY will disconnect from the server before it +can send a follow-up fake prompt or other type of attack. + +On the other hand, some servers \e{legitimately} let you through the +SSH authentication phase trivially, either because they are genuinely +public, or because the important authentication step happens during +the terminal session. (An example might be an SSH server that connects +you directly to the terminal login prompt of a legacy mainframe.) So +enabling this option might cause some kinds of session to stop +working. It's up to you. + \S{config-ssh-tryagent} \q{Attempt authentication using Pageant} If this option is enabled, then PuTTY will look for Pageant (the SSH @@ -3458,6 +3432,52 @@ will be impossible. This is an SSH-1-specific bug. +\H{config-psusan} The \q{Bare \cw{\i{ssh-connection}}} protocol + +In addition to SSH itself, PuTTY also supports a second protocol that +is derived from SSH. It's listed in the PuTTY GUI under the name +\q{Bare \cw{ssh-connection}}. + +This protocol consists of just the innermost of SSH-2's three layers: it +leaves out the cryptography layer providing network security, and it +leaves out the authentication layer where you provide a username and +prove you're allowed to log in as that user. + +It is therefore \s{completely unsuited to any network connection}. +Don't try to use it over a network! + +The purpose of this protocol is for various specialist circumstances +in which the \q{connection} is not over a real network, but is a pipe +or IPC channel between different processes running on the \e{same} +computer. In these contexts, the operating system will already have +guaranteed that each of the two communicating processes is owned by +the expected user (so that no authentication is necessary), and that +the communications channel cannot be tapped by a hostile user on the +same machine (so that no cryptography is necessary either). Examples +of possible uses involve communicating with a strongly separated +context such as the inside of a container, or a VM, or a different +network namespace. + +Explicit support for this protocol is new in PuTTY 0.75. As of +2021-04, the only known server for the bare \cw{ssh-connection} +protocol is the Unix program \cq{\i{psusan}} that is also part of the +PuTTY tool suite. + +(However, this protocol is also the same one used between instances of +PuTTY to implement connection sharing: see \k{config-ssh-sharing}. In +fact, in the Unix version of PuTTY, when a sharing upstream records +\q{Sharing this connection at [pathname]} in the Event Log, it's +possible to connect another instance of PuTTY directly to that Unix +socket, by entering its pathname in the host name box and selecting +\q{Bare \cw{ssh-connection}} as the protocol!) + +Many of the options under the SSH panel also affect this protocol, +although options to do with cryptography and authentication do not, +for obvious reasons. + +I repeat, \s{DON'T TRY TO USE THIS PROTOCOL FOR NETWORK CONNECTIONS!} +That's not what it's for, and it's not at all safe to do it. + \H{config-serial} The Serial panel The \i{Serial} panel allows you to configure options that only apply @@ -3535,6 +3555,164 @@ the serial line. \b \q{DSR/DTR}: flow control is done using the DSR and DTR wires on the serial line. +\H{config-telnet} The \i{Telnet} panel + +The Telnet panel allows you to configure options that only apply to +Telnet sessions. + +\S{config-oldenviron} \q{Handling of OLD_ENVIRON ambiguity} + +The original Telnet mechanism for passing \i{environment variables} was +badly specified. At the time the standard (RFC 1408) was written, +BSD telnet implementations were already supporting the feature, and +the intention of the standard was to describe the behaviour the BSD +implementations were already using. + +Sadly there was a typing error in the standard when it was issued, +and two vital function codes were specified the wrong way round. BSD +implementations did not change, and the standard was not corrected. +Therefore, it's possible you might find either \i{BSD} or \i{RFC}-compliant +implementations out there. This switch allows you to choose which +one PuTTY claims to be. + +The problem was solved by issuing a second standard, defining a new +Telnet mechanism called \i\cw{NEW_ENVIRON}, which behaved exactly like +the original \i\cw{OLD_ENVIRON} but was not encumbered by existing +implementations. Most Telnet servers now support this, and it's +unambiguous. This feature should only be needed if you have trouble +passing environment variables to quite an old server. + +\S{config-ptelnet} Passive and active \i{Telnet negotiation} modes + +In a Telnet connection, there are two types of data passed between +the client and the server: actual text, and \e{negotiations} about +which Telnet extra features to use. + +PuTTY can use two different strategies for negotiation: + +\b In \I{active Telnet negotiation}\e{active} mode, PuTTY starts to send +negotiations as soon as the connection is opened. + +\b In \I{passive Telnet negotiation}\e{passive} mode, PuTTY will wait to +negotiate until it sees a negotiation from the server. + +The obvious disadvantage of passive mode is that if the server is +also operating in a passive mode, then negotiation will never begin +at all. For this reason PuTTY defaults to active mode. + +However, sometimes passive mode is required in order to successfully +get through certain types of firewall and \i{Telnet proxy} server. If +you have confusing trouble with a \i{firewall}, you could try enabling +passive mode to see if it helps. + +\S{config-telnetkey} \q{Keyboard sends \i{Telnet special commands}} + +If this box is checked, several key sequences will have their normal +actions modified: + +\b the Backspace key on the keyboard will send the \I{Erase Character, +Telnet special command}Telnet special backspace code; + +\b Control-C will send the Telnet special \I{Interrupt Process, Telnet +special command}Interrupt Process code; + +\b Control-Z will send the Telnet special \I{Suspend Process, Telnet +special command}Suspend Process code. + +You probably shouldn't enable this +unless you know what you're doing. + +\S{config-telnetnl} \q{Return key sends \i{Telnet New Line} instead of ^M} + +Unlike most other remote login protocols, the Telnet protocol has a +special \q{\i{new line}} code that is not the same as the usual line +endings of Control-M or Control-J. By default, PuTTY sends the +Telnet New Line code when you press Return, instead of sending +Control-M as it does in most other protocols. + +Most Unix-style Telnet servers don't mind whether they receive +Telnet New Line or Control-M; some servers do expect New Line, and +some servers prefer to see ^M. If you are seeing surprising +behaviour when you press Return in a Telnet session, you might try +turning this option off to see if it helps. + +\H{config-rlogin} The Rlogin panel + +The \i{Rlogin} panel allows you to configure options that only apply to +Rlogin sessions. + +\S{config-rlogin-localuser} \I{local username in Rlogin}\q{Local username} + +Rlogin allows an automated (password-free) form of login by means of +a file called \i\c{.rhosts} on the server. You put a line in your +\c{.rhosts} file saying something like \c{jbloggs@pc1.example.com}, +and then when you make an Rlogin connection the client transmits the +username of the user running the Rlogin client. The server checks +the username and hostname against \c{.rhosts}, and if they match it +\I{passwordless login}does not ask for a password. + +This only works because Unix systems contain a safeguard to stop a +user from pretending to be another user in an Rlogin connection. +Rlogin connections have to come from \I{privileged port}port numbers below +1024, and Unix systems prohibit this to unprivileged processes; so when the +server sees a connection from a low-numbered port, it assumes the +client end of the connection is held by a privileged (and therefore +trusted) process, so it believes the claim of who the user is. + +Windows does not have this restriction: \e{any} user can initiate an +outgoing connection from a low-numbered port. Hence, the Rlogin +\c{.rhosts} mechanism is completely useless for securely +distinguishing several different users on a Windows machine. If you +have a \c{.rhosts} entry pointing at a Windows PC, you should assume +that \e{anyone} using that PC can \i{spoof} your username in +an Rlogin connection and access your account on the server. + +The \q{Local username} control allows you to specify what user name +PuTTY should claim you have, in case it doesn't match your \i{Windows +user name} (or in case you didn't bother to set up a Windows user +name). + +\H{config-supdup} The \i{SUPDUP} panel + +The SUPDUP panel allows you to configure options that only apply +to SUPDUP sessions. See \k{using-supdup} for more about the SUPDUP +protocol. + +\S{supdup-location} \q{Location string} + +In SUPDUP, the client sends a piece of text of its choice to the +server giving the user's location. This is typically displayed in +lists of logged-in users. + +By default, PuTTY just defaults this to "The Internet". If you want +your location to show up as something more specific, you can configure +it here. + +\S{supdup-ascii} \q{Extended ASCII Character set} + +This declares what kind of character set extension your terminal +supports. If the server supports it, it will send text using that +character set. \q{None} means the standard 95 printable ASCII +characters. \q{ITS} means ASCII extended with printable characters in +the control character range. This character set is documented in the +SUPDUP protocol definition. \q{WAITS} is similar to \q{ITS} but uses +some alternative characters in the extended set: most prominently, it +will display arrows instead of \c{^} and \c{_}, and \c{\}} instead of +\c{~}. \q{ITS} extended ASCII is used by ITS and Lisp machines, +whilst \q{WAITS} is only used by the WAITS operating system from the +Stanford AI Laboratory. + +\S{supdup-more} \q{**MORE** processing} + +When **MORE** processing is enabled, the server causes output to pause +at the bottom of the screen, until a space is typed. + +\S{supdup-scroll} \q{Terminal scrolling} + +This controls whether the terminal will perform scrolling then the +cursor goes below the last line, or if the cursor will return to the +first line. + \H{config-file} \ii{Storing configuration in a file} PuTTY does not currently support storing its configuration in a file diff --git a/doc/copy.but b/doc/copy.but index 29ad643..c86dceb 100644 --- a/doc/copy.but +++ b/doc/copy.but @@ -1,5 +1,5 @@ \# Generated by licence.pl from LICENCE. \# You should edit those files rather than editing this one. -\define{shortcopyrightdetails} 1997-2019 Simon Tatham +\define{shortcopyrightdetails} 1997-2021 Simon Tatham diff --git a/doc/errors.but b/doc/errors.but index ee88c00..36a42d9 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -332,8 +332,8 @@ your server was rejected by the server. Usually this happens because the server does not provide the service which PuTTY is trying to access. -Check that you are connecting with the correct protocol (SSH, Telnet -or Rlogin), and check that the port number is correct. If that +Check that you are connecting with the correct protocol (SSH, Telnet, +etc), and check that the port number is correct. If that fails, consult the administrator of your server. \H{errors-conntimedout} \q{Network error: Connection timed out} diff --git a/doc/faq.but b/doc/faq.but index 8977703..474b984 100644 --- a/doc/faq.but +++ b/doc/faq.but @@ -7,8 +7,8 @@ appendix in the manual. \S{faq-what}{Question} What is PuTTY? -PuTTY is a client program for the SSH, Telnet and Rlogin network -protocols. +PuTTY is a client program for the SSH, Telnet, Rlogin, and SUPDUP +network protocols. These protocols are all used to run a remote session on a computer, over a network. PuTTY implements the client end of that session: the @@ -1154,6 +1154,16 @@ inactive. And Pageant spends most of its time inactive. \H{faq-admin} Administrative questions +\S{faq-putty-org}{Question} Is \cw{putty.org} your website? + +No, it isn't. \cw{putty.org} is run by an opportunist who uses it to +advertise their own commercial SSH implementation to people looking +for our free one. We don't own that site, we can't control it, and we +don't advise anyone to use it in preference to our own site. + +The real PuTTY web site, run by the PuTTY team, has always been at +\W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}. + \S{faq-domain}{Question} Would you like me to register you a nicer domain name? diff --git a/doc/feedback.but b/doc/feedback.but index eb2fb5d..b7aa9c8 100644 --- a/doc/feedback.but +++ b/doc/feedback.but @@ -51,7 +51,9 @@ actually need it, and only one of us needs to download it instead of it being automatically copied to all the developers. (If the file contains confidential information, then you could encrypt -it with our Secure Contact Key; see \k{pgpkeys-pubkey} for details.) +it with our Secure Contact Key; see \k{pgpkeys-pubkey} for details. +Please \e{only} use this for information that \e{needs} to be +confidential.) Some people like to send mail in MS Word format. Please \e{don't} send us bug reports, or any other mail, as a Word document. Word @@ -156,7 +158,7 @@ for Arm, tell us, or we'll assume you're running on Windows for Intel as this is overwhelmingly the case.) \b Tell us what protocol you are connecting with: SSH, Telnet, -Rlogin, or Raw mode, or a serial connection. +Rlogin, SUPDUP, or Raw mode, or a serial connection. \b Tell us what kind of server you are connecting to; what OS, and if possible what SSH server (if you're using SSH). You can get some diff --git a/doc/gs.but b/doc/gs.but index fb33f3d..e6a8492 100644 --- a/doc/gs.but +++ b/doc/gs.but @@ -18,15 +18,15 @@ you want to connect to. You should have been told this by the provider of your login account. Now select a login \i{protocol} to use, from the \q{Connection type} -buttons. For a login session, you should select \i{Telnet}, -\i{Rlogin} or \i{SSH}. See \k{which-one} for a description of the -differences between the three protocols, and advice on which one to -use. The fourth protocol, \I{raw protocol}\e{Raw}, is not used for -interactive login sessions; you would usually use this for debugging -other Internet services (see \k{using-rawprot}). The fifth option, -\e{Serial}, is used for connecting to a local serial line, and works -somewhat differently: see \k{using-serial} for more information on -this. +controls. For a login session, you should select \i{SSH}, \i{Telnet}, +\i{Rlogin}, or \i{SUPDUP}. See \k{which-one} for a description of the +differences between these protocols, and advice on which one to +use. The \I{raw protocol}\e{Raw} protocol is not used for interactive +login sessions; you would usually use this for debugging other Internet +services (see \k{using-rawprot}). The \e{Serial} option is used for +connecting to a local serial line, and works somewhat differently: +see \k{using-serial} for more information on this. +\#{FIXME: describe bare ssh-connection} When you change the selected protocol, the number in the \q{Port} box will change. This is normal: it happens because the various @@ -37,7 +37,7 @@ provides login services on a non-standard port, your system administrator should have told you which one. (For example, many \i{MUDs} run Telnet service on a port other than 23.) -Once you have filled in the \q{Host Name}, \q{Protocol}, and +Once you have filled in the \q{Host Name}, \q{Connection type}, and possibly \q{Port} settings, you are ready to connect. Press the \q{Open} button at the bottom of the dialog box, and PuTTY will begin trying to connect you to the server. @@ -50,17 +50,15 @@ section. If you are using SSH to connect to a server for the first time, you will probably see a message looking something like this: -\c The server's host key is not cached in the registry. You -\c have no guarantee that the server is the computer you -\c think it is. -\c The server's rsa2 key fingerprint is: -\c ssh-rsa 1024 7b:e5:6f:a7:f4:f9:81:62:5c:e3:1f:bf:8b:57:6c:5a -\c If you trust this host, hit Yes to add the key to -\c PuTTY's cache and carry on connecting. -\c If you want to carry on connecting just once, without -\c adding the key to the cache, hit No. -\c If you do not trust this host, hit Cancel to abandon the -\c connection. +\c The server's host key is not cached in the registry. You have no +\c guarantee that the server is the computer you think it is. +\c The server's ssh-ed25519 key fingerprint is: +\c ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w +\c If you trust this host, press "Accept" to add the key to PuTTY's +\c cache and carry on connecting. +\c If you want to carry on connecting just once, without adding the key +\c to the cache, press "Connect Once". +\c If you do not trust this host, press "Cancel" to abandon the connection. This is a feature of the SSH protocol. It is designed to protect you against a network attack known as \i\e{spoofing}: secretly @@ -83,7 +81,8 @@ server, it checks that the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you type any private information (such as a -password) into it. +password) into it. (See \k{errors-hostkey-wrong} for what that looks +like.) However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the @@ -97,10 +96,26 @@ network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the Internet), you should check with your system administrator, perhaps -by telephone or in person. (Many servers have more than one -host key. If the system administrator sends you more than one -\I{host key fingerprint}fingerprint, you should make sure the one -PuTTY shows you is on the list, but it doesn't matter which one it is.) +by telephone or in person. (When verifying the fingerprint, be careful +with letters and numbers that can be confused with each other: +\c{0}/\c{O}, \c{1}/\c{I}/\c{l}, and so on.) + +Many servers have more than one host key. If the system administrator +sends you more than one \I{host key fingerprint}fingerprint, you should +make sure the one PuTTY shows you is on the list, but it doesn't matter +which one it is. + +If you don't have any fingerprints that look like the example +(\I{SHA256 fingerprint}\c{SHA256:} followed by a long string of +characters), but instead have pairs of characters separated by colons +like \c{a4:db:96:a7:...}, try pressing the \q{More info...} button and +see if you have a fingerprint matching the \q{\i{MD5 fingerprint}} +there. This is an older and less secure way to summarise the same +underlying host key; it's possible for an attacker to create their +own host key with the same fingerprint; so you should avoid relying on +this fingerprint format unless you have no choice. The +\q{More info...} dialog box also shows the full host public key, in +case that is easier to compare than a fingerprint. See \k{config-ssh-hostkey} for advanced options for managing host keys. diff --git a/doc/index.but b/doc/index.but index cd68d50..f7e7145 100644 --- a/doc/index.but +++ b/doc/index.but @@ -20,6 +20,12 @@ \IM{host key fingerprint} host key fingerprint (SSH) \IM{host key fingerprint} SSH host key fingerprint +\IM{MD5 fingerprint} MD5 fingerprint, of SSH host key +\IM{MD5 fingerprint} fingerprint, MD5, of SSH host key + +\IM{SHA256 fingerprint} SHA-256 fingerprint, of SSH host key +\IM{SHA256 fingerprint} fingerprint, SHA-256, of SSH host key + \IM{manually configuring host keys} manually configuring host keys \IM{manually configuring host keys} overriding host keys \IM{manually configuring host keys} host keys, manually configuring @@ -40,18 +46,20 @@ \IM{different usernames}{changes of username} login names, different \IM{different usernames}{changes of username} account names, different -\IM{differences between SSH, Telnet and Rlogin} differences between -SSH, Telnet and Rlogin -\IM{differences between SSH, Telnet and Rlogin} protocols, +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} differences between +SSH, Telnet, Rlogin, and SUPDUP +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} protocols, differences between -\IM{differences between SSH, Telnet and Rlogin} SSH, differences -from Telnet and Rlogin -\IM{differences between SSH, Telnet and Rlogin} Telnet, differences -from SSH and Rlogin -\IM{differences between SSH, Telnet and Rlogin} Rlogin, differences -from SSH and Telnet -\IM{differences between SSH, Telnet and Rlogin} selecting a protocol -\IM{differences between SSH, Telnet and Rlogin} choosing a protocol +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} SSH, differences +from other protocols +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} Telnet, differences +from other protocols +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} Rlogin, differences +from other protocols +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} SUPDUP, differences +from other protocols +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} selecting a protocol +\IM{differences between SSH, Telnet, Rlogin, and SUPDUP} choosing a protocol \IM{MUD}{MUDs} MUDs @@ -187,6 +195,11 @@ saved sessions from \IM{protocol selection} selecting a protocol \IM{protocol selection} choosing a protocol +\IM{ssh-connection} bare \cw{ssh-connection} protocol +\IM{ssh-connection} \cw{ssh-connection} protocol, bare + +\IM{psusan} \cq{psusan} program + \IM{login name}{username} login name \IM{login name}{username} user name \IM{login name}{username} account name @@ -218,7 +231,9 @@ saved sessions from \IM{-telnet} \c{-telnet} command-line option \IM{-raw} \c{-raw} command-line option \IM{-rlogin} \c{-rlogin} command-line option +\IM{-supdup} \c{-supdup} command-line option \IM{-ssh} \c{-ssh} command-line option +\IM{-ssh-connection} \c{-ssh-connection} command-line option \IM{-serial} \c{-serial} command-line option \IM{-cleanup} \c{-cleanup} command-line option \IM{-load} \c{-load} command-line option @@ -262,6 +277,11 @@ saved sessions from \IM{PPK} \cw{PPK} file \IM{PPK} private key file, PuTTY +\IM{Argon2} Argon2 passphrase hashing function + +\IM{passphrase hashing} passphrase hashing, for private key files +\IM{passphrase hashing} password hashing, for private key files + \IM{PGP key fingerprint} PGP key fingerprint \IM{PGP key fingerprint} fingerprint, of PGP key @@ -795,6 +815,9 @@ saved sessions from \IM{DSA} DSA \IM{DSA} Digital Signature Standard +\IM{EdDSA} EdDSA +\IM{EdDSA} Edwards-curve DSA + \IM{public-key algorithm} public-key algorithm \IM{public-key algorithm} asymmetric key algorithm \IM{public-key algorithm} algorithm, public-key @@ -805,6 +828,12 @@ saved sessions from \IM{generating keys} public keys, generating \IM{generating keys} private keys, generating +\IM{probable primes} probable primes +\IM{probable primes} primes, probable + +\IM{proven primes} proven primes +\IM{proven primes} primes, proven + \IM{authorized_keys file}{authorized_keys} \cw{authorized_keys} file \IM{key fingerprint} fingerprint, of SSH authentication key @@ -897,3 +926,7 @@ saved sessions from \IM{PuTTY icon} PuTTY icon \IM{PuTTY icon} icon, PuTTY's \IM{PuTTY icon} logo, PuTTY's + +\IM{system tray} system tray, Windows +\IM{system tray} notification area, Windows (aka system tray) +\IM{system tray} taskbar notification area, Windows (aka system tray) diff --git a/doc/index.html b/doc/index.html index 2bbae29..4906782 100644 --- a/doc/index.html +++ b/doc/index.html @@ -17,14 +17,14 @@

      PuTTY User Manual

      Note to Unix users: this manual currently primarily documents the Windows versions of the PuTTY utilities. Some options are therefore mentioned that are absent from the Unix version; the Unix version has features not described here; and the pterm and command-line puttygen and pageant utilities are not described at all. The only Unix-specific documentation that currently exists is the man pages.

      -This manual is copyright 1997-2019 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix C for the licence text in full. +This manual is copyright 1997-2021 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix D for the licence text in full.


      If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

      -[PuTTY release 0.73]
      +[PuTTY release 0.76] diff --git a/doc/intro.but b/doc/intro.but index 5d4c3f7..7c66b65 100644 --- a/doc/intro.but +++ b/doc/intro.but @@ -1,43 +1,45 @@ \C{intro} Introduction to PuTTY -PuTTY is a free SSH, Telnet and Rlogin client for Windows +PuTTY is a free SSH, Telnet, Rlogin, and SUPDUP client for Windows systems. -\H{you-what} What are SSH, Telnet and Rlogin? +\H{you-what} What are SSH, Telnet, Rlogin, and SUPDUP? -If you already know what SSH, Telnet and Rlogin are, you can safely -skip on to the next section. +If you already know what SSH, Telnet, Rlogin, and SUPDUP are, you can +safely skip on to the next section. -SSH, Telnet and Rlogin are three ways of doing the same thing: +SSH, Telnet, Rlogin, and SUPDUP are four ways of doing the same thing: logging in to a multi-user computer from another computer, over a network. -Multi-user operating systems, such as Unix and VMS, usually present -a \i{command-line interface} to the user, much like the \q{\i{Command -Prompt}} or \q{\i{MS-DOS Prompt}} in Windows. The system prints a -prompt, and you type commands which the system will obey. +Multi-user operating systems, typically of the Unix family (such as +Linux, MacOS, and the BSD family), usually present a \i{command-line +interface} to the user, much like the \q{\i{Command Prompt}} or +\q{\i{MS-DOS Prompt}} in Windows. The system prints a prompt, and you +type commands which the system will obey. Using this type of interface, there is no need for you to be sitting at the same machine you are typing commands to. The commands, and responses, can be sent over a network, so you can sit at one computer and give commands to another one, or even to more than one. -SSH, Telnet and Rlogin are \i\e{network protocols} that allow you to -do this. On the computer you sit at, you run a \i\e{client}, which -makes a network connection to the other computer (the \i\e{server}). -The network connection carries your keystrokes and commands from the -client to the server, and carries the server's responses back to -you. +SSH, Telnet, Rlogin, and SUPDUP are \i\e{network protocols} that allow +you to do this. On the computer you sit at, you run a \i\e{client}, +which makes a network connection to the other computer (the +\i\e{server}). The network connection carries your keystrokes and +commands from the client to the server, and carries the server's +responses back to you. These protocols can also be used for other types of keyboard-based interactive session. In particular, there are a lot of bulletin boards, \i{talker systems} and \i{MUDs} (Multi-User Dungeons) which support access using Telnet. There are even a few that support SSH. -You might want to use SSH, Telnet or Rlogin if: +You might want to use SSH, Telnet, Rlogin, or SUPDUP if: -\b you have an account on a Unix or VMS system which you want to be -able to access from somewhere else +\b you have an account on a Unix system (or some other multi-user OS +such as VMS or ITS) which you want to be able to access from somewhere +else \b your Internet Service Provider provides you with a login account on a \i{web server}. (This might also be known as a \i\e{shell account}. @@ -47,22 +49,22 @@ your commands for you.) \b you want to use a \i{bulletin board system}, talker or MUD which can be accessed using Telnet. -You probably do \e{not} want to use SSH, Telnet or Rlogin if: +You probably do \e{not} want to use SSH, Telnet, Rlogin, or SUPDUP if: \b you only use Windows. Windows computers have their own ways of networking between themselves, and unless you are doing something fairly unusual, you will not need to use any of these remote login protocols. -\H{which-one} How do SSH, Telnet and Rlogin differ? +\H{which-one} How do SSH, Telnet, Rlogin, and SUPDUP differ? -This list summarises some of the \i{differences between SSH, Telnet -and Rlogin}. +This list summarises some of the \i{differences between SSH, Telnet, +Rlogin, and SUPDUP}. \b SSH (which stands for \q{\i{secure shell}}) is a recently designed, high-security protocol. It uses strong cryptography to protect your -connection against eavesdropping, hijacking and other attacks. Telnet -and Rlogin are both older protocols offering minimal security. +connection against eavesdropping, hijacking and other attacks. Telnet, +Rlogin, and SUPDUP are all older protocols offering minimal security. \b SSH and Rlogin both allow you to \I{passwordless login}log in to the server without having to type a password. (Rlogin's method of doing this is @@ -82,5 +84,5 @@ doesn't support SSH, it might be worth trying to persuade the administrator to install it. If your client and server are both behind the same (good) firewall, -it is more likely to be safe to use Telnet or Rlogin, but we still -recommend you use SSH. +it is more likely to be safe to use Telnet, Rlogin, or SUPDUP, but we +still recommend you use SSH. diff --git a/doc/licence.but b/doc/licence.but index d425575..f317cb8 100644 --- a/doc/licence.but +++ b/doc/licence.but @@ -3,9 +3,9 @@ \A{licence} PuTTY \ii{Licence} -PuTTY is \i{copyright} 1997-2019 Simon Tatham. +PuTTY is \i{copyright} 1997-2021 Simon Tatham. -Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, and CORE SDI S.A. +Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \q{Software}), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/doc/man-pag.but b/doc/man-pageant.but similarity index 62% rename from doc/man-pag.but rename to doc/man-pageant.but index 575b729..358f3a0 100644 --- a/doc/man-pag.but +++ b/doc/man-pageant.but @@ -8,18 +8,18 @@ \S{pageant-manpage-synopsis} SYNOPSIS -\c pageant ( -X | -T | --permanent | --debug ) [ key-file... ] -\e bbbbbbb bb bb bbbbbbbbbbb bbbbbbb iiiiiiii -\c pageant [ key-file... ] --exec command [ args... ] -\e bbbbbbb iiiiiiii bbbbbb iiiiiii iiii -\c pageant -a key-file... -\e bbbbbbb bb iiiiiiii -\c pageant ( -d | --public | --public-openssh ) key-identifier... -\e bbbbbbb bb bbbbbbbb bbbbbbbbbbbbbbbb iiiiiiiiiiiiii -\c pageant -D -\e bbbbbbb bb -\c pageant -l -\e bbbbbbb bb +\c pageant ( -X | -T | --permanent | --debug ) [ [ --encrypted ] key-file... ] +\e bbbbbbb bb bb bbbbbbbbbbb bbbbbbb bbbbbbbbbbb iiiiiiii +\c pageant [ [ --encrypted ] key-file... ] --exec command [ args... ] +\e bbbbbbb bbbbbbbbb iiiiiiii bbbbbb iiiiiii iiii +\c pageant -a [ --encrypted ] key-file... +\e bbbbbbb bb bbbbbbbbbbb iiiiiiii +\c pageant ( -d | -r | --public | --public-openssh ) key-identifier... +\e bbbbbbb bb bb bbbbbbbb bbbbbbbbbbbbbbbb iiiiiiiiiiiiii +\c pageant ( -D | -R ) +\e bbbbbbb bb bb +\c pageant -l [ --fptype format ] +\e bbbbbbb bb bbbbbbbb iiiiii \c pageant --askpass prompt \e bbbbbbb bbbbbbbbb iiiiii @@ -41,7 +41,8 @@ extract their public half. The agent protocol used by \c{pageant} is compatible with the PuTTY tools and also with other implementations such as OpenSSH's SSH client -and \e{ssh-agent(1)}. +and \e{ssh-agent(1)}. Some \c{pageant} features are implemented with +protocol extensions, so will only work if \c{pageant} is on both ends. To run \c{pageant} as an agent, you must provide an option to tell it what its \e{lifetime} should be. Typically you would probably want @@ -75,18 +76,32 @@ extra command-line arguments, e.g. \c eval $(pageant -T ~/.ssh/key.ppk) \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb -in which case Pageant will prompt for the keys' passphrases (if any) -and start the agent with those keys already loaded. Passphrase prompts -will use the controlling terminal if one is available, or failing that -the GUI if one of those is available. (The prompt method can be -overridden with the \cw{--gui-prompt} or \cw{--tty-prompt} options.) -If neither is available, no passphrase prompting can be done. +in which case Pageant will immediately prompt for the keys' passphrases +(if any) and start the agent with those keys already loaded in +cleartext form. Passphrase prompts will use the controlling terminal if +one is available, or failing that the GUI if one of those is available. +(The prompt method can be overridden with the \cw{--gui-prompt} or +\cw{--tty-prompt} options.) If neither is available, no passphrase +prompting can be done. + +Alternatively, you can start an agent with keys stored in encrypted +form: + +\c eval $(pageant -T --encrypted ~/.ssh/key.ppk) +\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + +In this case, Pageant will not prompt for a passphrase at startup; +instead, it will prompt the first time a client tries to use the key. +(Pageant will need access to a GUI so that it can pop up a passphrase +prompt when required, unless it's running in \cw{--debug} mode.) To use Pageant to talk to an existing agent, you can add new keys using \cw{-a}, list the current set of keys' fingerprints and comments with \cw{-l}, extract the full public half of any key using -\cw{--public} or \cw{--public-openssh}, delete a key using \cw{-d}, or -delete all keys using \cw{-D}. +\cw{--public} or \cw{--public-openssh}, delete a specific key or +all keys using \cw{-d} or \cw{-D} respectively, or request +re-encryption of a specific key or all keys using \cw{-r} or \cw{-R} +respectively. \S{pageant-manpage-lifetime} LIFETIME @@ -163,7 +178,8 @@ before it manages to happen. \dd Pageant will run in the foreground, without forking. It will print its environment variable setup commands on standard output, and then it -will log all agent activity to standard output as well. This is useful +will log all agent activity to standard output as well; any passphrase +prompts will need to be answered on standard input. This is useful for debugging what Pageant itself is doing, or what another process is doing to it. @@ -175,20 +191,27 @@ already have set. \dt \cw{-a} \e{key-files} -\dd Load the specified private key file(s), decrypt them if necessary -by prompting for their passphrases (with the same choice of user -interfaces as in agent mode), and add them to the already-running agent. +\dd Load the specified private key file(s) and add them to the +already-running agent. Unless \cw{--encrypted} is also specified, +\c{pageant} will decrypt them if necessary by prompting for their +passphrases (with the same choice of user interfaces as in agent +mode). \lcont{ - The private key files must be in PuTTY's \cw{.ppk} file format. - } \dt \cw{-l} \dd List the keys currently in the running agent. Each key's -fingerprint and comment string will be shown. +fingerprint and comment string will be shown. (Use the \cw{-E} +option to change the fingerprint format.) + +\lcont{ +Keys that will require a passphrase on their next use are listed as +\q{encrypted}. Keys that can be returned to this state with \cw{-r} +are listed as \q{re-encryptable}. +} \dt \cw{--public} \e{key-identifiers} @@ -205,8 +228,8 @@ in \cw{.ppk} format) or just its public half. \b The key's comment string, as shown by \cw{pageant -l}. -\b Enough hex digits of the key's fingerprint to be unique among keys -currently loaded into the agent. +\b Enough of one of the key's fingerprint formats to be unique among +keys currently loaded into the agent. If Pageant can uniquely identify one key by interpreting the \e{key-identifier} in any of these ways, it will assume that key was @@ -214,9 +237,24 @@ the one you meant. If it cannot, you will have to specify more detail. If you find that your desired \e{key-identifier} string can be validly interpreted as more than one of the above \e{kinds} of identification, -you can disambiguate by prefixing it with \cq{file:}, \cq{comment:} or -\cq{fp:} to indicate that it is a filename, comment string or -fingerprint prefix respectively. +you can disambiguate by prefixing it as follows: + +\dt \cq{file:} + +\dd to indicate that it is a filename + +\dt \cq{comment:} + +\dd to indicate that it is a comment string + +\dt \cq{fp:} + +\dd to indicate that it is a fingerprint; any fingerprint format will +be matched + +\dt \cq{sha256:} or \cq{md5:} + +\dd to indicate that it is a fingerprint of a specific format } @@ -237,6 +275,44 @@ using \cw{pageant -a}. \dd Delete all keys from the agent's memory, leaving it completely empty. +\dt \cw{-r} \e{key-identifiers} + +\dd \q{Re-encrypt} each specified key in the agent's memory - +that is, forget any cleartext version, so that the user will be +prompted for a passphrase again next time the key is used. +(For this to be possible, the key must previously have been added +with the \cw{--encrypted} option.) + +\lcont{ +(Holding encrypted keys is a Pageant extension, so this option and +\cw{-R} are unlikely to work with other agents.) +} + +\dt \cw{-R} + +\dd \q{Re-encrypt} all possible keys in the agent's memory. +(This may leave some keys in cleartext, if they were not previously +added with the \cw{--encrypted} option.) + +\dt \cw{--test-sign} \e{key-identifier} + +\dt \cw{--test-sign-with-flags=}\e{flags} \e{key-identifier} + +\dd Sign arbitrary data with the given key. This mode is only likely +to be useful when testing \c{pageant} itself. + +\lcont{ + +The data to sign is taken from standard input, signed by the agent +with the key identified by \e{key-identifier}, and the resulting +signature emitted on standard output (as a binary blob in the format +defined by the SSH specifications). + +\e{flags} is a number representing a combination of flag bits defined +by the SSH agent protocol. + +} + \S{pageant-manpage-askpass} SSH-ASKPASS REPLACEMENT \dt \cw{--askpass} \e{prompt} @@ -292,13 +368,48 @@ respectively. If neither option is given, Pageant will guess based on whether the environment variable \cw{SHELL} has a value ending in \cq{csh}. +\dt \cw{--symlink} \e{fixed-path} + +\dd When operating in agent mode, as well as creating a uniquely named +listening socket, \c{pageant} will also create (or update) a symbolic +link at \e{fixed-path} pointing to that socket. + +\lcont{ +This allows access to an agent instance by setting the +\c{SSH_AUTH_SOCK} environment variable to \e{fixed-path}, rather than +having to use the value invented by \c{pageant} when it starts. It's +mainly expected to be useful for debugging. +} + +\dt \cw{--encrypted}, \cw{--no-decrypt} + +\dd When adding keys to the agent (at startup or later), keep them +in encrypted form until the first attempt to use them; the user will +be prompted for a passphrase then. Once decrypted, a key that was +added in this way can be \q{re-encrypted} with the \cw{-r} or \cw{-R} +client options. + +\lcont{ +The \cw{--encrypted} option makes no difference for key files which +do not have a passphrase. + +(Storing keys in encrypted form is a Pageant extension; other agent +implementations are unlikely to support it.) +} + +\dt \cw{-E} \e{fingerprint-type}, \cw{--fptype} \e{fingerprint-type} + +\dd Specify the fingerprint format to print. Only applicable when +listing fingerprints with \cw{-l}. The available formats are +\cw{sha256} (the default) and \cw{md5}. + \dt \cw{--gui-prompt}, \cw{--tty-prompt} \dd Force Pageant to prompt for key passphrases with a particular method (GUI or terminal) rather than trying to guess the most appropriate method as described above. (These options are relevant -whenever an encrypted key filename is specified to \c{pageant}, -and in \c{--askpass} mode.) +whenever a key file is specified to \c{pageant} that needs +immediate decryption, and in \c{--askpass} mode.) \dt \cw{--help} diff --git a/doc/man-pl.but b/doc/man-plink.but similarity index 87% rename from doc/man-pl.but rename to doc/man-plink.but index 179a926..26e65f7 100644 --- a/doc/man-pl.but +++ b/doc/man-plink.but @@ -56,6 +56,13 @@ to aid in verifying new files released by the PuTTY team. \dd Force serial mode. +\dt \cw{-ssh-connection} + +\dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is +only likely to be useful when connecting to a \e{psusan(1)} server, +most likely with an absolute path to a Unix-domain socket in place +of \e{host}. + \dt \cw{\-proxycmd} \e{command} \dd Instead of making a TCP connection, use \e{command} as a proxy; @@ -196,6 +203,15 @@ which of the agent's keys to use. } \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +\dt \cw{\-no\-trivial\-auth} + +\dd Disconnect from any SSH server which accepts authentication without +ever having asked for any kind of password or signature or token. (You +might want to enable this for a server you always expect to challenge +you, for instance to ensure you don't accidentally type your key file's +passphrase into a compromised server spoofing Plink's passphrase +prompt.) + \dt \cw{\-noshare} \dd Don't test and try to share an existing connection, always make @@ -208,8 +224,9 @@ a new connection. \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified -multiple times; each key can be either a fingerprint (\cw{99:aa:bb:...}) or -a base64-encoded blob in OpenSSH's one-line format. +multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, +\cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line +format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be @@ -267,6 +284,16 @@ an effort is made to suppress obvious passwords.) encrypted packet data. } +\dt \cw{\-logoverwrite} + +\dd If Plink is configured to write to a log file that already exists, +discard the existing file. + +\dt \cw{\-logappend} + +\dd If Plink is configured to write to a log file that already exists, +append new log data to the existing file. + \dt \cw{\-shareexists} \dd Instead of making a new connection, test for the presence of an diff --git a/doc/man-pscp.but b/doc/man-pscp.but index 515e541..60ce4f5 100644 --- a/doc/man-pscp.but +++ b/doc/man-pscp.but @@ -115,6 +115,19 @@ commands such as \q{\c{w}}). \dd Force use of SSH protocol version 2. +\dt \cw{-ssh-connection} + +\dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is +only likely to be useful when connecting to a \e{psusan(1)} server, +most likely with an absolute path to a Unix-domain socket in place +of \e{host}. + +\dt \cw{-ssh} + +\dd Force use of the SSH protocol. (This is usually not needed; it's +only likely to be useful if you need to override some other +configuration of the \q{bare \cw{ssh-connection}} protocol.) + \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. @@ -142,11 +155,20 @@ which of the agent's keys to use. } \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +\dt \cw{\-no\-trivial\-auth} + +\dd Disconnect from any SSH server which accepts authentication without +ever having asked for any kind of password or signature or token. (You +might want to enable this for a server you always expect to challenge +you, for instance to ensure you don't accidentally type your key file's +passphrase into a compromised server spoofing PSCP's passphrase prompt.) + \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified -multiple times; each key can be either a fingerprint (\cw{99:aa:bb:...}) or -a base64-encoded blob in OpenSSH's one-line format. +multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, +\cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line +format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be @@ -176,6 +198,16 @@ to suppress obvious passwords.) encrypted packet data. } +\dt \cw{\-logoverwrite} + +\dd If PSCP is configured to write to a log file that already exists, +discard the existing file. + +\dt \cw{\-logappend} + +\dd If PSCP is configured to write to a log file that already exists, +append new log data to the existing file. + \S{pscp-manpage-more-information} MORE INFORMATION For more information on \cw{pscp} it's probably best to go and look at diff --git a/doc/man-psft.but b/doc/man-psftp.but similarity index 78% rename from doc/man-psft.but rename to doc/man-psftp.but index c1329d0..5261729 100644 --- a/doc/man-psft.but +++ b/doc/man-psftp.but @@ -103,6 +103,19 @@ commands such as \q{\c{w}}). \dd Force use of SSH protocol version 2. +\dt \cw{-ssh-connection} + +\dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is +only likely to be useful when connecting to a \e{psusan(1)} server, +most likely with an absolute path to a Unix-domain socket in place +of \e{host}. + +\dt \cw{-ssh} + +\dd Force use of the SSH protocol. (This is usually not needed; it's +only likely to be useful if you need to override some other +configuration of the \q{bare \cw{ssh-connection}} protocol.) + \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. @@ -130,11 +143,21 @@ which of the agent's keys to use. } \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +\dt \cw{\-no\-trivial\-auth} + +\dd Disconnect from any SSH server which accepts authentication without +ever having asked for any kind of password or signature or token. (You +might want to enable this for a server you always expect to challenge +you, for instance to ensure you don't accidentally type your key file's +passphrase into a compromised server spoofing PSFTP's passphrase +prompt.) + \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified -multiple times; each key can be either a fingerprint (\cw{99:aa:bb:...}) or -a base64-encoded blob in OpenSSH's one-line format. +multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, +\cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line +format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be @@ -156,6 +179,16 @@ to suppress obvious passwords.) encrypted packet data. } +\dt \cw{\-logoverwrite} + +\dd If PSFTP is configured to write to a log file that already exists, +discard the existing file. + +\dt \cw{\-logappend} + +\dd If PSFTP is configured to write to a log file that already exists, +append new log data to the existing file. + \S{psftp-manpage-commands} COMMANDS For a list of commands available inside \cw{psftp}, type \cw{help} diff --git a/doc/man-psocks.but b/doc/man-psocks.but new file mode 100644 index 0000000..a9792e4 --- /dev/null +++ b/doc/man-psocks.but @@ -0,0 +1,92 @@ +\cfg{man-identity}{psocks}{1}{2021-04-08}{PuTTY tool suite}{PuTTY tool suite} + +\H{psocks-manpage} Man page for \cw{psocks} + +\S{psocks-manpage-name} NAME + +\cw{psocks} \- simple SOCKS proxy server + +\S{psocks-manpage-synopsis} SYNOPSIS + +\c psocks [ -d ] [ -f | -p pipe-cmd ] [ -g ] [ port-number ] +\e bbbbbb bb bb bb iiiiiiii bb iiiiiiiiiii + +\S{psocks-manpage-description} DESCRIPTION + +\cw{psocks} is a simple SOCKS4/5 proxy server. It supports proxying +IPv4 and IPv6 connections. It does not support requiring +authentication of its clients. + +\cw{psocks} can be used together with an SSH client such as +\cw{putty(1)} to implement a reverse dynamic SSH tunnel. It can also +be used for network protocol debugging, as it can record all the +traffic passing through it in various ways. + +By default, \cw{psocks} listens to connections from localhost only, +on TCP port 1080. A different \e{port-number} can optionally be +supplied, and with \cw{-g} it will listen to connections from any +host. + +\cw{psocks} will emit log messages about connections it receives on +standard error. With \cw{-d}, it will log the contents of those +connections too. + +\S{psocks-manpage-options} OPTIONS + +The command-line options supported by \cw{psocks} are: + +\dt \cw{-g} + +\dd Accept connections from anywhere. By default, \cw{psocks} only +accepts connections on the loopback interface. + +\dt \cw{--exec} \e{command} + +\dd \cw{psocks} will run the provided command as a subprocess. When +the subprocess terminates, \cw{psocks} will terminate as well. + +\lcont{ + +All arguments on the \cw{psocks} command line after \cw{--exec} will be +treated as part of the command to run, even if they look like other +valid \cw{psocks} options. + +} + +\dt \cw{-d} + +\dd Log all traffic to standard error, in a more or less human-readable +form (in addition to messages about connections being opened and +closed, which are always logged). + +\dt \cw{-f} + +\dd Record all traffic to files. For every incoming connection, two +files are created, \cw{sockout.NNNN} and \cw{sockin.NNNN}, where +\e{NNNN} is a decimal index starting at 0 identifying the proxied +connection. These record, respectively, traffic from the SOCKS client, +and from the server it connected to through the proxy. + +\dt \cw{-p} \e{pipe-cmd} + +\dd Pipe all traffic to a command. For every incoming connection, +\e{pipe-cmd} is invoked twice: + +\lcont{ +\c pipe-cmd out N +\e iiiiiiii bbb i +\c pipe-cmd in N +\e iiiiiiii bb i + +Each command will run for the direction of a proxied connection, and +have the connection's traffic piped into it, similar to \cw{-f}. +} + +\S{psocks-manpage-examples} EXAMPLES + +In combination with the \e{plink(1)} SSH client, to set up a reverse +dynamic SSH tunnel, in which the remote listening port 1080 on +remote host \cw{myhost} acts as a SOCKS server giving access to your +local network: + +\c psocks 12345 --exec plink -R 1080:localhost:12345 user@myhost diff --git a/doc/man-psusan.but b/doc/man-psusan.but new file mode 100644 index 0000000..fa986e8 --- /dev/null +++ b/doc/man-psusan.but @@ -0,0 +1,375 @@ +\cfg{man-identity}{psusan}{1}{2020-12-13}{PuTTY tool suite}{PuTTY tool suite} + +\H{psusan-manpage} Man page for \cw{psusan} + +\S{psusan-manpage-name} NAME + +\cw{psusan} \- pseudo-SSH for untappable, separately authenticated networks + +\S{psusan-manpage-synopsis} SYNOPSIS + +\c psusan [ options ] +\e bbbbbb iiiiiii + +\S{psusan-manpage-description} DESCRIPTION + +\cw{psusan} is a server program that behaves like the innermost +\q{connection} layer of an SSH session, without the two outer security +layers of encryption and authentication. It provides all the +post-authentication features of an SSH connection: + +\b choosing whether to run an interactive terminal session or a single +specified command + +\b multiple terminal sessions at once (or a mixture of those and +specified commands) + +\b SFTP file transfer + +\b all the standard SSH port-forwarding options + +\b X11 forwarding + +\b SSH agent forwarding + +The catch is that, because it lacks the outer layers of SSH, you have +to run it over some kind of data channel that is already authenticated +as the right user, and that is already protected to your satisfaction +against eavesdropping and session hijacking. A good rule of thumb is +that any channel that you were prepared to run a \e{bare} shell +session over, you can run \cw{psusan} over instead, which adds all the +above conveniences without changing the security properties. + +The protocol that \cw{psusan} speaks is also spoken by PuTTY, Plink, +PSCP, and PSFTP, if you select the protocol type \q{Bare ssh-connection} +or the command-line option \cw{-ssh-connection} and specify the +absolute path to the appropriate Unix-domain socket in place of +a hostname. + +\S{psusan-manpage-examples} EXAMPLES + +The idea of a secure, pre-authenticated data channel seems strange to +people thinking about \e{network} connections. But there are lots of +examples within the context of a single Unix system, and that's where +\cw{psusan} is typically useful. + +\S2{psusan-manpage-examples-docker} Docker + +A good example is the console or standard I/O channel leading into a +container or virtualisation system. Docker is a familiar example. If +you want to start a Docker container and run a shell directly within +it, you might say something like + +\c docker run -i -t some:image +\e iiiiiiiiii + +which will allow you to run a single shell session inside the +container, in the same terminal you started Docker from. + +Suppose that you'd prefer to run \e{multiple} shell sessions in the +same container at once (perhaps so that one of them can use debugging +tools to poke at what another is doing). And perhaps inside that +container you're going to run a program that you don't trust with full +access to your network, but are prepared to let it make one or two +specific network connections of the kind you could set up with an SSH +port forwarding. + +In that case, you could remove the \cw{-t} option from that Docker +command line (which means \q{allocate a terminal device}), and tell it +to run \cw{psusan} inside the container: + +\c docker run -i some:image /some/path/to/psusan +\e iiiiiiiiii iiiiiiiiiiii + +(Of course, you'll need to ensure that \cw{psusan} is installed +somewhere inside the container image.) + +If you do that from a shell command line, you'll see a banner line +looking something like this: + +\c SSHCONNECTION@putty.projects.tartarus.org-2.0-PSUSAN_Release_0.75 + +which isn't particularly helpful except that it tells you that +\cw{psusan} has started up successfully. + +To talk to this server \e{usefully}, you can set up a PuTTY saved +session as follows: + +\b Set the protocol to \q{Bare ssh-connection} (the \cw{psusan} +protocol). + +\b Write \e{something} in the hostname box. It will appear in PuTTY's +window title (if you run GUI PuTTY), so you might want to write +something that will remind you what kind of window it is. If you have +no opinion, something generic like \cq{dummy} will do. + +\b In the \q{Proxy} configuration panel, set the proxy type to +\q{Local}, and enter the above \cq{docker run} command in the +\q{Telnet command, or local proxy command} edit box. + +\b In the \q{SSH} configuration panel, you will very likely want to +turn on connection sharing. (See below.) + +This arranges that when PuTTY starts up, it will run the Docker +command as shown above in place of making a network connection, and +talk to that command using the \cw{psusan} SSH-like protocol. + +The effect is that you will still get a shell session in the context +of a Docker container. But this time, it's got all the SSH amenities. +If you also turn on connection sharing in the \q{SSH} configuration +panel, then the \q{Duplicate Session} option will get you a second +shell in the \e{same} Docker container (instead of a primary shell in +a separate instance). You can transfer files in and out of the +container while it's running using PSCP or PSFTP; you can forward +network ports, X11 programs, and/or an SSH agent to the container. + +Of course, another way to do all of this would be to run the \e{full} +SSH protocol over the same channel. This involves more setup: you have +to invent an SSH host key for the container, accept it in the client, +and deal with it being left behind in your client's host key cache +when the container is discarded. And you have to set up some login +details in the container: either configure a password, and type it in +the client, or copy in the public half of some SSH key you already +had. And all this inconvenience is \e{unnecessary}, because these are +all precautions you need to take when the connection between two +systems is going over a hostile network. In this case, it's only going +over a kernel IPC channel that's guaranteed to go to the right place, +so those safety precautions are redundant, and they only add +awkwardness. + +\S2{psusan-manpage-examples-uml} User-mode Linux + +User-mode Linux is another container type you can talk to in the same +way. Here's a small worked example. + +The \e{easiest} way to run UML is to use its \cq{hostfs} file system +type to give the guest kernel access to the same virtual filesystem as +you have on the host. For example, a command line like this gets you a +shell prompt inside a UML instance sharing your existing filesystem: + +\c linux mem=512M rootfstype=hostfs rootflags=/ rw init=/bin/bash + +If you run this at a command line (assuming you have a UML kernel +available on your path under the name \cq{linux}), then you should see +a lot of kernel startup messages, followed by a shell prompt along the +lines of + +\c root@(none):/# + +To convert this into a \cw{psusan}-based UML session, we need to +adjust the command line so that instead of running \cw{bash} it runs +\cw{psusan}. But running \cw{psusan} directly isn't quite enough, +because \cw{psusan} will depend on a small amount of setup, such as +having \cw{/proc} mounted. So instead, we set the init process to a +shell script which will do the necessary setup and \e{then} invoke +\cw{psusan}. + +Also, running \cw{psusan} directly over the UML console device is a +bad idea, because then the \cw{psusan} binary protocol will be mixed +with textual console messages. So a better plan is to redirect UML's +console to the standard error of the \cw{linux} process, and map its +standard input and output to a serial port. So the replacement UML +command line might look something like this: + +\c linux mem=512M rootfstype=hostfs rootflags=/ rw \ +\c con=fd:2,fd:2 ssl0=fd:0,fd:1 init=/some/path/to/uml-psusan.sh +\e iiiiiiiiiiiiiiiiiiiiiiiiiii + +And the setup script \cw{uml-psusan.sh} might look like this: + +\c #!/bin/bash +\c # Set up vital pseudo-filesystems +\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +\c mount -t proc none /proc +\c mount -t devpts none /dev/pts +\c # Redirect I/O to the serial port, but stderr to the console +\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +\c exec 0<>/dev/ttyS0 1>&0 2>/dev/console +\c # Set the serial port into raw mode, to run a binary protocol +\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +\c stty raw -echo +\c # Choose what shell you want to run inside psusan +\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +\c export SHELL=/bin/bash +\c # And now run psusan over the serial port +\e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii +\c exec /home/simon/src/putty/misc/psusan + +Now set up a PuTTY saved session as in the Docker example above, using +that \cw{linux} command as the local proxy command, and you'll have a +PuTTY session that starts up a clean UML instance when you run it, and +(if you enabled connection sharing) further instances of the same +session will connect to the same instance again. + +\S2{psusan-manpage-examples-wsl} Windows Subsystem for Linux + +On Windows, the default way to use WSL is to run the \cw{wsl} program, +or one of its aliases, in a Windows console, either by launching it +from an existing command prompt, or by using a shortcut that opens it +in a fresh console. This gives you a Linux terminal environment, but +in a Windows console window. + +If you'd prefer to interact with the same environment using PuTTY as +the terminal (for example, if you prefer PuTTY's mouse shortcuts for +copy and paste), you can set it up by installing \cw{psusan} in the +Linux environment, and then setting up a PuTTY saved session that +talks to it. A nice way to do this is to use the name of the WSL +distribution as the \q{host name}: + +\b set the local proxy command to \cq{wsl -d %host +/usr/local/bin/psusan} (or wherever you installed \cw{psusan} in the +Linux system) + +\b enter the name of a particular WSL distribution in the host name +box. (For example, if you installed WSL Debian in the standard way +from the Windows store, this will just be \q{Debian}.) + +\b set the protocol to \q{Bare ssh-connection}, as usual. + +Like all the other examples here, this also permits you to forward +ports in and out of the WSL environment (e.g. expose a WSL2 network +service through the hypervisor's internal NAT), forward Pageant into +it, and so on. + +\S2{psusan-manpage-examples-schroot} \cw{schroot} + +Another example of a container-like environment is the alternative +filesystem layout set up by \cw{schroot}(\e{1}). + +\cw{schroot} is another program that defaults to running an +interactive shell session in the terminal you launched it from. But +again, you can get a \cw{psusan} connection into the \cw{schroot} +environment by setting up a PuTTY saved session whose local proxy +command is along the lines of + +\c schroot -c chroot-name /some/path/to/psusan +\e iiiiiiiiiii iiiiiiiiiiii + +Depending on how much of the chroot environment is copied from your +main one, you might find this makes it easier to (for example) run X11 +programs inside the chroot that open windows on your main X display, +or transfer files in and out of the chroot. + +\S2{psusan-manpage-examples-namespace} Between network namespaces + +If you've set up multiple network namespaces on a Linux system, with +different TCP/IP configurations, then \cw{psusan} can be a convenient +unprivileged-user gateway between them, if you run it as a non-root +user in the non-default one of your namespaces, listening for +connections on a Unix-domain socket. + +If you do that, then it gives you convenient control over which of +your outgoing network connections use which TCP/IP configuration: you +can use PuTTY to run a shell session in the context of the other +namespace if you want to run commands like \cw{ping}, or you can set +up individual port forwardings or even a SOCKS server so that +processes running in one namespace can send their network connections +via the other one. + +For this application, it's probably most convenient to use the +\cw{--listen} option in \cw{psusan}, which makes it run as a server +and listen for connections on a Unix-domain socket. Then you can enter +that socket name in PuTTY's host name configuration field (and also +still select the \q{Bare ssh-connection} protocol option), to connect +to that socket as if it were an SSH client. + +Provided the Unix-domain socket is inside a directory that only the +right user has access to, this will ensure that authentication is done +implicitly by the Linux kernel. + +\S2{psusan-manpage-examples-userv} Between user ids, via GNU userv + +If you use multiple user ids on the same machine, say for purposes of +privilege separation (running some less-trusted program with limited +abilities to access all your stuff), then you probably have a +\q{default} or most privileged account where you run your main login +session, and sometimes need to run a shell in another account. + +\cw{psusan} can be used as an access channel between the accounts, +using GNU \cw{userv}(\e{1}) as the transport. In the account you want +to access, write a \cw{userv} configuration stanza along the lines of + +\c if (glob service psusan & glob calling-user my-main-account-name) +\e iiiiiiiiiiiiiiiiiiii +\c reset +\c execute /some/path/to/psusan +\e iiiiiiiiiiii +\c fi + +This gives your main account the right to run the command + +\c userv my-sub-account-name psusan +\e iiiiiiiiiiiiiiiiiii + +and you can configure that command name as a PuTTY local proxy +command, in the same way as most of the previous examples. + +Of course, there are plenty of ways already to access one local +account from another, such as \cw{sudo}. One advantage of doing it +this way is that you don't need the system administrator to intervene +when you want to change the access controls (e.g. change which of your +accounts have access to another): as long as you have \e{some} means +of getting into each account in the first place, and \cw{userv} is +installed, you can make further configuration changes without having +to bother root about it. + +Another advantage is that it might make file transfer between the +accounts easier. If you're the kind of person who keeps your home +directories private, then it's awkward to copy a file from one of your +accounts to another just by using the \cw{cp} command, because there's +nowhere convenient that you can leave it in one account where the +other one can read it. But with \cw{psusan} over \cw{userv}, you don't +need any shared piece of filesystem: you can \cw{scp} files back and +forth without any difficulty. + +\S{psusan-manpage-options} OPTIONS + +The command-line options supported by \cw{psusan} are: + +\dt \cw{--listen} \e{unix-socket-name} + +\dd Run \cw{psusan} in listening mode. \e{unix-socket-name} is the +pathname of a Unix-domain socket to listen on. You should ensure that +this pathname is inside a directory whose read and exec permissions +are restricted to only the user(s) you want to be able to access the +environment that \cw{psusan} is running in. + +\lcont{ + +The listening socket has to be a Unix-domain socket. \cw{psusan} does +not provide an option to run over TCP/IP, because the unauthenticated +nature of the protocol would make it inherently insecure. + +} + +\dt \cw{--listen-once} + +\dd In listening mode, this option causes \cw{psusan} to listen for +only one connection, and exit immediately after that connection +terminates. + +\dt \cw{--sessiondir} \e{pathname} + +\dd This option sets the directory that shell sessions and +subprocesses will start in. By default it is \cw{psusan}'s own working +directory, but in some situations it's easier to change it with a +command-line option than by wrapping \cw{psusan} in a script that +changes directory before starting it. + +\dt \cw{-v}, \cw{--verbose} + +\dd This option causes \cw{psusan} to print verbose log messages on +its standard error. This is probably most useful in listening mode. + +\dt \cw{\-sshlog} \e{logfile} + +\dt \cw{\-sshrawlog} \e{logfile} + +\dd These options cause \cw{psusan} to log protocol details to a file, +similarly to the logging options in PuTTY and Plink. + +\lcont{ +\cw{\-sshlog} logs decoded SSH packets and other events (those that +\cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw wire +data, including the outer packet format and the initial greetings. +} diff --git a/doc/man-pter.but b/doc/man-pterm.but similarity index 100% rename from doc/man-pter.but rename to doc/man-pterm.but diff --git a/doc/man-putt.but b/doc/man-putty.but similarity index 90% rename from doc/man-putt.but rename to doc/man-putty.but index cb7cca4..858ec0b 100644 --- a/doc/man-putt.but +++ b/doc/man-putty.but @@ -4,7 +4,7 @@ \S{putty-manpage-name} NAME -\cw{putty} - GUI SSH, Telnet and Rlogin client for X +\cw{putty} - GUI SSH, Telnet, Rlogin, and SUPDUP client for X \S{putty-manpage-synopsis} SYNOPSIS @@ -13,8 +13,8 @@ \S{putty-manpage-description} DESCRIPTION -\cw{putty} is a graphical SSH, Telnet and Rlogin client for X. It is -a direct port of the Windows SSH client of the same name. +\cw{putty} is a graphical SSH, Telnet, Rlogin, and SUPDUP client for +X. It is a direct port of the Windows SSH client of the same name. \S{putty-manpage-options} OPTIONS @@ -128,6 +128,16 @@ an effort is made to suppress obvious passwords.) encrypted packet data. } +\dt \cw{\-logoverwrite} + +\dd If \cw{putty} is configured to write to a log file that already exists, +discard the existing file. + +\dt \cw{\-logappend} + +\dd If \cw{putty} is configured to write to a log file that already exists, +append new log data to the existing file. + \dt \cw{\-cs} \e{charset} \dd This option specifies the character set in which \cw{putty} @@ -174,7 +184,8 @@ in verifying new files released by the PuTTY team. straight from the command line without having to go through the configuration box first. -\dt \cw{\-ssh}, \cw{\-telnet}, \cw{\-rlogin}, \cw{\-raw}, \cw{\-serial} +\dt \cw{\-ssh}, \cw{\-telnet}, \cw{\-rlogin}, \cw{\-supdup}, \cw{\-raw}, +\cw{-ssh-connection}, \cw{\-serial} \dd Select the protocol \cw{putty} will use to make the connection. @@ -276,11 +287,21 @@ which of the agent's keys to use. } \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +\dt \cw{\-no\-trivial\-auth} + +\dd Disconnect from any SSH server which accepts authentication without +ever having asked for any kind of password or signature or token. (You +might want to enable this for a server you always expect to challenge +you, for instance to ensure you don't accidentally type your key file's +passphrase into a compromised server spoofing PuTTY's passphrase +prompt.) + \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified -multiple times; each key can be either a fingerprint (\cw{99:aa:bb:...}) or -a base64-encoded blob in OpenSSH's one-line format. +multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, +\cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line +format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be diff --git a/doc/man-pg.but b/doc/man-puttygen.but similarity index 57% rename from doc/man-pg.but rename to doc/man-puttygen.but index 7e244d1..021af20 100644 --- a/doc/man-pg.but +++ b/doc/man-puttygen.but @@ -8,12 +8,14 @@ \S{puttygen-manpage-synopsis} SYNOPSIS -\c puttygen ( keyfile | -t keytype [ -b bits ] ) -\e bbbbbbbb iiiiiii bb iiiiiii bb iiii -\c [ -C new-comment ] [ -P ] [ -q ] -\e bb iiiiiiiiiii bb bb -\c [ -O output-type | -l | -L | -p ] -\e bb iiiiiiiiiii bb bb bb +\c puttygen ( keyfile | -t keytype [ -b bits ] [ --primes method ] [ -q ] ) +\e bbbbbbbb iiiiiii bb iiiiiii bb iiii bbbbbbbb iiiiii bb +\c [ -C new-comment ] [ -P ] [ --reencrypt ] +\e bb iiiiiiiiiii bb bbbbbbbbbbb +\c [ -O output-type | -l | -L | -p | --dump ] [ -E fptype ] +\e bb iiiiiiiiiii bb bb bb bbbbbb bb iiiiii +\c [ --ppk-param key=value,... ] +\e bbbbbbbbbbb iiibiiiiib \c [ -o output-file ] \e bb iiiiiiiiiii @@ -26,7 +28,7 @@ also interoperate with the key formats used by some other SSH clients. When you run \c{puttygen}, it does three things. Firstly, it either loads an existing key file (if you specified \e{keyfile}), or generates a new key (if you specified \e{keytype}). Then, it -optionally makes modifications to the key (changing the comment +optionally makes modifications to the key (such as changing the comment and/or the passphrase); finally, it outputs the key, or some information about the key, to a file. @@ -44,7 +46,8 @@ The options to control this phase are: \dt \e{keyfile} -\dd Specify a key file to be loaded. +\dd Specify a key file to be loaded. (Use \cq{-} to read a key +file from standard input.) \lcont{ @@ -63,12 +66,50 @@ OpenSSH format, or the standard SSH-1 format. \dt \cw{\-t} \e{keytype} \dd Specify a type of key to generate. The acceptable values here are -\c{rsa}, \c{dsa}, \c{ecdsa}, and \c{ed25519} (to generate SSH-2 keys), -and \c{rsa1} (to generate SSH-1 keys). +\c{rsa}, \c{dsa}, \c{ecdsa}, \c{eddsa}, \c{ed25519}, and \c{ed448} +(to generate SSH-2 keys), and \c{rsa1} (to generate SSH-1 keys). \dt \cw{\-b} \e{bits} -\dd Specify the size of the key to generate, in bits. Default is 2048. +\dd Specify the size of the key to generate, in bits. Default for +\c{rsa} and \c{dsa} keys is 2048. + +\dt \cw{\-\-primes} \e{method} + +\dd Method for generating prime numbers. The acceptable values here +are \c{probable} (the default), \c{proven}, and \c{proven-even}; +the later methods are slower. (Various synonyms for these method +names are also accepted.) + +\lcont{ + +The \q{probable primes} method sounds unsafe, but it's the most +commonly used prime-generation strategy. There is in theory a +possibility that it might accidentally generate a number that isn't +prime, but the software does enough checking to make that probability +vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in +practice, nobody worries about it very much. + +The other methods cause PuTTYgen to use numbers that it is \e{sure} +are prime, because it generates the output number together with a +proof of its primality. This takes more effort, but it eliminates that +theoretical risk in the probabilistic method. + +You might choose to switch from probable to proven primes if you have +a local security standard that demands it, or if you don't trust the +probabilistic argument for the safety of the usual method. + +} + +\dt \cw{\-\-strong-rsa} + +\dd When generating an RSA key, make sure the prime factors of the key +modulus are \q{strong primes}. A strong prime is a prime number chosen +to have a particular structure that makes certain factoring algorithms +more difficult to apply, so some security standards recommend their +use. However, the most modern factoring algorithms are unaffected, so +this option is probably not worth turning on \e{unless} you have a +local standard that recommends it. \dt \cw{\-q} @@ -102,6 +143,70 @@ to type). automatic when you are generating a new key, but not when you are modifying an existing key. +\dt \cw{\-\-reencrypt} + +\dd For an existing private key saved with a passphrase, refresh the +encryption without changing the passphrase. + +\lcont{ +This is most likely to be useful with the \cw{\-\-ppk-param} option, +to change some aspect of the key file's format or encryption. +} + +\dt \cw{\-\-ppk-param} \e{key}\cw{=}\e{value}\cw{,}... + +\dd When saving a PPK file (the default \cw{private} output type for SSH-2 +keys), adjust details of the on-disk format. + +\lcont{ + +Aspects to change are specified as a series of \e{key}\cw{=}\e{value} pairs +separated by commas. The \e{key}s are: + +\dt \cw{version} + +\dd The PPK format version. Possible values are \cw{3} (the default) +and \cw{2} (which is less resistant to brute-force decryption, but +which you might need if your key needs to be used by old versions of +PuTTY tools, or other PPK consumers). + +\lcont{ +The following \e{key}s only affect PPK version 3 files. +} + +\dt \cw{kdf} + +\dd The variant of the Argon2 key derivation function to use. Options +are \cw{argon2id} (default, and recommended), \cw{argon2i}, and +\cw{argon2d}. + +\lcont{ +You might change this if you consider your exposure to side-channel +attacks to be different to the norm. +} + +\dt \cw{memory} + +\dd The amount of memory needed to decrypt the key, in Kbyte. Default +is 8192 (i.e., 8 Mbyte). + +\dt \cw{time} + +\dd Approximate time, on this machine, required to attempt decrypting +the key, in milliseconds. Default is 100 (ms). + +\dt \cw{passes} + +\dd Alternative to \cw{time}: explicitly specify the number of hash +passes required to attempt decrypting the key. + +\dt \cw{parallelism} + +\dd Number of parallelisable threads that can be used to decrypt the +key. Default is 1 (force decryption to run single-threaded). + +} + In the third phase, \c{puttygen} saves the key or information about it. The options to control this are: @@ -115,7 +220,8 @@ Acceptable options are: \dt \cw{private} \dd Save the private key in a format usable by PuTTY. This will either -be the standard SSH-1 key format, or PuTTY's own SSH-2 key format. +be the standard SSH-1 key format, or PuTTY's own SSH-2 key format +(\q{PPK}). This is the default. \dt \cw{public} @@ -134,8 +240,9 @@ which is a single line (\q{\cw{ssh-rsa AAAAB3NzaC1yc2}...}). \dt \cw{fingerprint} -\dd Print the fingerprint of the public key. All fingerprinting -algorithms are believed compatible with OpenSSH. +\dd Print a fingerprint of the public key. The \cw{-E} option lets you +specify which fingerprinting algorithm to use. All algorithms are +believed compatible with OpenSSH. \dt \cw{private-openssh} @@ -153,6 +260,19 @@ newer format even for RSA, DSA, and ECDSA keys. \dd Save an SSH-2 private key in ssh.com's format. This option is not permitted for SSH-1 keys. +\dt \cw{text} + +\dd Save a textual dump of the numeric components comprising the key +(both the public and private parts, if present). Useful for debugging, +or for using PuTTYgen as a key generator for applications other than +SSH. + +\lcont{ +The output consists of a series of \cw{name=value} lines, where each +\c{value} is either a C-like string literal in double quotes, or a +hexadecimal number starting with \cw{0x...} +} + If no output type is specified, the default is \c{private}. } @@ -178,6 +298,15 @@ fingerprint. Otherwise, the \c{\-o} option is required. \dd Synonym for \q{\cw{-O public}}. +\dt \cw{\-\-dump} + +\dd Synonym for \q{\cw{-O text}}. + +\dt \cw{-E} \e{fptype} + +\dd Specify the algorithm to use if generating a fingerprint. The +options are \cw{sha256} (the default) and \cw{md5}. + \dt \cw{\-\-new\-passphrase} \e{file} \dd Specify a file name; the first line will be read from this file @@ -231,7 +360,7 @@ automatically detect the input key type): \c puttygen my-ssh.com-key -o mykey.ppk -To display the fingerprint of a key (some key types require a +To display the SHA-256 fingerprint of a key (some key types require a passphrase to extract even this much information): \c puttygen -l mykey.ppk diff --git a/doc/man-ptel.but b/doc/man-puttytel.but similarity index 95% rename from doc/man-ptel.but rename to doc/man-puttytel.but index 73b85ec..bf852dd 100644 --- a/doc/man-ptel.but +++ b/doc/man-puttytel.but @@ -4,7 +4,7 @@ \S{puttytel-manpage-name} NAME -\cw{puttytel} \- GUI Telnet and Rlogin client for X +\cw{puttytel} \- GUI Telnet, Rlogin, and SUPDUP client for X \S{puttytel-manpage-synopsis} SYNOPSIS @@ -13,9 +13,9 @@ \S{puttytel-manpage-description} DESCRIPTION -\cw{puttytel} is a graphical Telnet and Rlogin client for X. It -is a direct port of the Windows Telnet and Rlogin client of the same -name, and a cut-down cryptography-free version of PuTTY. +\cw{puttytel} is a graphical Telnet, Rlogin, and SUPDUP client for X. It +is a direct port of the Windows Telnet, Rlogin, and SUPDUP client of the +same name, and a cut-down cryptography-free version of PuTTY. \S{puttytel-manpage-options} OPTIONS @@ -161,7 +161,7 @@ in verifying new files released by the PuTTY team. straight from the command line without having to go through the configuration box first. -\dt \cw{\-telnet}, \cw{\-rlogin}, \cw{\-raw} +\dt \cw{\-telnet}, \cw{\-rlogin}, \cw{\-supdup}, \cw{\-raw} \dd Select the protocol \cw{puttytel} will use to make the connection. diff --git a/doc/pageant.1 b/doc/pageant.1 index 9ff31c8..1147365 100644 --- a/doc/pageant.1 +++ b/doc/pageant.1 @@ -7,12 +7,12 @@ .SH "SYNOPSIS" .PP .nf -\fBpageant\fP\ (\ \fB\-X\fP\ |\ \fB\-T\fP\ |\ \fB\-\-permanent\fP\ |\ \fB\-\-debug\fP\ )\ [\ \fIkey\-file\fP...\ ] -\fBpageant\fP\ [\ \fIkey\-file\fP...\ ]\ \fB\-\-exec\fP\ \fIcommand\fP\ [\ \fIargs\fP...\ ] -\fBpageant\fP\ \fB\-a\fP\ \fIkey\-file\fP... -\fBpageant\fP\ (\ \fB\-d\fP\ |\ \fB\-\-public\fP\ |\ \fB\-\-public\-openssh\fP\ )\ \fIkey\-identifier\fP... -\fBpageant\fP\ \fB\-D\fP -\fBpageant\fP\ \fB\-l\fP +\fBpageant\fP\ (\ \fB\-X\fP\ |\ \fB\-T\fP\ |\ \fB\-\-permanent\fP\ |\ \fB\-\-debug\fP\ )\ [\ [\ \fB\-\-encrypted\fP\ ]\ \fIkey\-file\fP...\ ] +\fBpageant\fP\ [\ [\ \-\-\fBencrypted\fP\ ]\ \fIkey\-file\fP...\ ]\ \fB\-\-exec\fP\ \fIcommand\fP\ [\ \fIargs\fP...\ ] +\fBpageant\fP\ \fB\-a\fP\ [\ \fB\-\-encrypted\fP\ ]\ \fIkey\-file\fP... +\fBpageant\fP\ (\ \fB\-d\fP\ |\ \fB\-r\fP\ |\ \fB\-\-public\fP\ |\ \fB\-\-public\-openssh\fP\ )\ \fIkey\-identifier\fP... +\fBpageant\fP\ (\ \fB\-D\fP\ |\ \fB\-R\fP\ ) +\fBpageant\fP\ \fB\-l\fP\ [\ \fB\-\-fptype\fP\ \fIformat\fP\ ] \fBpageant\fP\ \fB\-\-askpass\fP\ \fIprompt\fP .fi .SH "DESCRIPTION" @@ -23,7 +23,7 @@ When running as an SSH agent, it listens on a Unix-domain socket for connections .PP \fBpageant\fP can also act as a client program itself, communicating with an already-running agent to add or remove keys, list the keys, or extract their public half. .PP -The agent protocol used by \fBpageant\fP is compatible with the PuTTY tools and also with other implementations such as OpenSSH\*(Aqs SSH client and \fIssh-agent(1)\fP. +The agent protocol used by \fBpageant\fP is compatible with the PuTTY tools and also with other implementations such as OpenSSH\*(Aqs SSH client and \fIssh-agent(1)\fP. Some \fBpageant\fP features are implemented with protocol extensions, so will only work if \fBpageant\fP is on both ends. .PP To run \fBpageant\fP as an agent, you must provide an option to tell it what its \fIlifetime\fP should be. Typically you would probably want Pageant to last for the duration of a login session, in which case you should use either \fB-X\fP or \fB-T\fP, depending on whether your login session is GUI or purely terminal-based respectively. For example, in your X session startup script you might write .PP @@ -47,9 +47,17 @@ In either of these modes, you can also add one or more private keys as extra com \fBeval\ $(pageant\ \-T\ ~/.ssh/key.ppk)\fP .fi .PP -in which case Pageant will prompt for the keys' passphrases (if any) and start the agent with those keys already loaded. Passphrase prompts will use the controlling terminal if one is available, or failing that the GUI if one of those is available. (The prompt method can be overridden with the \fB--gui-prompt\fP or \fB--tty-prompt\fP options.) If neither is available, no passphrase prompting can be done. +in which case Pageant will immediately prompt for the keys' passphrases (if any) and start the agent with those keys already loaded in cleartext form. Passphrase prompts will use the controlling terminal if one is available, or failing that the GUI if one of those is available. (The prompt method can be overridden with the \fB--gui-prompt\fP or \fB--tty-prompt\fP options.) If neither is available, no passphrase prompting can be done. .PP -To use Pageant to talk to an existing agent, you can add new keys using \fB-a\fP, list the current set of keys\*(Aq fingerprints and comments with \fB-l\fP, extract the full public half of any key using \fB--public\fP or \fB--public-openssh\fP, delete a key using \fB-d\fP, or delete all keys using \fB-D\fP. +Alternatively, you can start an agent with keys stored in encrypted form: +.PP +.nf +\fBeval\ $(pageant\ \-T\ \-\-encrypted\ ~/.ssh/key.ppk)\fP +.fi +.PP +In this case, Pageant will not prompt for a passphrase at startup; instead, it will prompt the first time a client tries to use the key. (Pageant will need access to a GUI so that it can pop up a passphrase prompt when required, unless it's running in \fB--debug\fP mode.) +.PP +To use Pageant to talk to an existing agent, you can add new keys using \fB-a\fP, list the current set of keys\*(Aq fingerprints and comments with \fB-l\fP, extract the full public half of any key using \fB--public\fP or \fB--public-openssh\fP, delete a specific key or all keys using \fB-d\fP or \fB-D\fP respectively, or request re-encryption of a specific key or all keys using \fB-r\fP or \fB-R\fP respectively. .SH "LIFETIME" .PP The following options are called \fIlifetime modes\fP. They all request Pageant to operate in agent mode; each one specifies a different method for Pageant to start up and know when to shut down. @@ -80,18 +88,22 @@ Pageant will fork off a subprocess to be the agent, and print environment-variab This option is not recommended, because any method of manually killing the agent carries the risk of the session terminating unexpectedly before it manages to happen. .RE .IP "\fB--debug\fP" -Pageant will run in the foreground, without forking. It will print its environment variable setup commands on standard output, and then it will log all agent activity to standard output as well. This is useful for debugging what Pageant itself is doing, or what another process is doing to it. +Pageant will run in the foreground, without forking. It will print its environment variable setup commands on standard output, and then it will log all agent activity to standard output as well; any passphrase prompts will need to be answered on standard input. This is useful for debugging what Pageant itself is doing, or what another process is doing to it. .SH "CLIENT OPTIONS" .PP The following options tell Pageant to operate in client mode, contacting an existing agent via environment variables that it should already have set. .IP "\fB-a\fP \fIkey-files\fP" -Load the specified private key file(s), decrypt them if necessary by prompting for their passphrases (with the same choice of user interfaces as in agent mode), and add them to the already-running agent. +Load the specified private key file(s) and add them to the already-running agent. Unless \fB--encrypted\fP is also specified, \fBpageant\fP will decrypt them if necessary by prompting for their passphrases (with the same choice of user interfaces as in agent mode). .RS .PP -The private key files must be in PuTTY's \fB.ppk\fP file format. +The private key files must be in PuTTY's \fB.ppk\fP file format. .RE .IP "\fB-l\fP" -List the keys currently in the running agent. Each key's fingerprint and comment string will be shown. +List the keys currently in the running agent. Each key's fingerprint and comment string will be shown. (Use the \fB-E\fP option to change the fingerprint format.) +.RS +.PP +Keys that will require a passphrase on their next use are listed as `encrypted'. Keys that can be returned to this state with \fB-r\fP are listed as `re-encryptable'. +.RE .IP "\fB--public\fP \fIkey-identifiers\fP" Print the public half of each specified key, in the RFC 4716 standard format (multiple lines, starting with `\fB---- BEGIN SSH2 PUBLIC KEY ----\fP'). .RS @@ -102,11 +114,19 @@ The name of a file containing the key, either the whole key (again in \fB.ppk\fP .IP "\fB\(bu\fP" The key's comment string, as shown by \fBpageant -l\fP. .IP "\fB\(bu\fP" -Enough hex digits of the key's fingerprint to be unique among keys currently loaded into the agent. +Enough of one of the key's fingerprint formats to be unique among keys currently loaded into the agent. .PP If Pageant can uniquely identify one key by interpreting the \fIkey-identifier\fP in any of these ways, it will assume that key was the one you meant. If it cannot, you will have to specify more detail. .PP -If you find that your desired \fIkey-identifier\fP string can be validly interpreted as more than one of the above \fIkinds\fP of identification, you can disambiguate by prefixing it with `\fBfile:\fP', `\fBcomment:\fP' or `\fBfp:\fP' to indicate that it is a filename, comment string or fingerprint prefix respectively. +If you find that your desired \fIkey-identifier\fP string can be validly interpreted as more than one of the above \fIkinds\fP of identification, you can disambiguate by prefixing it as follows: +.IP "`\fBfile:\fP'" +to indicate that it is a filename +.IP "`\fBcomment:\fP'" +to indicate that it is a comment string +.IP "`\fBfp:\fP'" +to indicate that it is a fingerprint; any fingerprint format will be matched +.IP "`\fBsha256:\fP' or `\fBmd5:\fP'" +to indicate that it is a fingerprint of a specific format .RE .IP "\fB--public-openssh\fP \fIkey-identifiers\fP, \fB-L\fP \fIkey-identifiers\fP" Print the public half of each specified key, in the one-line format used by OpenSSH, suitable for putting in \fB.ssh/authorized_keys\fP files. @@ -114,6 +134,24 @@ Print the public half of each specified key, in the one-line format used by Open Delete each specified key from the agent's memory, so that the agent will no longer serve it to clients unless it is loaded in again using \fBpageant -a\fP. .IP "\fB-D\fP" Delete all keys from the agent's memory, leaving it completely empty. +.IP "\fB-r\fP \fIkey-identifiers\fP" +`Re-encrypt' each specified key in the agent's memory - that is, forget any cleartext version, so that the user will be prompted for a passphrase again next time the key is used. (For this to be possible, the key must previously have been added with the \fB--encrypted\fP option.) +.RS +.PP +(Holding encrypted keys is a Pageant extension, so this option and \fB-R\fP are unlikely to work with other agents.) +.RE +.IP "\fB-R\fP" +`Re-encrypt' all possible keys in the agent's memory. (This may leave some keys in cleartext, if they were not previously added with the \fB--encrypted\fP option.) +.IP "\fB--test-sign\fP \fIkey-identifier\fP" + +.IP "\fB--test-sign-with-flags=\fP\fIflags\fP \fIkey-identifier\fP" +Sign arbitrary data with the given key. This mode is only likely to be useful when testing \fBpageant\fP itself. +.RS +.PP +The data to sign is taken from standard input, signed by the agent with the key identified by \fIkey-identifier\fP, and the resulting signature emitted on standard output (as a binary blob in the format defined by the SSH specifications). +.PP +\fIflags\fP is a number representing a combination of flag bits defined by the SSH agent protocol. +.RE .SH "SSH-ASKPASS REPLACEMENT" .IP "\fB--askpass\fP \fIprompt\fP" With this option, \fBpageant\fP acts as an \fIssh-askpass(1)\fP replacement, rather than performing any SSH agent functionality. This may be useful if you prefer Pageant\*(Aqs GUI prompt style, which minimises information leakage about your passphrase length in its visual feedback, compared to other \fIssh-askpass(1)\fP implementations. @@ -136,8 +174,24 @@ The log information is the same as that produced by the \fB--debug\fP lifetime o .RE .IP "\fB-s\fP, \fB-c\fP" Force Pageant to output its environment setup commands in the style of POSIX / Bourne shells (\fB-s\fP) or C shells (\fB-c\fP) respectively. If neither option is given, Pageant will guess based on whether the environment variable \fBSHELL\fP has a value ending in `\fBcsh\fP'. +.IP "\fB--symlink\fP \fIfixed-path\fP" +When operating in agent mode, as well as creating a uniquely named listening socket, \fBpageant\fP will also create (or update) a symbolic link at \fIfixed-path\fP pointing to that socket. +.RS +.PP +This allows access to an agent instance by setting the \fBSSH_AUTH_SOCK\fP environment variable to \fIfixed-path\fP, rather than having to use the value invented by \fBpageant\fP when it starts. It\*(Aqs mainly expected to be useful for debugging. +.RE +.IP "\fB--encrypted\fP, \fB--no-decrypt\fP" +When adding keys to the agent (at startup or later), keep them in encrypted form until the first attempt to use them; the user will be prompted for a passphrase then. Once decrypted, a key that was added in this way can be `re-encrypted' with the \fB-r\fP or \fB-R\fP client options. +.RS +.PP +The \fB--encrypted\fP option makes no difference for key files which do not have a passphrase. +.PP +(Storing keys in encrypted form is a Pageant extension; other agent implementations are unlikely to support it.) +.RE +.IP "\fB-E\fP \fIfingerprint-type\fP, \fB--fptype\fP \fIfingerprint-type\fP" +Specify the fingerprint format to print. Only applicable when listing fingerprints with \fB-l\fP. The available formats are \fBsha256\fP (the default) and \fBmd5\fP. .IP "\fB--gui-prompt\fP, \fB--tty-prompt\fP" -Force Pageant to prompt for key passphrases with a particular method (GUI or terminal) rather than trying to guess the most appropriate method as described above. (These options are relevant whenever an encrypted key filename is specified to \fBpageant\fP, and in \fB--askpass\fP mode.) +Force Pageant to prompt for key passphrases with a particular method (GUI or terminal) rather than trying to guess the most appropriate method as described above. (These options are relevant whenever a key file is specified to \fBpageant\fP that needs immediate decryption, and in \fB--askpass\fP mode.) .IP "\fB--help\fP" Print a brief summary of command-line options and terminate. .IP "\fB--version\fP, \fB-V\fP" diff --git a/doc/pageant.but b/doc/pageant.but index df8a151..8abb5cd 100644 --- a/doc/pageant.but +++ b/doc/pageant.but @@ -11,7 +11,8 @@ format. See \k{pubkey} to find out how to generate and use one. When you run Pageant, it will put an icon of a computer wearing a hat into the \ii{System tray}. It will then sit and do nothing, until you -load a private key into it. +load a private key into it. (You may need to use Windows' +\q{Show hidden icons} arrow to see the Pageant icon.) If you click the Pageant icon with the right mouse button, you will see a menu. Select \q{View Keys} from this menu. The Pageant main @@ -46,6 +47,9 @@ When you want to shut down Pageant, click the right button on the Pageant icon in the System tray, and select \q{Exit} from the menu. Closing the Pageant main window does \e{not} shut down Pageant. +If you want Pageant to stay running but forget all the keys it has +acquired, select \q{Remove All Keys} from the System tray menu. + \H{pageant-mainwin} The Pageant main window The Pageant main window appears when you left-click on the Pageant @@ -60,8 +64,8 @@ The large list box in the Pageant main window lists the private keys that are currently loaded into Pageant. The list might look something like this: -\c ssh-rsa 2048 22:d6:69:c9:22:51:ac:cb:b9:15:67:47:f7:65:6d:d7 k1 -\c ssh-dss 2048 e4:6c:69:f3:4f:fc:cf:fc:96:c0:88:34:a7:1e:59:d7 k2 +\c ssh-ed25519 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w +\c ssh-rsa 2048 SHA256:8DFtyHm3kQihgy52nzX96qMcEVOq7/yJmmwQQhBWYFg For each key, the list box will tell you: @@ -70,17 +74,29 @@ For each key, the list box will tell you: \c{ssh-dss} (a DSA key for use with the SSH-2 protocol), \c{ecdsa-sha2-*} (an ECDSA key for use with the SSH-2 protocol), \c{ssh-ed25519} (an Ed25519 key for use with the SSH-2 protocol), +\c{ssh-ed448} (an Ed448 key for use with the SSH-2 protocol), or \c{ssh1} (an RSA key for use with the old SSH-1 protocol). -\b The size (in bits) of the key. +\b The size (in bits) of the key, for key types that come in different +sizes. \b The \I{key fingerprint}fingerprint for the public key. This should be the same fingerprint given by PuTTYgen, and (hopefully) also the same fingerprint shown by remote utilities such as \i\c{ssh-keygen} when applied to your \c{authorized_keys} file. +\lcont{ +By default this is shown in the \q{SHA256} format. You can change to the +older \q{MD5} format (which looks like \c{aa:bb:cc:...}) with the +\q{Fingerprint type} drop-down, but bear in mind that this format is +less secure and should be avoided for comparison purposes where possible. +} + \b The comment attached to the key. +\b The state of deferred decryption, if enabled for this key. +See \k{pageant-deferred-decryption}. + \S{pageant-mainwin-addkey} The \q{Add Key} button To add a key to Pageant by reading it out of a local disk file, @@ -138,6 +154,9 @@ passphrases on startup. If Pageant is already running, this syntax loads keys into the existing Pageant. +You can specify the \cq{--encrypted} option to defer decryption of +these keys; see \k{pageant-deferred-decryption}. + \S{pageant-cmdline-command} Making Pageant run another program You can arrange for Pageant to start another program once it has @@ -151,6 +170,11 @@ by the command, like this: \c C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe +\S{pageant-cmdline-keylist} Starting with the key list visible + +Start Pageant with the \i\c{--keylist} option to show the main window +as soon as it starts up. + \S{pageant-cmdline-restrict-acl} Restricting the \i{Windows process ACL} Pageant supports the same \i\c{-restrict-acl} option as the other @@ -221,6 +245,50 @@ you can send it all the way back to Pageant using the local and then it's available to every machine that has agent forwarding available (not just the ones downstream of the place you added it). +\H{pageant-deferred-decryption} Loading keys without decrypting them + +You can add keys to Pageant \e{without} decrypting them. The key +file will be held in Pageant's memory still encrypted, and when a +client program first tries to use the key, Pageant will display a +dialog box prompting for the passphrase so that the key can be +decrypted. + +This works the same way whether the key is used by an instance of +PuTTY running locally, or a remote client connecting to Pageant +through agent forwarding. + +To add a key to Pageant in this encrypted form, press the \q{Add Key +(encrypted)} button in the Pageant main window, or alternatively +right-click on the Pageant icon in the system tray and select \q{Add +Key (encrypted)} from there. Pageant will bring up a file dialog, in +just the same way as it would for the plain \q{Add Key} button. But it +won't ask for a passphrase. Instead, the key will be listed in the +main window with \q{(encrypted)} after it. + +To start Pageant up in the first place with encrypted keys loaded into +it, you can use the \cq{--encrypted} option on the command line. For +example: + +\c C:\PuTTY\pageant.exe --encrypted d:\main.ppk + +After a key has been decrypted for the first use, it remains +decrypted, so that it can be used again. The main window will list +the key with \q{(\i{re-encryptable})} after it. You can revert it +to the previous state, where a passphrase is required, using the +\q{\i{Re-encrypt}} button in the Pageant main window. + +You can also \q{re-encrypt} all keys that were added encrypted by +choosing \q{Re-encrypt All Keys} from the System tray menu. +(Note that this does \e{not} discard cleartext keys that were not +previously added encrypted!) + +\s{CAUTION}: When Pageant displays a prompt to decrypt an +already-loaded key, it cannot give keyboard focus to the prompt dialog +box. As far as I know this is a deliberate defensive measure by +Windows, against malicious software. So make sure you click in the +prompt window before typing your passphrase, or else the passphrase +might be sent to somewhere you didn't want to trust with it! + \H{pageant-security} Security considerations \I{security risk}Using Pageant for public-key authentication gives you the diff --git a/doc/plink.1 b/doc/plink.1 index 37a84b2..a70606d 100644 --- a/doc/plink.1 +++ b/doc/plink.1 @@ -33,6 +33,8 @@ Force use of rlogin protocol. Force raw mode. .IP "\fB-serial\fP" Force serial mode. +.IP "\fB-ssh-connection\fP" +Force use of the `bare \fBssh-connection\fP' protocol. This is only likely to be useful when connecting to a \fIpsusan(1)\fP server, most likely with an absolute path to a Unix-domain socket in place of \fIhost\fP. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. .RS @@ -97,12 +99,14 @@ If you are using an authentication agent, you can also specify a \fIpublic\fP ke Don't try to use an authentication agent for local authentication. (This doesn't affect agent forwarding.) .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +.IP "\fB\-no\-trivial\-auth\fP" +Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing Plink's passphrase prompt.) .IP "\fB\-noshare\fP" Don't test and try to share an existing connection, always make a new connection. .IP "\fB\-share\fP" Test and try to share an existing connection. .IP "\fB\-hostkey\fP \fIkey\fP" -Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fB99:aa:bb:...\fP) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. +Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. @@ -135,6 +139,10 @@ For SSH connections, these options make \fBplink\fP log protocol details to a fi .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE +.IP "\fB\-logoverwrite\fP" +If Plink is configured to write to a log file that already exists, discard the existing file. +.IP "\fB\-logappend\fP" +If Plink is configured to write to a log file that already exists, append new log data to the existing file. .IP "\fB\-shareexists\fP" Instead of making a new connection, test for the presence of an existing connection that can be shared. The desired session can be specified in any of the usual ways. .RS diff --git a/doc/plink.but b/doc/plink.but index 25139be..30dcead 100644 --- a/doc/plink.but +++ b/doc/plink.but @@ -39,9 +39,9 @@ Once you've got a console window to type into, you can just type version of Plink you're using, and gives you a brief summary of how to use Plink: -\c Z:\sysosd>plink +\c C:\>plink \c Plink: command-line connection utility -\c Release 0.73 +\c Release 0.76 \c Usage: plink [options] [user@]host [command] \c ("host" can also be a PuTTY saved session name) \c Options: @@ -51,6 +51,8 @@ use Plink: \c -load sessname Load settings from saved session \c -ssh -telnet -rlogin -raw -serial \c force use of a particular protocol +\c -ssh-connection +\c force use of the bare ssh-connection protocol \c -P port connect to specified port \c -l user connect with specified username \c -batch disable all interactive prompts @@ -69,15 +71,17 @@ use Plink: \c -X -x enable / disable X11 forwarding \c -A -a enable / disable agent forwarding \c -t -T enable / disable pty allocation -\c -1 -2 force use of particular protocol version +\c -1 -2 force use of particular SSH protocol version \c -4 -6 force use of IPv4 or IPv6 \c -C enable compression \c -i key private key file for user authentication \c -noagent disable use of Pageant \c -agent enable use of Pageant +\c -no-trivial-auth +\c disconnect if SSH authentication succeeds trivially \c -noshare disable use of connection sharing \c -share enable use of connection sharing -\c -hostkey aa:bb:cc:... +\c -hostkey keyid \c manually specify a host key (may be repeated) \c -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout \c do/don't strip control chars from standard output/error @@ -90,6 +94,9 @@ use Plink: \c -sshlog file \c -sshrawlog file \c log protocol details to a file +\c -logoverwrite +\c -logappend +\c control what happens when a log file already exists \c -shareexists \c test whether a connection-sharing upstream exists @@ -100,7 +107,7 @@ Once this works, you are ready to use Plink. To make a simple interactive connection to a remote server, just type \c{plink} and then the host name: -\c Z:\sysosd>plink login.example.com +\c C:\>plink login.example.com \c \c Debian GNU/Linux 2.2 flunky.example.com \c flunky login: @@ -114,10 +121,10 @@ characters appearing in your window. Interactive connections like this are not the main point of Plink. In order to connect with a different protocol, you can give the -command line options \c{-ssh}, \c{-telnet}, \c{-rlogin} or \c{-raw}. -To make an SSH connection, for example: +command line options \c{-ssh}, \c{-ssh-connection}, \c{-telnet}, +\c{-rlogin}, or \c{-raw}. To make an SSH connection, for example: -\c Z:\sysosd>plink -ssh login.example.com +\c C:\>plink -ssh login.example.com \c login as: If you have already set up a PuTTY saved session, then instead of @@ -125,7 +132,7 @@ supplying a host name, you can give the saved session name. This allows you to use public-key authentication, specify a user name, and use most of the other features of PuTTY: -\c Z:\sysosd>plink my-ssh-session +\c C:\>plink my-ssh-session \c Sent username "fred" \c Authenticating with public key "fred@winbox" \c Last login: Thu Dec 6 19:25:33 2001 from :0.0 @@ -163,12 +170,14 @@ key of the server you're connecting to, to enter a user name, or to enter a password. To avoid being prompted for the server host key when using Plink for -an automated connection, you should first make a \e{manual} +an automated connection, you can first make a \e{manual} connection (using either of PuTTY or Plink) to the same server, verify the host key (see \k{gs-hostkey} for more information), and -select Yes to add the host key to the Registry. After that, Plink -commands connecting to that server should not give a host key prompt -unless the host key changes. +select \q{Accept} to add the host key to the Registry. After that, +Plink commands connecting to that server should not give a host key +prompt unless the host key changes. Alternatively, you can specify +the appropriate host key(s) on Plink's command line every time you +use it; see \k{using-cmdline-hostkey}. To avoid being prompted for a user name, you can: @@ -196,18 +205,18 @@ Once you have done all this, you should be able to run a remote command on the SSH server machine and have it execute automatically with no prompting: -\c Z:\sysosd>plink login.example.com -l fred echo hello, world +\c C:\>plink login.example.com -l fred echo hello, world \c hello, world \c -\c Z:\sysosd> +\c C:\> Or, if you have set up a saved session with all the connection details: -\c Z:\sysosd>plink mysession echo hello, world +\c C:\>plink mysession echo hello, world \c hello, world \c -\c Z:\sysosd> +\c C:\> Then you can set up other programs to run this Plink command and talk to it as if it were a process on the server machine. @@ -310,26 +319,26 @@ But in case Plink guesses wrong about whether you want this sanitisation, you can override it in either direction, using one of these options: -\dd \c{-sanitise-stderr} +\dt \c{-sanitise-stderr} -\dt Sanitise server data written to Plink's standard error channel, +\dd Sanitise server data written to Plink's standard error channel, regardless of terminals and consoles and remote ptys. -\dd \c{-no-sanitise-stderr} +\dt \c{-no-sanitise-stderr} -\dt Do not sanitise server data written to Plink's standard error +\dd Do not sanitise server data written to Plink's standard error channel. -\dd \c{-sanitise-stdout} +\dt \c{-sanitise-stdout} -\dt Sanitise server data written to Plink's standard output channel. +\dd Sanitise server data written to Plink's standard output channel. -\dd \c{-no-sanitise-stdout} +\dt \c{-no-sanitise-stdout} -\dt Do not sanitise server data written to Plink's standard output +\dd Do not sanitise server data written to Plink's standard output channel. -\S2{plink-option-antispoof} \I{-no-antispoof}: turn off authentication spoofing protection prompt +\S2{plink-option-antispoof} \i{-no-antispoof}: turn off authentication spoofing protection prompt In SSH, some possible server authentication methods require user input (for example, password authentication, or entering a private key diff --git a/doc/pscp.1 b/doc/pscp.1 index fa78453..da8f964 100644 --- a/doc/pscp.1 +++ b/doc/pscp.1 @@ -59,6 +59,10 @@ Set remote password to \fIpassword\fP. \fICAUTION:\fP this will likely make the Force use of SSH protocol version 1. .IP "\fB-2\fP" Force use of SSH protocol version 2. +.IP "\fB-ssh-connection\fP" +Force use of the `bare \fBssh-connection\fP' protocol. This is only likely to be useful when connecting to a \fIpsusan(1)\fP server, most likely with an absolute path to a Unix-domain socket in place of \fIhost\fP. +.IP "\fB-ssh\fP" +Force use of the SSH protocol. (This is usually not needed; it's only likely to be useful if you need to override some other configuration of the `bare \fBssh-connection\fP' protocol.) .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .IP "\fB-C\fP" @@ -73,8 +77,10 @@ If you are using an authentication agent, you can also specify a \fIpublic\fP ke Don't try to use an authentication agent. .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +.IP "\fB\-no\-trivial\-auth\fP" +Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PSCP's passphrase prompt.) .IP "\fB\-hostkey\fP \fIkey\fP" -Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fB99:aa:bb:...\fP) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. +Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. @@ -91,6 +97,10 @@ These options make \fBpscp\fP log protocol details to a file. (Some of these may .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE +.IP "\fB\-logoverwrite\fP" +If PSCP is configured to write to a log file that already exists, discard the existing file. +.IP "\fB\-logappend\fP" +If PSCP is configured to write to a log file that already exists, append new log data to the existing file. .SH "MORE INFORMATION" .PP For more information on \fBpscp\fP it\*(Aqs probably best to go and look at the manual on the PuTTY web page: diff --git a/doc/pscp.but b/doc/pscp.but index c8c5b94..e816f3e 100644 --- a/doc/pscp.but +++ b/doc/pscp.but @@ -37,9 +37,9 @@ Once you've got a console window to type into, you can just type version of PSCP you're using, and gives you a brief summary of how to use PSCP: -\c Z:\owendadmin>pscp +\c C:\>pscp \c PuTTY Secure Copy client -\c Release 0.73 +\c Release 0.76 \c Usage: pscp [options] [user@]host:source target \c pscp [options] source [source...] [user@]host:target \c pscp [options] -ls [user@]host:filespec @@ -55,12 +55,16 @@ use PSCP: \c -l user connect with specified username \c -pw passw login with specified password \c -1 -2 force use of particular SSH protocol version +\c -ssh -ssh-connection +\c force use of particular SSH protocol variant \c -4 -6 force use of IPv4 or IPv6 \c -C enable compression \c -i key private key file for user authentication \c -noagent disable use of Pageant \c -agent enable use of Pageant -\c -hostkey aa:bb:cc:... +\c -no-trivial-auth +\c disconnect if SSH authentication succeeds trivially +\c -hostkey keyid \c manually specify a host key (may be repeated) \c -batch disable all interactive prompts \c -no-sanitise-stderr don't strip control chars from standard error @@ -72,6 +76,9 @@ use PSCP: \c -sshlog file \c -sshrawlog file \c log protocol details to a file +\c -logoverwrite +\c -logappend +\c control what happens when a log file already exists (PSCP's interface is much like the Unix \c{scp} command, if you're familiar with that.) @@ -252,7 +259,7 @@ scripts: using \c{-batch}, if something goes wrong at connection time, the batch job will fail rather than hang. \S2{pscp-usage-options-backend}\i\c{-sftp}, \i\c{-scp} force use of -particular protocol +particular file transfer protocol As mentioned in \k{pscp-usage-basics}, there are two different file transfer protocols in use with SSH. Despite its name, PSCP (like many diff --git a/doc/psftp.1 b/doc/psftp.1 index 2bbb764..ae704e4 100644 --- a/doc/psftp.1 +++ b/doc/psftp.1 @@ -53,6 +53,10 @@ Set remote password to \fIpassword\fP. \fICAUTION:\fP this will likely make the Force use of SSH protocol version 1. .IP "\fB-2\fP" Force use of SSH protocol version 2. +.IP "\fB-ssh-connection\fP" +Force use of the `bare \fBssh-connection\fP' protocol. This is only likely to be useful when connecting to a \fIpsusan(1)\fP server, most likely with an absolute path to a Unix-domain socket in place of \fIhost\fP. +.IP "\fB-ssh\fP" +Force use of the SSH protocol. (This is usually not needed; it's only likely to be useful if you need to override some other configuration of the `bare \fBssh-connection\fP' protocol.) .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .IP "\fB-C\fP" @@ -67,8 +71,10 @@ If you are using an authentication agent, you can also specify a \fIpublic\fP ke Don't try to use an authentication agent. .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +.IP "\fB\-no\-trivial\-auth\fP" +Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PSFTP's passphrase prompt.) .IP "\fB\-hostkey\fP \fIkey\fP" -Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fB99:aa:bb:...\fP) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. +Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. @@ -81,6 +87,10 @@ These options make \fBpsftp\fP log protocol details to a file. (Some of these ma .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE +.IP "\fB\-logoverwrite\fP" +If PSFTP is configured to write to a log file that already exists, discard the existing file. +.IP "\fB\-logappend\fP" +If PSFTP is configured to write to a log file that already exists, append new log data to the existing file. .SH "COMMANDS" .PP For a list of commands available inside \fBpsftp\fP, type \fBhelp\fP at the \fBpsftp>\fP prompt. diff --git a/doc/pubkey.but b/doc/pubkey.but index 9a360e7..f40c952 100644 --- a/doc/pubkey.but +++ b/doc/pubkey.but @@ -64,7 +64,7 @@ The key types supported by PuTTY are described in \k{puttygen-keytype}. PuTTYgen is a key generator. It \I{generating keys}generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see \k{pageant}). PuTTYgen -generates RSA, DSA, ECDSA, and Ed25519 keys. +generates RSA, DSA, ECDSA, and EdDSA keys. When you run PuTTYgen you will see a window where you have two main choices: \q{Generate}, to generate a new public/private key pair, or @@ -108,7 +108,8 @@ Before generating a key pair using PuTTYgen, you need to select which type of key you need. The current version of the SSH protocol, SSH-2, supports several -different key types. PuTTYgen can generate: +different key types, although specific servers may not support all of +them. PuTTYgen can generate: \b An \i{RSA} key for use with the SSH-2 protocol. @@ -117,8 +118,8 @@ different key types. PuTTYgen can generate: \b An \i{ECDSA} (\i{elliptic curve} DSA) key for use with the SSH-2 protocol. -\b An \i{Ed25519} key (another elliptic curve algorithm) for use -with the SSH-2 protocol. +\b An \i{EdDSA} key (Edwards-curve DSA, another elliptic curve +algorithm) for use with the SSH-2 protocol. PuTTYgen can also generate an RSA key suitable for use with the old SSH-1 protocol (which only supports RSA); for this, you need to select @@ -130,14 +131,64 @@ considered secure, it's rare to need this option. The \q{Number of bits} input box allows you to choose the strength of the key PuTTYgen will generate. -\b For RSA, 2048 bits should currently be sufficient for most purposes. - -\#{FIXME: advice for DSA?} +\b For RSA and DSA, 2048 bits should currently be sufficient for most +purposes. \b For ECDSA, only 256, 384, and 521 bits are supported. (ECDSA offers equivalent security to RSA with smaller key sizes.) -\b For Ed25519, the only valid size is 256 bits. +\b For EdDSA, the only valid sizes are 255 bits (these keys are also +known as \q{\i{Ed25519}} and are commonly used) and 448 bits +(\q{\i{Ed448}}, which is much less common at the time of writing). +(256 is also accepted for backward compatibility, but the effect is +the same as 255.) + +\S{puttygen-primes} Selecting the \i{prime generation method} + +On the \q{Key} menu, you can also optionally change the method for +generating the prime numbers used in the generated key. This is used +for RSA and DSA keys only. (The other key types don't require +generating prime numbers at all.) + +The prime-generation method does not affect compatibility: a key +generated with any of these methods will still work with all the same +SSH servers. + +If you don't care about this, it's entirely sensible to leave it on the +default setting. + +The available methods are: + +\b Use \i{probable primes} (fast) + +\b Use \i{proven primes} (slower) + +\b Use proven primes with even distribution (slowest) + +The \q{probable primes} method sounds unsafe, but it's the most +commonly used prime-generation strategy. There is in theory a +possibility that it might accidentally generate a number that isn't +prime, but the software does enough checking to make that probability +vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in +practice, nobody worries about it very much. + +The other methods cause PuTTYgen to use numbers that it is \e{sure} +are prime, because it generates the output number together with a +proof of its primality. This takes more effort, but it eliminates that +theoretical risk in the probabilistic method. + +You might choose to switch from probable to proven primes if you have +a local security standard that demands it, or if you don't trust the +probabilistic argument for the safety of the usual method. + +For RSA keys, there's also an option on the \q{Key} menu to use +\i{\q{strong} primes} as the prime factors of the public key. A \q{strong} +prime is a prime number chosen to have a particular structure that +makes certain factoring algorithms more difficult to apply, so some +security standards recommend their use. However, the most modern +factoring algorithms are unaffected, so this option is probably not +worth turning on \e{unless} you have a local standard that recommends +it. \S{puttygen-generate} The \q{Generate} button @@ -179,6 +230,13 @@ a particular fingerprint. So some utilities, such as the Pageant key list box (see \k{pageant-mainwin-keylist}) and the Unix \c{ssh-add} utility, will list key fingerprints rather than the whole public key. +By default, PuTTYgen will display fingerprints in the \q{SHA256} +format. If you need to see the fingerprint in the older \q{MD5} format +(which looks like \c{aa:bb:cc:...}), you can choose +\q{Show fingerprint as MD5} from the \q{Key} menu, but bear in mind +that this is less cryptographically secure; it may be feasible for +an attacker to create a key with the same fingerprint as yours. + \S{puttygen-comment} Setting a comment for your key If you have more than one key and use them for different purposes, @@ -254,6 +312,10 @@ will need to tell PuTTY to use for authentication (see \k{config-ssh-privkey}) or tell Pageant to load (see \k{pageant-mainwin-addkey}). +(You can optionally change some details of the PPK format for your saved +key files; see \k{puttygen-save-params}. But The defaults should be +fine for most purposes.) + \S{puttygen-savepub} Saving your public key to a disk file RFC 4716 specifies a \I{SSH-2 public key format}standard format for @@ -293,6 +355,60 @@ PuTTY session which is already connected to the server. See \k{pubkey-gettingready} for general instructions on configuring public-key authentication once you have generated a key. +\S{puttygen-save-params} Parameters for saving key files + +Selecting \q{Parameters for saving key files...} from the \q{Key} menu +lets you adjust some aspects of PPK-format private key files stored on +disk. None of these options affect compatibility with SSH servers. + +In most cases, it's entirely sensible to leave all of these at their +default settings. + +\S2{puttygen-save-ppk-version} PPK file version + +This defaults to version 3, which is fine for most uses. + +You might need to select PPK version 2 if you need your private key +file to be loadable in older versions of PuTTY (0.74 and older), or in +other tools which do not yet support the version 3 format (which was +introduced in 2021). + +The version 2 format is less resistant to brute-force decryption, and +doesn't support any of the following options to control that. + +\S2{puttygen-save-passphrase-hashing} Options affecting \i{passphrase hashing} + +All of the following options only affect keys saved with passphrases. +They control how much work is required to decrypt the key (which +happens every time you type its passphrase). This allows you to trade +off the cost of legitimate use of the key against the resistance of +the encrypted key to password-guessing attacks. + +These options only affect PPK version 3. + +\dt Key derivation function + +\dd The variant of the \i{Argon2} key derivation function to use. +You might change this if you consider your exposure to side-channel +attacks to be different to the norm. + +\dt Memory to use for passphrase hash + +\dd The amount of memory needed to decrypt the key, in Kbyte. + +\dt Time to use for passphrase hash + +\dd Controls how much time is required to attempt decrypting the key. +You can either specify an approximate time in milliseconds (on this +machine), or explicitly specify a number of hash passes (which is what +the time is turned into during encryption). + +\dt Parallelism for passphrase hash + +\dd Number of parallelisable threads that can be used to decrypt the +key. The default, 1, forces the process to run single-threaded, even +on machines with multiple cores. + \S{puttygen-load} Reloading a private key PuTTYgen allows you to load an existing private key file into diff --git a/doc/pubkeyfmt.but b/doc/pubkeyfmt.but new file mode 100644 index 0000000..78da488 --- /dev/null +++ b/doc/pubkeyfmt.but @@ -0,0 +1,408 @@ +\A{ppk} PPK file format + +This appendix documents the file format used by PuTTY to store private +keys. + +In this appendix, binary data structures are described using data type +representations such as \cq{uint32}, \cq{string} and \cq{mpint} as +used in the SSH protocol standards themselves. These are defined +authoritatively by +\W{https://tools.ietf.org/html/rfc4251#section-5}{RFC 4251 section 5}, +\q{Data Type Representations Used in the SSH Protocols}. + +\H{ppk-overview} Overview + +A PPK file stores a private key, and the corresponding public key. +Both are contained in the same file. + +The file format can be completely unencrypted, or it can encrypt the +private key. The \e{public} key is stored in cleartext in both cases. +(This enables PuTTY to send the public key to an SSH server to see +whether it will accept it, and not bother prompting for the passphrase +unless the server says yes.) + +When the key file is encrypted, the encryption key is derived from a +passphrase. An encrypted PPK file is also tamper-proofed using a MAC +(authentication code), also derived from the same passphrase. The MAC +protects the encrypted private key data, but it also covers the +cleartext parts of the file. So you can't edit the public half of the +key without invalidating the MAC and causing the key file as a whole +to become useless. + +This MAC protects the key file against active cryptographic attacks in +which the public half of a key pair is modified in a controlled way +that allows an attacker to deduce information about the private half +from the resulting corrupted signatures. Any attempt to do that to a +PPK file should be reliably caught by the MAC failing to validate. + +(Such an attack would only be useful if the key file was stored in a +location where the attacker could modify it without also having full +access to the process that you type passphrases into. But that's not +impossible; for example, if your home directory was on a network file +server, then the file server's administrator could access the key file +but not processes on the client machine.) + +The MAC also covers the \e{comment} on the key. This stops an attacker +from swapping keys with each other and editing the comments to +disguise the fact. As a consequence, PuTTYgen cannot edit the comment +on a key unless you decrypt the key with your passphrase first. + +(The circumstances in which \e{that} attack would be useful are even +more restricted. One example might be that the different keys trigger +specific actions on the server you're connecting to and one of those +actions is more useful to the attacker than the other. But once you +have a MAC at all, it's no extra effort to make it cover as much as +possible, and usually sensible.) + +\H{ppk-outer} Outer layer + +The outer layer of a PPK file is text-based. The PuTTY tools will +always use LF line termination when writing PPK files, but will +tolerate CR+LF and CR-only on input. + +The first few lines identify it as a PPK, and give some initial data +about what's stored in it and how. They look like this: + +\c PuTTY-User-Key-File-version: algorithm-name +\e bbbbbbb bbbbbbbbbbbbbb +\c Encryption: encryption-type +\e bbbbbbbbbbbbbbb +\c Comment: key-comment-string +\e bbbbbbbbbbbbbbbbbb + +\s{version} is a decimal number giving the version number of the file +format itself. The current file format version is 3. + +\s{algorithm-name} is the SSH protocol identifier for the public key +algorithm that this key is used for (such as \cq{ssh-dss} or +\cq{ecdsa-sha2-nistp384}). + +\s{encryption-type} indicates whether this key is stored encrypted, +and if so, by what method. Currently the only supported encryption +types are \cq{aes256-cbc} and \cq{none}. + +\s{key-comment-string} is a free text field giving the comment. This +can contain any byte values other than 13 and 10 (CR and LF). + +The next part of the file gives the public key. This is stored +unencrypted but base64-encoded +(\W{https://tools.ietf.org/html/rfc4648}{RFC 4648}), and is preceded +by a header line saying how many lines of base64 data are shown, +looking like this: + +\c Public-Lines: number-of-lines +\e bbbbbbbbbbbbbbb +\c that many lines of base64 data +\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + +The base64-encoded data in this blob is formatted in exactly the same +way as an SSH public key sent over the wire in the SSH protocol +itself. That is also the same format as the base64 data stored in +OpenSSH's \c{authorized_keys} file, except that in a PPK file the +base64 data is split across multiple lines. But if you remove the +newlines from the middle of this section, the resulting base64 blob is +in the right format to go in an \c{authorized_keys} line. + +If the key is encrypted (i.e. \s{encryption-type} is not \cq{none}), +then the next thing that appears is a sequence of lines specifying how +the keys for encrypting the file are to be derived from the +passphrase: + +\c Key-Derivation: argon2-flavour +\e bbbbbbbbbbbbbb +\c Argon2-Memory: decimal-integer +\e bbbbbbbbbbbbbbb +\c Argon2-Passes: decimal-integer +\e bbbbbbbbbbbbbbb +\c Argon2-Parallelism: decimal-integer +\e bbbbbbbbbbbbbbb +\c Argon2-Salt: hex-string +\e bbbbbbbbbb + +\s{argon2-flavour} is one of the identifiers \cq{Argon2d}, +\cq{Argon2i} or \cq{Argon2id}, all describing variants of the Argon2 +password-hashing function. + +The three integer values are used as parameters for Argon2, which +allows you to configure the amount of memory used (in Kbyte), the number +of passes of the algorithm to run (to tune its running time), and the +degree of parallelism required by the hash function. The salt is +decoded into a sequence of binary bytes and used as an additional +input to Argon2. (It is chosen randomly when the key file is written, +so that a guessing attack can't be mounted in parallel against +multiple key files.) + +The next part of the file gives the private key. This is +base64-encoded in the same way: + +\c Private-Lines: number-of-lines +\e bbbbbbbbbbbbbbb +\c that many lines of base64 data +\e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + +The binary data represented in this base64 blob may be encrypted, +depending on the \e{encryption-type} field in the key file header +shown above: + +\b If \s{encryption-type} is \cq{none}, then this data is stored in +plain text. + +\b If \s{encryption-type} is \cq{aes256-cbc}, then this data is +encrypted using AES, with a 256-bit key length, in the CBC cipher +mode. The key and initialisation vector are derived from the +passphrase: see \k{ppk-keys}. + +\lcont{ + +In order to encrypt the private key data with AES, it must be a +multiple of 16 bytes (the AES cipher block length). This is achieved +by appending random padding to the data before encrypting it. When +decoding it after decryption, the random data can be ignored: the +internal structure of the data is enough to tell you when you've +reached the end of the meaningful part. + +} + +Unlike public keys, the binary encoding of private keys is not +specified at all in the SSH standards. See \k{ppk-privkeys} for +details of the private key format for each key type supported by +PuTTY. + +The final thing in the key file is the MAC: + +\c Private-MAC: hex-mac-data +\e bbbbbbbbbbbb + +\s{hex-mac-data} is a hexadecimal-encoded value, 64 digits long (i.e. +32 bytes), generated using the HMAC-SHA-256 algorithm with the +following binary data as input: + +\b \cw{string}: the \s{algorithm-name} header field. + +\b \cw{string}: the \s{encryption-type} header field. + +\b \cw{string}: the \s{key-comment-string} header field. + +\b \cw{string}: the binary public key data, as decoded from the base64 +lines after the \cq{Public-Lines} header. + +\b \cw{string}: the plaintext of the binary private key data, as +decoded from the base64 lines after the \cq{Private-Lines} header. If +that data was stored encrypted, then the decrypted version of it is +used in this MAC preimage, \e{including} the random padding mentioned +above. + +The MAC key is derived from the passphrase: see \k{ppk-keys}. + +\H{ppk-privkeys} Private key encodings + +This section describes the private key format for each key type +supported by PuTTY. + +Because the PPK format also contains the public key (and both public +and private key are protected by the same MAC to ensure they can't be +made inconsistent), there is no need for the private key section of +the file to repeat data from the public section. So some of these +formats are very short. + +In all cases, a decoding application can begin reading from the start +of the decrypted private key data, and know when it has read all that +it needs. This allows random padding after the meaningful data to be +safely ignored. + +\S{ppk-privkey-rsa} RSA + +RSA keys are stored using an \s{algorithm-name} of \cq{ssh-rsa}. (Keys +stored like this are also used by the updated RSA signature schemes +that use hashes other than SHA-1.) + +The public key data has already provided the key modulus and the +public encoding exponent. The private data stores: + +\b \cw{mpint}: the private decoding exponent of the key. + +\b \cw{mpint}: one prime factor \e{p} of the key. + +\b \cw{mpint}: the other prime factor \e{q} of the key. (RSA keys +stored in this format are expected to have exactly two prime factors.) + +\b \cw{mpint}: the multiplicative inverse of \e{q} modulo \e{p}. + +\S{ppk-privkey-dsa} DSA + +DSA keys are stored using an \s{algorithm-name} of \cq{ssh-dss}. + +The public key data has already provided the key parameters (the large +prime \e{p}, the small prime \e{q} and the group generator \e{g}), and +the public key \e{y}. The private key stores: + +\b \cw{mpint}: the private key \e{x}, which is the discrete logarithm +of \e{y} in the group generated by \e{g} mod \e{p}. + +\S{ppk-privkey-ecdsa} NIST elliptic-curve keys + +NIST elliptic-curve keys are stored using one of the following +\s{algorithm-name} values, each corresponding to a different elliptic +curve and key size: + +\b \cq{ecdsa-sha2-nistp256} + +\b \cq{ecdsa-sha2-nistp384} + +\b \cq{ecdsa-sha2-nistp521} + +The public key data has already provided the public elliptic curve +point. The private key stores: + +\b \cw{mpint}: the private exponent, which is the discrete log of the +public point. + +\S{ppk-privkey-eddsa} EdDSA elliptic-curve keys (Ed25519 and Ed448) + +EdDSA elliptic-curve keys are stored using one of the following +\s{algorithm-name} values, each corresponding to a different elliptic +curve and key size: + +\b \cq{ssh-ed25519} + +\b \cq{ssh-ed448} + +The public key data has already provided the public elliptic curve +point. The private key stores: + +\b \cw{mpint}: the private exponent, which is the discrete log of the +public point. + +\H{ppk-keys} Key derivation + +When a key file is encrypted, there are three pieces of key material +that need to be computed from the passphrase: + +\b the key for the symmetric cipher used to encrypt the private key + +\b the initialisation vector for that cipher encryption + +\b the key for the MAC. + +If \s{encryption-type} is \cq{aes256-cbc}, then the symmetric cipher +key is 32 bytes long, and the initialisation vector is 16 bytes (one +cipher block). The length of the MAC key is also chosen to be 32 +bytes. + +If \s{encryption-type} is \cq{none}, then all three of these pieces of +data have zero length. (The MAC is still generated and checked in the +key file format, but it has a zero-length key.) + +If the amount of key material required is not zero, then the +passphrase is fed to the Argon2 key derivation function, in whichever +mode is described in the \cq{Key-Derivation} header in the key file, +with parameters derived from the various +\q{\cw{Argon2-}\e{Parameter}\cw{:}} headers. + +(If the key is unencrypted, then all those headers are omitted, and +Argon2 is not run at all.) + +Argon2 takes two extra string inputs in addition to the passphrase and +the salt: a secret key, and some \q{associated data}. In PPK's use of +Argon2, these are both set to the empty string. + +The \q{tag length} parameter to Argon2 (i.e. the amount of data it is +asked to output) is set to the sum of the lengths of all of the data +items required, i.e. (cipher key length + IV length + MAC key length). +The output data is interpreted as the concatenation of the cipher key, +the IV and the MAC key, in that order. + +So, for \cq{aes256-cbc}, the tag length will be 32+16+32\_=\_80 bytes; +of the 80 bytes of output data, the first 32 bytes are used as the +256-bit AES key, the next 16 as the CBC IV, and the final 32 bytes as +the HMAC-SHA-256 key. + +\H{ppk-old} Older versions of the PPK format + +\S{ppk-v2} Version 2 + +PPK version 2 was used by PuTTY 0.52 to 0.74 inclusive. + +In PPK version 2, the MAC algorithm used was HMAC-SHA-1 (so the +\cw{Private-MAC} line contained only 40 hex digits). + +The \cq{Key-Derivation:} header and all the +\q{\cw{Argon2-}\e{Parameter}\cw{:}} headers were absent. Instead of +using Argon2, the key material for encrypting the private blob was +derived from the passphrase in a totally different way, as follows. + +The cipher key for \cq{aes256-cbc} was constructed by generating two +SHA-1 hashes, concatenating them, and taking the first 32 bytes of the +result. (So you'd get all 20 bytes of the first hash output, and the +first 12 of the second). Each hash preimage was as follows: + +\b \cw{uint32}: a sequence number. This is 0 in the first hash, and 1 +in the second. (The idea was to extend this mechanism to further +hashes by continuing to increment the sequence number, if future +changes required even longer keys.) + +\b the passphrase, without any prefix length field. + +In PPK v2, the CBC initialisation vector was all zeroes. + +The MAC key was 20 bytes long, and was a single SHA-1 hash of the +following data: + +\b the fixed string \cq{putty-private-key-file-mac-key}, without any +prefix length field. + +\b the passphrase, without any prefix length field. (If the key is +stored unencrypted, the passphrase was taken to be the empty string +for these purposes.) + +\S{ppk-v1} Version 1 + +PPK version 1 was a badly designed format, only used during initial +development, and not recommended for production use. + +PPK version 1 was never used by a released version of PuTTY. It was +only emitted by some early development snapshots between version 0.51 +(which did not support SSH-2 public keys at all) and 0.52 (which +already used version 2 of this file format). I \e{hope} there are no +PPK v1 files in use anywhere. But just in case, the old badly designed +format is documented here anyway. + +In PPK version 1, the input to the MAC does not include any of the +header fields or the public key. It is simply the private key data +(still in plaintext and including random padding), all by itself +(without a wrapping \cw{string}). + +PPK version 1 keys must therefore be rigorously validated after +loading, to ensure that the public and private parts of the key were +consistent with each other. + +PPK version 1 only supported the RSA and DSA key types. For RSA, this +validation can be done using only the provided data (since the private +key blob contains enough information to reconstruct the public values +anyway). But for DSA, that isn't quite enough. + +Hence, PPK version 1 DSA keys extended the private data so that +immediately after \e{x} was stored an extra value: + +\b \cw{string}: a SHA-1 hash of the public key data, whose preimage +consists of + +\lcont{ + +\b \cw{string}: the large prime \e{p} + +\b \cw{string}: the small prime \e{q} + +\b \cw{string}: the group generator \e{g} + +} + +The idea was that checking this hash would verify that the key +parameters had not been tampered with, and then the loading +application could directly verify that +\e{g}\cw{^}\e{x}\cw{\_=\_}\e{y}. + +In an \e{unencrypted} version 1 key file, the MAC is replaced by a +plain SHA-1 hash of the private key data. This is indicated by the +\cq{Private-MAC:} header being replaced with \cq{Private-Hash:} +instead. diff --git a/doc/putty.1 b/doc/putty.1 index 5902ebf..20ab813 100644 --- a/doc/putty.1 +++ b/doc/putty.1 @@ -3,7 +3,7 @@ .TH "putty" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP -\fBputty\fP - GUI SSH, Telnet and Rlogin client for X +\fBputty\fP - GUI SSH, Telnet, Rlogin, and SUPDUP client for X .SH "SYNOPSIS" .PP .nf @@ -11,7 +11,7 @@ .fi .SH "DESCRIPTION" .PP -\fBputty\fP is a graphical SSH, Telnet and Rlogin client for X. It is a direct port of the Windows SSH client of the same name. +\fBputty\fP is a graphical SSH, Telnet, Rlogin, and SUPDUP client for X. It is a direct port of the Windows SSH client of the same name. .SH "OPTIONS" .PP The command-line options supported by \fBputty\fP are: @@ -57,6 +57,10 @@ For SSH connections, these options make \fBputty\fP log protocol details to a fi .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE +.IP "\fB\-logoverwrite\fP" +If \fBputty\fP is configured to write to a log file that already exists, discard the existing file. +.IP "\fB\-logappend\fP" +If \fBputty\fP is configured to write to a log file that already exists, append new log data to the existing file. .IP "\fB\-cs\fP \fIcharset\fP" This option specifies the character set in which \fBputty\fP should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \fBputty\fP will be converted into this character set before being sent to the session. .RS @@ -75,7 +79,7 @@ Display a message summarizing the available options. Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. .IP "\fB\-load\fP \fIsession\fP" Load a saved session by name. This allows you to run a saved session straight from the command line without having to go through the configuration box first. -.IP "\fB\-ssh\fP, \fB\-telnet\fP, \fB\-rlogin\fP, \fB\-raw\fP, \fB\-serial\fP" +.IP "\fB\-ssh\fP, \fB\-telnet\fP, \fB\-rlogin\fP, \fB\-supdup\fP, \fB\-raw\fP, \fB-ssh-connection\fP, \fB\-serial\fP" Select the protocol \fBputty\fP will use to make the connection. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. @@ -119,8 +123,10 @@ If you are using an authentication agent, you can also specify a \fIpublic\fP ke Don't try to use an authentication agent for local authentication. (This doesn't affect agent forwarding.) .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) +.IP "\fB\-no\-trivial\-auth\fP" +Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PuTTY's passphrase prompt.) .IP "\fB\-hostkey\fP \fIkey\fP" -Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fB99:aa:bb:...\fP) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. +Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. diff --git a/doc/putty.chm b/doc/putty.chm index a84a239..135b468 100644 Binary files a/doc/putty.chm and b/doc/putty.chm differ diff --git a/doc/puttydoc.txt b/doc/puttydoc.txt index f92e715..b8642dd 100644 --- a/doc/puttydoc.txt +++ b/doc/puttydoc.txt @@ -12,49 +12,53 @@ not described here; and the pterm and command-line puttygen and pageant utilities are not described at all. The only Unix-specific documentation that currently exists is the man pages. -This manual is copyright 1997-2019 Simon Tatham. All rights reserved. You -may distribute this documentation under the MIT licence. See appendix C for +This manual is copyright 1997-2021 Simon Tatham. All rights reserved. You +may distribute this documentation under the MIT licence. See appendix D for the licence text in full. Chapter 1: Introduction to PuTTY -------------------------------- - PuTTY is a free SSH, Telnet and Rlogin client for Windows systems. + PuTTY is a free SSH, Telnet, Rlogin, and SUPDUP client for Windows + systems. - 1.1 What are SSH, Telnet and Rlogin? + 1.1 What are SSH, Telnet, Rlogin, and SUPDUP? - If you already know what SSH, Telnet and Rlogin are, you can safely - skip on to the next section. + If you already know what SSH, Telnet, Rlogin, and SUPDUP are, you + can safely skip on to the next section. - SSH, Telnet and Rlogin are three ways of doing the same thing: - logging in to a multi-user computer from another computer, over a - network. + SSH, Telnet, Rlogin, and SUPDUP are four ways of doing the same + thing: logging in to a multi-user computer from another computer, + over a network. - Multi-user operating systems, such as Unix and VMS, usually present - a command-line interface to the user, much like the `Command Prompt' - or `MS-DOS Prompt' in Windows. The system prints a prompt, and you - type commands which the system will obey. + Multi-user operating systems, typically of the Unix family (such + as Linux, MacOS, and the BSD family), usually present a command- + line interface to the user, much like the `Command Prompt' or `MS- + DOS Prompt' in Windows. The system prints a prompt, and you type + commands which the system will obey. Using this type of interface, there is no need for you to be sitting at the same machine you are typing commands to. The commands, and responses, can be sent over a network, so you can sit at one computer and give commands to another one, or even to more than one. - SSH, Telnet and Rlogin are _network protocols_ that allow you to do - this. On the computer you sit at, you run a _client_, which makes a - network connection to the other computer (the _server_). The network - connection carries your keystrokes and commands from the client to - the server, and carries the server's responses back to you. + SSH, Telnet, Rlogin, and SUPDUP are _network protocols_ that allow + you to do this. On the computer you sit at, you run a _client_, + which makes a network connection to the other computer (the + _server_). The network connection carries your keystrokes and + commands from the client to the server, and carries the server's + responses back to you. These protocols can also be used for other types of keyboard-based interactive session. In particular, there are a lot of bulletin boards, talker systems and MUDs (Multi-User Dungeons) which support access using Telnet. There are even a few that support SSH. - You might want to use SSH, Telnet or Rlogin if: + You might want to use SSH, Telnet, Rlogin, or SUPDUP if: - - you have an account on a Unix or VMS system which you want to be - able to access from somewhere else + - you have an account on a Unix system (or some other multi-user + OS such as VMS or ITS) which you want to be able to access from + somewhere else - your Internet Service Provider provides you with a login account on a web server. (This might also be known as a _shell account_. @@ -64,23 +68,23 @@ Chapter 1: Introduction to PuTTY - you want to use a bulletin board system, talker or MUD which can be accessed using Telnet. - You probably do _not_ want to use SSH, Telnet or Rlogin if: + You probably do _not_ want to use SSH, Telnet, Rlogin, or SUPDUP if: - you only use Windows. Windows computers have their own ways of networking between themselves, and unless you are doing something fairly unusual, you will not need to use any of these remote login protocols. - 1.2 How do SSH, Telnet and Rlogin differ? + 1.2 How do SSH, Telnet, Rlogin, and SUPDUP differ? - This list summarises some of the differences between SSH, Telnet and - Rlogin. + This list summarises some of the differences between SSH, Telnet, + Rlogin, and SUPDUP. - SSH (which stands for `secure shell') is a recently designed, high-security protocol. It uses strong cryptography to protect your connection against eavesdropping, hijacking and other - attacks. Telnet and Rlogin are both older protocols offering - minimal security. + attacks. Telnet, Rlogin, and SUPDUP are all older protocols + offering minimal security. - SSH and Rlogin both allow you to log in to the server without having to type a password. (Rlogin's method of doing this is @@ -100,8 +104,8 @@ Chapter 1: Introduction to PuTTY administrator to install it. If your client and server are both behind the same (good) firewall, - it is more likely to be safe to use Telnet or Rlogin, but we still - recommend you use SSH. + it is more likely to be safe to use Telnet, Rlogin, or SUPDUP, but + we still recommend you use SSH. Chapter 2: Getting started with PuTTY ------------------------------------- @@ -124,14 +128,14 @@ Chapter 2: Getting started with PuTTY provider of your login account. Now select a login protocol to use, from the `Connection type' - buttons. For a login session, you should select Telnet, Rlogin or - SSH. See section 1.2 for a description of the differences between - the three protocols, and advice on which one to use. The fourth - protocol, _Raw_, is not used for interactive login sessions; you + controls. For a login session, you should select SSH, Telnet, + Rlogin, or SUPDUP. See section 1.2 for a description of the + differences between these protocols, and advice on which one to use. + The _Raw_ protocol is not used for interactive login sessions; you would usually use this for debugging other Internet services (see - section 3.6). The fifth option, _Serial_, is used for connecting to - a local serial line, and works somewhat differently: see section 3.7 - for more information on this. + section 3.7). The _Serial_ option is used for connecting to a local + serial line, and works somewhat differently: see section 3.6 for + more information on this. When you change the selected protocol, the number in the `Port' box will change. This is normal: it happens because the various @@ -142,10 +146,10 @@ Chapter 2: Getting started with PuTTY administrator should have told you which one. (For example, many MUDs run Telnet service on a port other than 23.) - Once you have filled in the `Host Name', `Protocol', and possibly - `Port' settings, you are ready to connect. Press the `Open' button - at the bottom of the dialog box, and PuTTY will begin trying to - connect you to the server. + Once you have filled in the `Host Name', `Connection type', and + possibly `Port' settings, you are ready to connect. Press the `Open' + button at the bottom of the dialog box, and PuTTY will begin trying + to connect you to the server. 2.2 Verifying the host key (SSH only) @@ -154,17 +158,15 @@ Chapter 2: Getting started with PuTTY If you are using SSH to connect to a server for the first time, you will probably see a message looking something like this: - The server's host key is not cached in the registry. You - have no guarantee that the server is the computer you - think it is. - The server's rsa2 key fingerprint is: - ssh-rsa 1024 7b:e5:6f:a7:f4:f9:81:62:5c:e3:1f:bf:8b:57:6c:5a - If you trust this host, hit Yes to add the key to - PuTTY's cache and carry on connecting. - If you want to carry on connecting just once, without - adding the key to the cache, hit No. - If you do not trust this host, hit Cancel to abandon the - connection. + The server's host key is not cached in the registry. You have no + guarantee that the server is the computer you think it is. + The server's ssh-ed25519 key fingerprint is: + ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w + If you trust this host, press "Accept" to add the key to PuTTY's + cache and carry on connecting. + If you want to carry on connecting just once, without adding the key + to the cache, press "Connect Once". + If you do not trust this host, press "Cancel" to abandon the connection. This is a feature of the SSH protocol. It is designed to protect you against a network attack known as _spoofing_: secretly redirecting @@ -186,7 +188,8 @@ Chapter 2: Getting started with PuTTY the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you - type any private information (such as a password) into it. + type any private information (such as a password) into it. (See + section 10.2 for what that looks like.) However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the @@ -198,13 +201,29 @@ Chapter 2: Getting started with PuTTY the network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the - Internet), you should check with your system administrator, - perhaps by telephone or in person. (Many servers have more than - one host key. If the system administrator sends you more than one - fingerprint, you should make sure the one PuTTY shows you is on the - list, but it doesn't matter which one it is.) - - See section 4.21 for advanced options for managing host keys. + Internet), you should check with your system administrator, perhaps + by telephone or in person. (When verifying the fingerprint, be + careful with letters and numbers that can be confused with each + other: `0'/`O', `1'/`I'/`l', and so on.) + + Many servers have more than one host key. If the system + administrator sends you more than one fingerprint, you should make + sure the one PuTTY shows you is on the list, but it doesn't matter + which one it is. + + If you don't have any fingerprints that look like the example + (`SHA256:' followed by a long string of characters), but instead + have pairs of characters separated by colons like `a4:db:96:a7:...', + try pressing the `More info...' button and see if you have a + fingerprint matching the `MD5 fingerprint' there. This is an older + and less secure way to summarise the same underlying host key; it's + possible for an attacker to create their own host key with the same + fingerprint; so you should avoid relying on this fingerprint format + unless you have no choice. The `More info...' dialog box also shows + the full host public key, in case that is easier to compare than a + fingerprint. + + See section 4.19 for advanced options for managing host keys. 2.3 Logging in @@ -427,7 +446,7 @@ Chapter 3: Using PuTTY Only available in SSH-2. Forces a repeat key exchange immediately (and resets associated timers and counters). For - more information about repeat key exchanges, see section 4.20.2. + more information about repeat key exchanges, see section 4.18.2. - Cache new host key type @@ -440,7 +459,7 @@ Chapter 3: Using PuTTY of the connection to cross-certify the new key). That key will be used for the rest of the current session; it may not actually be used for future sessions, depending on your preferences (see - section 4.21.1). + section 4.19.1). Normally, PuTTY will carry on using a host key it already knows, even if the server offers key formats that PuTTY would otherwise @@ -473,7 +492,7 @@ Chapter 3: Using PuTTY - Erase Character PuTTY can also be configured to send this when the Backspace key - is pressed; see section 4.17.3. + is pressed; see section 4.29.3. - Erase Line @@ -490,12 +509,12 @@ Chapter 3: Using PuTTY - Interrupt Process PuTTY can also be configured to send this when Ctrl-C is typed; - see section 4.17.3. + see section 4.29.3. - Suspend Process PuTTY can also be configured to send this when Ctrl-Z is typed; - see section 4.17.3. + see section 4.29.3. - End Of Record @@ -624,7 +643,7 @@ Chapter 3: Using PuTTY what it does do. You should then tick the `Enable X11 forwarding' box in the X11 - panel (see section 4.26) before starting your SSH session. The `X + panel (see section 4.24) before starting your SSH session. The `X display location' box is blank by default, which means that PuTTY will try to use a sensible default such as `:0', which is the usual display location where your X server will be installed. If that @@ -648,7 +667,7 @@ Chapter 3: Using PuTTY If this works, you should then be able to run X applications in the remote session and have them display their windows on your PC. - For more options relating to X11 forwarding, see section 4.26. + For more options relating to X11 forwarding, see section 4.24. 3.5 Using port forwarding in SSH @@ -668,7 +687,7 @@ Chapter 3: Using PuTTY loopback address here; see below for more details.) - Now, before you start your SSH connection, go to the Tunnels - panel (see section 4.27). Make sure the `Local' radio button + panel (see section 4.25). Make sure the `Local' radio button is set. Enter the local port number into the `Source port' box. Enter the destination host name and port number into the `Destination' box, separated by a colon (for example, @@ -748,7 +767,7 @@ Chapter 3: Using PuTTY to obtain a fix from Microsoft in order to use addresses like 127.0.0.5 - see question A.7.17.) - For more options relating to port forwarding, see section 4.27. + For more options relating to port forwarding, see section 4.25. If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the @@ -756,7 +775,37 @@ Chapter 3: Using PuTTY which host key it should be expecting. See section 4.14.5 for details of this. - 3.6 Making raw TCP connections + 3.6 Connecting to a local serial line + + PuTTY can connect directly to a local serial line as an alternative + to making a network connection. In this mode, text typed into the + PuTTY window will be sent straight out of your computer's serial + port, and data received through that port will be displayed in the + PuTTY window. You might use this mode, for example, if your serial + port is connected to another computer which has a serial connection. + + To make a connection of this type, simply select `Serial' from the + `Connection type' radio buttons on the `Session' configuration panel + (see section 4.1.1). The `Host Name' and `Port' boxes will transform + into `Serial line' and `Speed', allowing you to specify which serial + line to use (if your computer has more than one) and what speed + (baud rate) to use when transferring data. For further configuration + options (data bits, stop bits, parity, flow control), you can use + the `Serial' configuration panel (see section 4.28). + + After you start up PuTTY in serial mode, you might find that you + have to make the first move, by sending some data out of the serial + line in order to notify the device at the other end that someone is + there for it to talk to. This probably depends on the device. If you + start up a PuTTY serial session and nothing appears in the window, + try pressing Return a few times and see if that helps. + + A serial line provides no well defined means for one end of the + connection to notify the other that the connection is finished. + Therefore, PuTTY in serial mode will remain connected until you + close the window using the close button. + + 3.7 Making raw TCP connections A lot of Internet protocols are composed of commands and responses in plain text. For example, SMTP (the protocol used to transfer e- @@ -786,55 +835,81 @@ Chapter 3: Using PuTTY section 4.1.1.) You can then enter a host name and a port number, and make the connection. - 3.7 Connecting to a local serial line + 3.8 Connecting using the Telnet protocol - PuTTY can connect directly to a local serial line as an alternative - to making a network connection. In this mode, text typed into the - PuTTY window will be sent straight out of your computer's serial - port, and data received through that port will be displayed in the - PuTTY window. You might use this mode, for example, if your serial - port is connected to another computer which has a serial connection. + PuTTY can use the Telnet protocol to connect to a server. - To make a connection of this type, simply select `Serial' from the - `Connection type' radio buttons on the `Session' configuration panel - (see section 4.1.1). The `Host Name' and `Port' boxes will transform - into `Serial line' and `Speed', allowing you to specify which serial - line to use (if your computer has more than one) and what speed - (baud rate) to use when transferring data. For further configuration - options (data bits, stop bits, parity, flow control), you can use - the `Serial' configuration panel (see section 4.29). + Telnet was perhaps the most popular remote login protocol before SSH + was introduced. It was general enough to be used by multiple server + operating systems (Unix and VMS in particular), and supported many + optional protocol extensions providing extra support for particular + server features. - After you start up PuTTY in serial mode, you might find that you - have to make the first move, by sending some data out of the serial - line in order to notify the device at the other end that someone is - there for it to talk to. This probably depends on the device. If you - start up a PuTTY serial session and nothing appears in the window, - try pressing Return a few times and see if that helps. + Unlike SSH, Telnet runs over an unsecured network connection, so it + is a very bad idea to use it over the hostile Internet (though it is + still used to some extent as of 2020). - A serial line provides no well defined means for one end of the - connection to notify the other that the connection is finished. - Therefore, PuTTY in serial mode will remain connected until you - close the window using the close button. + 3.9 Connecting using the Rlogin protocol + + PuTTY can use the Rlogin protocol to connect to a server. + + Rlogin was similar to Telnet in concept, but more focused on + connections between Unix machines. It supported a feature for + passwordless login, based on use of `privileged ports' (ports with + numbers below 1024, which Unix traditionally does not allow users + other than root to allocate). Ultimately, based on the server + trusting that the client's IP address was owned by the Unix machine + it claimed to be, and that that machine would guard its privileged + ports appropriately. + + Like Telnet, Rlogin runs over an unsecured network connection. + + 3.10 Connecting using the SUPDUP protocol + + PuTTY can use the SUPDUP protocol to connect to a server. + + SUPDUP is a login protocol used mainly by PDP-10 and Lisp machines + during the period 1975-1990. Like Telnet and Rlogin, it is + unsecured, so modern systems almost never support it. + + To make a connection of this type, select `SUPDUP' from the + `Connection type' radio buttons on the `Session' panel (see section + 4.1.1). For further configuration options (character set, more + processing, scrolling), you can use the `SUPDUP' configuration panel + (see section 4.31). + + In SUPDUP, terminal emulation is more integrated with the network + protocol than in other protocols such as SSH. The SUPDUP protocol + can thus only be used with PuTTY proper, not with the command-line + tool Plink. + + The SUPDUP protocol does not support changing the terminal + dimensions, so this capability is disabled during a SUPDUP session. - 3.8 The PuTTY command line + SUPDUP provides no well defined means for one end of the connection + to notify the other that the connection is finished. Therefore, + PuTTY in SUPDUP mode will remain connected until you close the + window using the close button. + + 3.11 The PuTTY command line PuTTY can be made to do various things without user intervention by supplying command-line arguments (e.g., from a command prompt window, or a Windows shortcut). - 3.8.1 Starting a session from the command line +3.11.1 Starting a session from the command line These options allow you to bypass the configuration window and launch straight into a session. To start a connection to a server called `host': - putty.exe [-ssh | -telnet | -rlogin | -raw] [user@]host + putty.exe [-ssh | -ssh-connection | -telnet | -rlogin | -supdup | -raw] [user@]host If this syntax is used, settings are taken from the Default Settings (see section 4.1.2); `user' overrides these settings if supplied. Also, you can specify a protocol, which will override the default - protocol (see section 3.8.3.2). + protocol (see section 3.11.3.2). For telnet sessions, the following alternative syntax is supported (this makes PuTTY suitable for use as a URL handler for telnet URLs @@ -847,11 +922,11 @@ Chapter 3: Using PuTTY putty.exe -serial com1 In order to start an existing saved session called `sessionname', - use the `-load' option (described in section 3.8.3.1). + use the `-load' option (described in section 3.11.3.1). putty.exe -load "session name" - 3.8.2 `-cleanup' +3.11.2 `-cleanup' If invoked with the `-cleanup' option, rather than running as normal, PuTTY will remove its registry entries and random seed file @@ -862,7 +937,7 @@ Chapter 3: Using PuTTY Note that on multi-user systems, `-cleanup' only removes registry entries and files associated with the currently logged-in user. - 3.8.3 Standard command-line options +3.11.3 Standard command-line options PuTTY and its associated tools support a range of command-line options, most of which are consistent across all the tools. This @@ -870,7 +945,7 @@ Chapter 3: Using PuTTY specific to a particular tool are covered in the chapter about that tool. -3.8.3.1 `-load': load a saved session +3.11.3.1 `-load': load a saved session The `-load' option causes PuTTY to load configuration details out of a saved session. If these details include a host name, then this @@ -892,37 +967,44 @@ Chapter 3: Using PuTTY the very first thing on the command line. This form of the option is deprecated.) -3.8.3.2 Selecting a protocol: `-ssh', `-telnet', `-rlogin', `-raw' `- - serial' +3.11.3.2 Selecting a protocol: `-ssh', `-ssh-connection', `-telnet', `- + rlogin', `-supdup', `-raw', `-serial' To choose which protocol you want to connect with, you can use one of these options: - `-ssh' selects the SSH protocol. + - `-ssh-connection' selects the bare ssh-connection protocol. + (This is only useful in specialised circumstances; see section + 4.27 for more information.) + - `-telnet' selects the Telnet protocol. - `-rlogin' selects the Rlogin protocol. + - `-supdup' selects the SUPDUP protocol. + - `-raw' selects the raw protocol. - `-serial' selects a serial connection. - These options are not available in the file transfer tools PSCP and - PSFTP (which only work with the SSH protocol). + Most of these options are not available in the file transfer tools + PSCP and PSFTP (which only work with the SSH protocol and the bare + ssh-connection protocol). These options are equivalent to the protocol selection buttons in the Session panel of the PuTTY configuration box (see section 4.1.1). -3.8.3.3 `-v': increase verbosity +3.11.3.3 `-v': increase verbosity Most of the PuTTY tools can be made to tell you more about what they are doing by supplying the `-v' option. If you are having trouble when making a connection, or you're simply curious, you can turn this switch on and hope to find out more about what is happening. -3.8.3.4 `-l': specify a login name +3.11.3.4 `-l': specify a login name You can specify the user name to log in as on the remote server using the `-l' option. For example, `plink login.example.com - @@ -932,10 +1014,10 @@ Chapter 3: Using PuTTY the Connection panel of the PuTTY configuration box (see section 4.15.1). -3.8.3.5 `-L', `-R' and `-D': set up port forwardings +3.11.3.5 `-L', `-R' and `-D': set up port forwardings As well as setting up port forwardings in the PuTTY configuration - (see section 4.27), you can also set up forwardings on the command + (see section 4.25), you can also set up forwardings on the command line. The command-line options work just like the ones in Unix `ssh' programs. @@ -967,11 +1049,11 @@ Chapter 3: Using PuTTY These options are not available in the file transfer tools PSCP and PSFTP. -3.8.3.6 `-m': read a remote command or script from a file +3.11.3.6 `-m': read a remote command or script from a file The `-m' option performs a similar function to the `Remote command' box in the SSH panel of the PuTTY configuration box (see section - 4.19.1). However, the `-m' option expects to be given a local file + 4.17.1). However, the `-m' option expects to be given a local file name, and it will read a command from that file. With some servers (particularly Unix systems), you can even put @@ -984,7 +1066,7 @@ Chapter 3: Using PuTTY This option is not available in the file transfer tools PSCP and PSFTP. -3.8.3.7 `-P': specify a port number +3.11.3.7 `-P': specify a port number The `-P' option is used to specify the port number to connect to. If you have a Telnet server running on port 9696 of a machine instead @@ -1000,18 +1082,19 @@ Chapter 3: Using PuTTY This option is equivalent to the port number control in the Session panel of the PuTTY configuration box (see section 4.1.1). -3.8.3.8 `-pw': specify a password +3.11.3.8 `-pw': specify a password A simple way to automate a remote login is to supply your password on the command line. This is _not recommended_ for reasons of security. If you possibly can, we recommend you set up public-key authentication instead. See chapter 8 for details. - Note that the `-pw' option only works when you are using the SSH - protocol. Due to fundamental limitations of Telnet and Rlogin, these - protocols do not support automated password authentication. + Note that the `-pw' option only works when you are using the + SSH protocol. Due to fundamental limitations of Telnet, Rlogin, + and SUPDUP, these protocols do not support automated password + authentication. -3.8.3.9 `-agent' and `-noagent': control use of Pageant for authentication +3.11.3.9 `-agent' and `-noagent': control use of Pageant for authentication The `-agent' option turns on SSH authentication using Pageant, and `-noagent' turns it off. These options are only meaningful if you @@ -1020,25 +1103,25 @@ Chapter 3: Using PuTTY See chapter 9 for general information on Pageant. These options are equivalent to the agent authentication checkbox in - the Auth panel of the PuTTY configuration box (see section 4.23.3). + the Auth panel of the PuTTY configuration box (see section 4.21.4). -3.8.3.10 `-A' and `-a': control agent forwarding +3.11.3.10 `-A' and `-a': control agent forwarding The `-A' option turns on SSH agent forwarding, and `-a' turns it off. These options are only meaningful if you are using SSH. See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security - risk involved with enabling this option; see section 9.5 for + risk involved with enabling this option; see section 9.6 for details. These options are equivalent to the agent forwarding checkbox in the - Auth panel of the PuTTY configuration box (see section 4.23.6). + Auth panel of the PuTTY configuration box (see section 4.21.7). These options are not available in the file transfer tools PSCP and PSFTP. -3.8.3.11 `-X' and `-x': control X11 forwarding +3.11.3.11 `-X' and `-x': control X11 forwarding The `-X' option turns on X11 forwarding in SSH, and `-x' turns it off. These options are only meaningful if you are using SSH. @@ -1046,12 +1129,12 @@ Chapter 3: Using PuTTY For information on X11 forwarding, see section 3.4. These options are equivalent to the X11 forwarding checkbox in the - X11 panel of the PuTTY configuration box (see section 4.26). + X11 panel of the PuTTY configuration box (see section 4.24). These options are not available in the file transfer tools PSCP and PSFTP. -3.8.3.12 `-t' and `-T': control pseudo-terminal allocation +3.11.3.12 `-t' and `-T': control pseudo-terminal allocation The `-t' option ensures PuTTY attempts to allocate a pseudo-terminal at the server, and `-T' stops it from allocating one. These options @@ -1059,12 +1142,12 @@ Chapter 3: Using PuTTY These options are equivalent to the `Don't allocate a pseudo- terminal' checkbox in the SSH panel of the PuTTY configuration box - (see section 4.25.1). + (see section 4.23.1). These options are not available in the file transfer tools PSCP and PSFTP. -3.8.3.13 `-N': suppress starting a shell or command +3.11.3.13 `-N': suppress starting a shell or command The `-N' option prevents PuTTY from attempting to start a shell or command on the remote server. You might want to use this option if @@ -1076,13 +1159,13 @@ Chapter 3: Using PuTTY This option is equivalent to the `Don't start a shell or command at all' checkbox in the SSH panel of the PuTTY configuration box (see - section 4.19.2). + section 4.17.2). This option is not available in the file transfer tools PSCP and PSFTP. -3.8.3.14 `-nc': make a remote network connection in place of a remote shell - or command +3.11.3.14 `-nc': make a remote network connection in place of a remote + shell or command The `-nc' option prevents Plink (or PuTTY) from attempting to start a shell or command on the remote server. Instead, it will instruct @@ -1119,15 +1202,15 @@ Chapter 3: Using PuTTY However, Plink's built-in `-nc' option does not depend on the `nc' program being installed on the server.) -3.8.3.15 `-C': enable compression +3.11.3.15 `-C': enable compression The `-C' option enables compression of the data sent across the network. This option is only meaningful if you are using SSH. This option is equivalent to the `Enable compression' checkbox in - the SSH panel of the PuTTY configuration box (see section 4.19.3). + the SSH panel of the PuTTY configuration box (see section 4.17.3). -3.8.3.16 `-1' and `-2': specify an SSH protocol version +3.11.3.16 `-1' and `-2': specify an SSH protocol version The `-1' and `-2' options force PuTTY to use version 1 or version 2 of the SSH protocol. These options are only meaningful if you are @@ -1135,9 +1218,9 @@ Chapter 3: Using PuTTY These options are equivalent to selecting the SSH protocol version in the SSH panel of the PuTTY configuration box (see section - 4.19.4). + 4.17.4). -3.8.3.17 `-4' and `-6': specify an Internet protocol version +3.11.3.17 `-4' and `-6': specify an Internet protocol version The `-4' and `-6' options force PuTTY to use the older Internet protocol IPv4 or the newer IPv6 for most outgoing connections. @@ -1146,7 +1229,7 @@ Chapter 3: Using PuTTY protocol version as `IPv4' or `IPv6' in the Connection panel of the PuTTY configuration box (see section 4.14.4). -3.8.3.18 `-i': specify an SSH private key +3.11.3.18 `-i': specify an SSH private key The `-i' option allows you to specify the name of a private key file in `*.PPK' format which PuTTY will use to authenticate with the @@ -1160,9 +1243,18 @@ Chapter 3: Using PuTTY This option is equivalent to the `Private key file for authentication' box in the Auth panel of the PuTTY configuration box - (see section 4.23.8). + (see section 4.21.9). + +3.11.3.19 `-no-trivial-auth': disconnect if SSH authentication succeeds + trivially + + This option causes PuTTY to abandon an SSH session if the server + accepts authentication without ever having asked for any kind of + password or signature or token. + + See section 4.21.3 for why you might want this. -3.8.3.19 `-loghost': specify a logical host name +3.11.3.20 `-loghost': specify a logical host name This option overrides PuTTY's normal SSH host key caching policy by telling it the name of the host you expect your connection to end up @@ -1171,26 +1263,26 @@ Chapter 3: Using PuTTY by a colon and a port number. See section 4.14.5 for more detail on this. -3.8.3.20 `-hostkey': manually specify an expected host key +3.11.3.21 `-hostkey': manually specify an expected host key This option overrides PuTTY's normal SSH host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key - fingerprint, or an SSH-2 public key blob. See section 4.21.2 for + fingerprint, or an SSH-2 public key blob. See section 4.19.3 for more information. You can specify this option more than once if you want to configure more than one key to be accepted. -3.8.3.21 `-pgpfp': display PGP key fingerprints +3.11.3.22 `-pgpfp': display PGP key fingerprints This option causes the PuTTY tools not to run as normal, but instead to display the fingerprints of the PuTTY PGP Master Keys, in - order to aid with verifying new versions. See appendix E for more + order to aid with verifying new versions. See appendix F for more information. -3.8.3.22 `-sercfg': specify serial port configuration +3.11.3.23 `-sercfg': specify serial port configuration This option specifies the configuration parameters for the serial port (baud rate, stop bits etc). Its argument is interpreted as @@ -1212,7 +1304,7 @@ Chapter 3: Using PuTTY For example, `-sercfg 19200,8,n,1,N' denotes a baud rate of 19200, 8 data bits, no parity, 1 stop bit and no flow control. -3.8.3.23 `-sessionlog', `-sshlog', `-sshrawlog': specify session logging +3.11.3.24 `-sessionlog', `-sshlog', `-sshrawlog': enable session logging These options cause the PuTTY network tools to write out a log file. Each of them expects a file name as an argument, e.g. `- @@ -1228,7 +1320,15 @@ Chapter 3: Using PuTTY For more information on logging configuration, see section 4.2. -3.8.3.24 `-proxycmd': specify a local proxy command +3.11.3.25 `-logoverwrite', `-logappend': control behaviour with existing + log file + + If logging has been enabled (in the saved configuration, or by + another command-line option), and the specified log file already + exists, these options tell the PuTTY network tools what to do so + that they don't have to ask the user. See section 4.2.2 for details. + +3.11.3.26 `-proxycmd': specify a local proxy command This option enables PuTTY's mode for running a command on the local machine and using it as a proxy for the network connection. It @@ -1240,7 +1340,7 @@ Chapter 3: Using PuTTY backslashes must be doubled (if you want `\' in your command, you must put `\\' on the command line). -3.8.3.25 `-restrict-acl': restrict the Windows process ACL +3.11.3.27 `-restrict-acl': restrict the Windows process ACL This option (on Windows only) causes PuTTY (or another PuTTY tool) to try to lock down the operating system's access control on its own @@ -1296,31 +1396,47 @@ Chapter 4: Configuring PuTTY 4.1.1 The host name section - The top box on the Session panel, labelled `Specify your connection - by host name', contains the details that need to be filled in before - PuTTY can open a session at all. + The top box on the Session panel, labelled `Specify the destination + you want to connect to', contains the details that need to be filled + in before PuTTY can open a session at all. - The `Host Name' box is where you type the name, or the IP address, of the server you want to connect to. - - The `Connection type' radio buttons let you choose what type - of connection you want to make: a raw connection, a Telnet - connection, an Rlogin connection, an SSH connection, or a - connection to a local serial line. (See section 1.2 for a - summary of the differences between SSH, Telnet and rlogin; see - section 3.6 for an explanation of `raw' connections; see section - 3.7 for information about using a serial line.) + - The `Connection type' controls let you choose what type of + connection you want to make: an SSH network connection, a + connection to a local serial line, or various other kinds of + network connection. + + - See section 1.2 for a summary of the differences between + the network remote login protocols SSH, Telnet, Rlogin, and + SUPDUP. + + - See section 3.6 for information about using a serial line. + + - See section 3.7 for an explanation of `raw' connections. + + - See section 3.8 for a little information about Telnet. + + - See section 3.9 for information about using Rlogin. + + - See section 3.10 for information about using SUPDUP. + + - The `Bare ssh-connection' option in the `Connection type' + control is intended for specialist uses not involving + network connections. See section 4.27 for some information + about it. - The `Port' box lets you specify which port number on the server - to connect to. If you select Telnet, Rlogin, or SSH, this box - will be filled in automatically to the usual value, and you will - only need to change it if you have an unusual server. If you - select Raw mode, you will almost certainly need to fill in the - `Port' box yourself. + to connect to. If you select Telnet, Rlogin, SUPDUP, or SSH, + this box will be filled in automatically to the usual value, and + you will only need to change it if you have an unusual server. + If you select Raw mode, you will almost certainly need to fill + in the `Port' box yourself. If you select `Serial' from the `Connection type' radio buttons, the `Host Name' and `Port' boxes are replaced by `Serial line' and - `Speed'; see section 4.29 for more details of these. + `Speed'; see section 4.28 for more details of these. 4.1.2 Loading and storing saved sessions @@ -1383,7 +1499,7 @@ Chapter 4: Configuring PuTTY HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions If you need to store them in a file, you could try the method - described in section 4.30. + described in section 4.32. 4.1.3 `Close window on exit' @@ -1788,7 +1904,7 @@ Chapter 4: Configuring PuTTY press Backspace. If you are connecting over SSH, PuTTY by default tells the server - the value of this option (see section 4.25.2), so you may find that + the value of this option (see section 4.23.2), so you may find that the Backspace key does the right thing either way. Similarly, if you are connecting to a Unix system, you will probably find that the Unix `stty' command lets you configure which the server expects @@ -2919,18 +3035,18 @@ Chapter 4: Configuring PuTTY increase the probability that PuTTY will attempt to send data during a break in connectivity. (Other types of periodic network activity can cause this behaviour; in particular, SSH-2 re-keys can have this - effect. See section 4.20.2.) + effect. See section 4.18.2.) Therefore, you might find that keepalives help connection loss, or you might find they make it worse, depending on what _kind_ of network problems you have between you and the server. - Keepalives are only supported in Telnet and SSH; the Rlogin and Raw - protocols offer no way of implementing them. (For an alternative, - see section 4.14.3.) + Keepalives are only supported in Telnet and SSH; the Rlogin, SUPDUP, + and Raw protocols offer no way of implementing them. (For an + alternative, see section 4.14.3.) Note that if you are using SSH-1 and the server has a bug that makes - it unable to deal with SSH-1 ignore messages (see section 4.28.11), + it unable to deal with SSH-1 ignore messages (see section 4.26.11), enabling keepalives will have no effect. 4.14.2 `Disable Nagle's algorithm' @@ -2955,8 +3071,8 @@ Chapter 4: Configuring PuTTY The idea of TCP keepalives is similar to application-level keepalives, and the same caveats apply. The main differences are: - - TCP keepalives are available on _all_ connection types, - including Raw and Rlogin. + - TCP keepalives are available on _all_ network connection types, + including Raw, Rlogin, and SUPDUP. - The interval between TCP keepalives is usually much longer, typically two hours; this is set by the operating system, and @@ -2977,7 +3093,7 @@ Chapter 4: Configuring PuTTY Internet protocols and addressing schemes (IPv4 and IPv6). The selected protocol will be used for most outgoing network connections (including connections to proxies); however, tunnels have their own - configuration, for which see section 4.27.2. + configuration, for which see section 4.25.2. The default setting is `Auto', which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify @@ -3017,7 +3133,7 @@ Chapter 4: Configuring PuTTY servers, you probably didn't want any particular server's host key cached under that local port number. (For this latter case, you could instead explicitly configure host keys in the relevant - sessions; see section 4.21.2.) + sessions; see section 4.19.3.) If you just enter a host name for this option, PuTTY will cache the SSH host key under the default SSH port for that host, irrespective @@ -3048,7 +3164,7 @@ Chapter 4: Configuring PuTTY 4.15.1 `Auto-login username' - All three of the SSH, Telnet and Rlogin protocols allow you to + All three of the SSH, Telnet, and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.) @@ -3063,8 +3179,8 @@ Chapter 4: Configuring PuTTY implementing single sign-on, a more sensible default may be to use the name of the user logged in to the local operating system (if any); this is particularly likely to be useful with GSSAPI key - exchange and user authentication (see section 4.24 and section - 4.20.1.1). This control allows you to change the default behaviour. + exchange and user authentication (see section 4.22 and section + 4.18.1.1). This control allows you to change the default behaviour. The current system username is displayed in the dialog as a convenience. It is not saved in the configuration; if a saved @@ -3077,7 +3193,7 @@ Chapter 4: Configuring PuTTY connected to from lots of different types of terminal. In order to send the right control sequences to each one, the server will need to know what type of terminal it is dealing with. Therefore, each - of the SSH, Telnet and Rlogin protocols allow a text string to be + of the SSH, Telnet, and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a Unix server, this selects an entry from the `termcap' or `terminfo' database that tells applications what control sequences to send to the terminal, @@ -3181,10 +3297,10 @@ Chapter 4: Configuring PuTTY If you want your local proxy command to make a secondary SSH connection to a proxy host and then tunnel the primary connection over that, you might well want the `-nc' command-line - option in Plink. See section 3.8.3.14 for more information. + option in Plink. See section 3.11.3.14 for more information. You can also enable this mode on the command line; see section - 3.8.3.24. + 3.11.3.26. 4.16.2 Excluding parts of the network from proxying @@ -3333,127 +3449,12 @@ Chapter 4: Configuring PuTTY further proxy-related messages during the session will only go to the Event Log. - 4.17 The Telnet panel - - The Telnet panel allows you to configure options that only apply to - Telnet sessions. - -4.17.1 `Handling of OLD_ENVIRON ambiguity' - - The original Telnet mechanism for passing environment variables was - badly specified. At the time the standard (RFC 1408) was written, - BSD telnet implementations were already supporting the feature, and - the intention of the standard was to describe the behaviour the BSD - implementations were already using. - - Sadly there was a typing error in the standard when it was issued, - and two vital function codes were specified the wrong way round. BSD - implementations did not change, and the standard was not corrected. - Therefore, it's possible you might find either BSD or RFC-compliant - implementations out there. This switch allows you to choose which - one PuTTY claims to be. - - The problem was solved by issuing a second standard, defining a - new Telnet mechanism called NEW_ENVIRON, which behaved exactly - like the original OLD_ENVIRON but was not encumbered by existing - implementations. Most Telnet servers now support this, and it's - unambiguous. This feature should only be needed if you have trouble - passing environment variables to quite an old server. - -4.17.2 Passive and active Telnet negotiation modes - - In a Telnet connection, there are two types of data passed between - the client and the server: actual text, and _negotiations_ about - which Telnet extra features to use. - - PuTTY can use two different strategies for negotiation: - - - In _active_ mode, PuTTY starts to send negotiations as soon as - the connection is opened. - - - In _passive_ mode, PuTTY will wait to negotiate until it sees a - negotiation from the server. - - The obvious disadvantage of passive mode is that if the server is - also operating in a passive mode, then negotiation will never begin - at all. For this reason PuTTY defaults to active mode. - - However, sometimes passive mode is required in order to successfully - get through certain types of firewall and Telnet proxy server. If - you have confusing trouble with a firewall, you could try enabling - passive mode to see if it helps. - -4.17.3 `Keyboard sends Telnet special commands' - - If this box is checked, several key sequences will have their normal - actions modified: - - - the Backspace key on the keyboard will send the Telnet special - backspace code; - - - Control-C will send the Telnet special Interrupt Process code; - - - Control-Z will send the Telnet special Suspend Process code. - - You probably shouldn't enable this unless you know what you're - doing. - -4.17.4 `Return key sends Telnet New Line instead of ^M' - - Unlike most other remote login protocols, the Telnet protocol has - a special `new line' code that is not the same as the usual line - endings of Control-M or Control-J. By default, PuTTY sends the - Telnet New Line code when you press Return, instead of sending - Control-M as it does in most other protocols. - - Most Unix-style Telnet servers don't mind whether they receive - Telnet New Line or Control-M; some servers do expect New Line, - and some servers prefer to see ^M. If you are seeing surprising - behaviour when you press Return in a Telnet session, you might try - turning this option off to see if it helps. - - 4.18 The Rlogin panel - - The Rlogin panel allows you to configure options that only apply to - Rlogin sessions. - -4.18.1 `Local username' - - Rlogin allows an automated (password-free) form of login by means - of a file called `.rhosts' on the server. You put a line in your - `.rhosts' file saying something like `jbloggs@pc1.example.com', and - then when you make an Rlogin connection the client transmits the - username of the user running the Rlogin client. The server checks - the username and hostname against `.rhosts', and if they match it - does not ask for a password. - - This only works because Unix systems contain a safeguard to stop a - user from pretending to be another user in an Rlogin connection. - Rlogin connections have to come from port numbers below 1024, and - Unix systems prohibit this to unprivileged processes; so when the - server sees a connection from a low-numbered port, it assumes the - client end of the connection is held by a privileged (and therefore - trusted) process, so it believes the claim of who the user is. - - Windows does not have this restriction: _any_ user can initiate - an outgoing connection from a low-numbered port. Hence, the - Rlogin `.rhosts' mechanism is completely useless for securely - distinguishing several different users on a Windows machine. If you - have a `.rhosts' entry pointing at a Windows PC, you should assume - that _anyone_ using that PC can spoof your username in an Rlogin - connection and access your account on the server. - - The `Local username' control allows you to specify what user name - PuTTY should claim you have, in case it doesn't match your Windows - user name (or in case you didn't bother to set up a Windows user - name). - - 4.19 The SSH panel + 4.17 The SSH panel The SSH panel allows you to configure options that only apply to SSH sessions. -4.19.1 Executing a specific command on the server +4.17.1 Executing a specific command on the server In SSH, you don't have to run a general shell session on the server. Instead, you can choose to run a single specific command (such as @@ -3463,7 +3464,7 @@ Chapter 4: Configuring PuTTY Note that most servers will close the session after executing the command. -4.19.2 `Don't start a shell or command at all' +4.17.2 `Don't start a shell or command at all' If you tick this box, PuTTY will not attempt to run a shell or command after connecting to the remote server. You might want to @@ -3475,14 +3476,14 @@ Chapter 4: Configuring PuTTY version 1 protocol assumes you will always want to run a shell). This feature can also be enabled using the `-N' command-line option; - see section 3.8.3.13. + see section 3.11.3.13. If you use this feature in Plink, you will not be able to terminate the Plink process by any graceful means; the only way to kill it will be by pressing Control-C or sending a kill signal from another program. -4.19.3 `Enable compression' +4.17.3 `Enable compression' This enables data compression in the SSH connection: data sent by the server is compressed before sending, and decompressed at the @@ -3490,7 +3491,7 @@ Chapter 4: Configuring PuTTY first and the server decompresses it at the other end. This can help make the most of a low-bandwidth connection. -4.19.4 `SSH protocol version' +4.17.4 `SSH protocol version' This allows you to select whether to use SSH protocol version 2 or the older version 1. @@ -3511,7 +3512,7 @@ Chapter 4: Configuring PuTTY This prevents an active attacker downgrading an intended SSH-2 connection to SSH-1. -4.19.5 Sharing an SSH connection between PuTTY tools +4.17.5 Sharing an SSH connection between PuTTY tools The controls in this box allow you to configure PuTTY to reuse an existing SSH connection, where possible. @@ -3573,7 +3574,7 @@ Chapter 4: Configuring PuTTY It is possible to test programmatically for the existence of a live upstream using Plink. See section 7.2.3.4. - 4.20 The Kex panel + 4.18 The Kex panel The Kex panel (short for `key exchange') allows you to configure options related to SSH-2 key exchange. @@ -3597,11 +3598,11 @@ Chapter 4: Configuring PuTTY This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all. -4.20.1 Key exchange algorithm selection +4.18.1 Key exchange algorithm selection PuTTY supports a variety of SSH-2 key exchange methods, and allows you to choose which one you prefer to use; configuration is similar - to cipher selection (see section 4.22). + to cipher selection (see section 4.20). PuTTY currently supports the following key exchange methods: @@ -3626,13 +3627,13 @@ Chapter 4: Configuring PuTTY on the part of the client, and somewhat less on the part of the server, than Diffie-Hellman key exchange. - - `GSSAPI key exchange': see section 4.20.1.1. + - `GSSAPI key exchange': see section 4.18.1.1. If the first algorithm PuTTY finds is below the `warn below here' line, you will see a warning box when you make the connection, - similar to that for cipher selection (see section 4.22). + similar to that for cipher selection (see section 4.20). -4.20.1.1 GSSAPI-based key exchange +4.18.1.1 GSSAPI-based key exchange PuTTY supports a set of key exchange methods that also incorporates GSSAPI-based authentication. They are enabled with the `Attempt @@ -3647,7 +3648,7 @@ Chapter 4: Configuring PuTTY The advantage of doing GSSAPI authentication as part of the SSH key exchange is apparent when you are using credential delegation - (see section 4.24.1). The SSH key exchange can be repeated later in + (see section 4.22.1). The SSH key exchange can be repeated later in the session, and this allows your Kerberos V5 credentials (which are typically short-lived) to be automatically re-delegated to the server when they are refreshed on the client. (This feature is @@ -3667,7 +3668,7 @@ Chapter 4: Configuring PuTTY the Kerberos exchange will verify the identity of the host you connect to, at the same time as verifying your identity to it. -4.20.2 Repeat key exchange +4.18.2 Repeat key exchange If the session key negotiated at connection startup is used too much or for too long, it may become feasible to mount attacks against the @@ -3737,7 +3738,7 @@ Chapter 4: Configuring PuTTY same problems. The SSH-1 protocol, incidentally, has even weaker integrity protection than SSH-2 without rekeys. - 4.21 The Host Keys panel + 4.19 The Host Keys panel The Host Keys panel allows you to configure options related to SSH-2 host key management. @@ -3750,17 +3751,21 @@ Chapter 4: Configuring PuTTY This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all. -4.21.1 Host key type selection +4.19.1 Host key type selection PuTTY supports a variety of SSH-2 host key types, and allows you to choose which one you prefer to use to identify the server. - Configuration is similar to cipher selection (see section 4.22). + Configuration is similar to cipher selection (see section 4.20). PuTTY currently supports the following host key types: - `Ed25519': Edwards-curve DSA using a twisted Edwards curve with modulus 2^255-19. + - `Ed448': another Edwards-curve DSA type, using a larger elliptic + curve with a 448-bit instead of 255-bit modulus (so it has a + higher security level than Ed25519). + - `ECDSA': elliptic curve DSA using one of the NIST-standardised elliptic curves. @@ -3768,20 +3773,41 @@ Chapter 4: Configuring PuTTY - `RSA': the ordinary RSA algorithm. - If PuTTY already has one or more host keys stored for the server, - it will prefer to use one of those, even if the server has a key - type that is higher in the preference order. You can add such a key - to PuTTY's cache from within an existing session using the `Special - Commands' menu; see section 3.1.3.2. + If PuTTY already has one or more host keys stored for the server, it + will by default prefer to use one of those, even if the server has + a key type that is higher in the preference order. You can add such + a key to PuTTY's cache from within an existing session using the + `Special Commands' menu; see section 3.1.3.2. Otherwise, PuTTY will choose a key type based purely on the preference order you specify in the configuration. If the first key type PuTTY finds is below the `warn below here' line, you will see a warning box when you make the connection, - similar to that for cipher selection (see section 4.22). + similar to that for cipher selection (see section 4.20). -4.21.2 Manually configuring host keys +4.19.2 Preferring known host keys + + By default, PuTTY will adjust the preference order for host key + algorithms so that any host keys it already knows are moved to the + top of the list. + + This prevents you from having to check and confirm a new host key + for a server you already had one for (e.g. because the server has + generated an alternative key of a type higher in PuTTY's preference + order, or because you changed the preference order itself). + + However, on the other hand, it can leak information to a listener + in the network about _whether_ you already know a host key for this + server. + + For this reason, this policy is configurable. By turning this + checkbox off, you can reset PuTTY to always use the exact order of + host key algorithms configured in the preference list described in + section 4.19.1, so that a listener will find out nothing about what + keys you had stored. + +4.19.3 Manually configuring host keys In some situations, if PuTTY's automated host key management is not doing what you need, you might need to manually configure PuTTY to @@ -3799,7 +3825,7 @@ Chapter 4: Configuring PuTTY is running in a Windows environment without access to the Registry. In that situation, you will probably want to use the -hostkey command-line option to configure the expected host key(s); see - section 3.8.3.20. + section 3.11.3.21. For situations where PuTTY's automated host key management simply picks the wrong host name to store a key under, you may want to @@ -3815,9 +3841,13 @@ Chapter 4: Configuring PuTTY The text describing a host key can be in one of the following formats: - - An MD5-based host key fingerprint of the form displayed in - PuTTY's Event Log and host key dialog boxes, i.e. sixteen 2- - digit hex numbers separated by colons. + - An SHA-256-based host key fingerprint of the form displayed in + PuTTY's Event Log and host key dialog boxes, i.e. `SHA256:' + followed by 43 case-sensitive characters. + + - An MD5-based host key fingerprint, i.e. sixteen 2-digit hex + numbers separated by colons, optionally preceded by the prefix + `MD5:'. (The case of the characters does not matter.) - A base64-encoded blob describing an SSH-2 public key in OpenSSH's one-line public key format. How you acquire @@ -3835,7 +3865,7 @@ Chapter 4: Configuring PuTTY If the box is empty (as it usually is), then PuTTY's automated host key management will work as normal. - 4.22 The Cipher panel + 4.20 The Cipher panel PuTTY supports a variety of different encryption algorithms, and allows you to choose which one you prefer to use. You can do this by @@ -3886,12 +3916,12 @@ Chapter 4: Configuring PuTTY `Enable legacy use of single-DES in SSH-2' option; by default this is disabled and PuTTY will stick to recommended ciphers. - 4.23 The Auth panel + 4.21 The Auth panel The Auth panel allows you to configure authentication options for SSH sessions. -4.23.1 `Display pre-authentication banner' +4.21.1 `Display pre-authentication banner' SSH-2 servers can provide a message for clients to display to the prospective user before the user logs in; this is sometimes known @@ -3904,7 +3934,7 @@ Chapter 4: Configuring PuTTY design). By unchecking this option, display of the banner can be suppressed entirely. -4.23.2 `Bypass authentication entirely' +4.21.2 `Bypass authentication entirely' In SSH-2, it is in principle possible to establish a connection without using SSH's mechanisms to identify or prove who you are to @@ -3923,13 +3953,55 @@ Chapter 4: Configuring PuTTY also probably not what if you're trying to set up passwordless login to a mainstream SSH server; depending on the server, you probably wanted public-key authentication (chapter 8) or perhaps - GSSAPI authentication (section 4.24). (These are still forms of + GSSAPI authentication (section 4.22). (These are still forms of authentication, even if you don't have to interact with them.) This option only affects SSH-2 connections. SSH-1 connections always require an authentication step. -4.23.3 `Attempt authentication using Pageant' +4.21.3 `Disconnect if authentication succeeds trivially' + + This option causes PuTTY to abandon an SSH session and disconnect + from the server, if the server accepted authentication without ever + having asked for any kind of password or signature or token. + + This might be used as a security measure. There are some forms + of attack against an SSH client user which work by terminating + the SSH authentication stage early, and then doing something in + the main part of the SSH session which _looks_ like part of the + authentication, but isn't really. + + For example, instead of demanding a signature from your public key, + for which PuTTY would ask for your key's passphrase, a compromised + or malicious server might allow you to log in with no signature or + password at all, and then print a message that _imitates_ PuTTY's + request for your passphrase, in the hope that you would type it in. + (In fact, the passphrase for your public key should not be sent to + any server.) + + PuTTY's main defence against attacks of this type is the `trust + sigil' system: messages in the PuTTY window that are truly + originated by PuTTY itself are shown next to a small copy of the + PuTTY icon, which the server cannot fake when it tries to imitate + the same message using terminal output. + + However, if you think you might be at risk of this kind of thing + anyway (if you don't watch closely for the trust sigils, or if you + think you're at extra risk of one of your servers being malicious), + then you could enable this option as an extra defence. Then, if the + server tries any of these attacks involving letting you through the + authentication stage, PuTTY will disconnect from the server before + it can send a follow-up fake prompt or other type of attack. + + On the other hand, some servers _legitimately_ let you through + the SSH authentication phase trivially, either because they are + genuinely public, or because the important authentication step + happens during the terminal session. (An example might be an SSH + server that connects you directly to the terminal login prompt of a + legacy mainframe.) So enabling this option might cause some kinds of + session to stop working. It's up to you. + +4.21.4 `Attempt authentication using Pageant' If this option is enabled, then PuTTY will look for Pageant (the SSH private-key storage agent) and attempt to authenticate with any @@ -3941,11 +4013,11 @@ Chapter 4: Configuring PuTTY passwords. This option can also be controlled using the `-noagent' command-line - option. See section 3.8.3.9. + option. See section 3.11.3.9. See chapter 9 for more information about Pageant in general. -4.23.4 `Attempt TIS or CryptoCard authentication' +4.21.5 `Attempt TIS or CryptoCard authentication' TIS and CryptoCard authentication are (despite their names) generic forms of simple challenge/response authentication available in SSH @@ -3962,7 +4034,7 @@ Chapter 4: Configuring PuTTY administrator about precisely what form these challenges and responses take. -4.23.5 `Attempt keyboard-interactive authentication' +4.21.6 `Attempt keyboard-interactive authentication' The SSH-2 equivalent of TIS authentication is called `keyboard- interactive'. It is a flexible authentication method using an @@ -3974,7 +4046,7 @@ Chapter 4: Configuring PuTTY PuTTY leaves this option enabled by default, but supplies a switch to turn it off in case you should have trouble with it. -4.23.6 `Allow agent forwarding' +4.21.7 `Allow agent forwarding' This option allows the SSH server to open forwarded connections back to your local copy of Pageant. If you are not running Pageant, this @@ -3982,10 +4054,10 @@ Chapter 4: Configuring PuTTY See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security - risk involved with enabling this option; see section 9.5 for + risk involved with enabling this option; see section 9.6 for details. -4.23.7 `Allow attempted changes of username in SSH-2' +4.21.8 `Allow attempted changes of username in SSH-2' In the SSH-1 protocol, it is impossible to change username after failing to authenticate. So if you mis-type your username at the @@ -4005,7 +4077,7 @@ Chapter 4: Configuring PuTTY your server can cope with it, you can enable the `Allow attempted changes of username' option to modify PuTTY's behaviour. -4.23.8 `Private key file for authentication' +4.21.9 `Private key file for authentication' This box is where you enter the name of your private key file if you are using public key authentication. See chapter 8 for information @@ -4013,7 +4085,7 @@ Chapter 4: Configuring PuTTY This key must be in PuTTY's native format (`*.PPK'). If you have a private key in another format that you want to use with PuTTY, see - section 8.2.12. + section 8.2.14. You can use the authentication agent Pageant so that you do not need to explicitly configure a key here; see chapter 9. @@ -4026,7 +4098,7 @@ Chapter 4: Configuring PuTTY sufficient to identify the key to Pageant, but of course if Pageant isn't present PuTTY can't fall back to using this file itself. - 4.24 The GSSAPI panel + 4.22 The GSSAPI panel The `GSSAPI' subpanel of the `Auth' panel controls the use of GSSAPI authentication. This is a mechanism which delegates the @@ -4044,7 +4116,7 @@ Chapter 4: Configuring PuTTY In the other method, GSSAPI-based authentication is combined with the SSH key exchange phase. If this succeeds, then the SSH - authentication step has nothing left to do. See section 4.20.1.1 for + authentication step has nothing left to do. See section 4.18.1.1 for more information about this method. The checkbox labelled `Attempt GSSAPI key exchange' controls this form. (The same checkbox appears on the `Kex' panel.) @@ -4058,7 +4130,7 @@ Chapter 4: Configuring PuTTY If both of those checkboxes are disabled, PuTTY will not try any form of GSSAPI at all, and the rest of this panel will be unused. -4.24.1 `Allow GSSAPI credential delegation' +4.22.1 `Allow GSSAPI credential delegation' GSSAPI credential delegation is a mechanism for passing on your Kerberos (or other) identity to the session on the SSH server. If @@ -4083,9 +4155,9 @@ Chapter 4: Configuring PuTTY If your connection is not using GSSAPI key exchange, it is possible for the delegation to expire during your session. See section - 4.20.1.1 for more information. + 4.18.1.1 for more information. -4.24.2 Preference order for GSSAPI libraries +4.22.2 Preference order for GSSAPI libraries GSSAPI is a mechanism which allows more than one authentication method to be accessed through the same interface. Therefore, more @@ -4111,11 +4183,11 @@ Chapter 4: Configuring PuTTY of PuTTY, and the same with 64-bit (see question A.6.10). On Unix, shared libraries generally have a .so extension. - 4.25 The TTY panel + 4.23 The TTY panel The TTY panel lets you configure the remote pseudo-terminal. -4.25.1 `Don't allocate a pseudo-terminal' +4.23.1 `Don't allocate a pseudo-terminal' When connecting to a Unix system, most interactive shell sessions are run in a _pseudo-terminal_, which allows the Unix system to @@ -4128,7 +4200,7 @@ Chapter 4: Configuring PuTTY very specialist purposes; although in Plink (see chapter 7) it is the usual way of working. -4.25.2 Sending terminal modes +4.23.2 Sending terminal modes The SSH protocol allows the client to send `terminal modes' for the remote pseudo-terminal. These usually control the server's @@ -4217,7 +4289,7 @@ Chapter 4: Configuring PuTTY - Terminal speeds are configured elsewhere; see section 4.15.4. - 4.26 The X11 panel + 4.24 The X11 panel The X11 panel allows you to configure forwarding of X11 over an SSH connection. @@ -4234,7 +4306,7 @@ Chapter 4: Configuring PuTTY See section 3.4 for more information about X11 forwarding. -4.26.1 Remote X11 authentication +4.24.1 Remote X11 authentication If you are using X11 forwarding, the virtual X server created on the SSH server machine will be protected by authorisation data. This @@ -4279,7 +4351,7 @@ Chapter 4: Configuring PuTTY PuTTY's default is MIT-MAGIC-COOKIE-1. If you change it, you should be sure you know what you're doing. -4.26.2 X authority file for local display +4.24.2 X authority file for local display If you are using X11 forwarding, the local X server to which your forwarded connections are eventually directed may itself require @@ -4298,7 +4370,7 @@ Chapter 4: Configuring PuTTY configuring this option. By default, PuTTY will not attempt to find any authorisation for your local display. - 4.27 The Tunnels panel + 4.25 The Tunnels panel The Tunnels panel allows you to configure tunnelling of arbitrary connection types through an SSH connection. @@ -4383,7 +4455,7 @@ Chapter 4: Configuring PuTTY which host key it should be expecting. See section 4.14.5 for details of this. -4.27.1 Controlling the visibility of forwarded ports +4.25.1 Controlling the visibility of forwarded ports The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine @@ -4402,7 +4474,7 @@ Chapter 4: Configuring PuTTY not all SSH-2 servers support it (OpenSSH 3.0 does not, for example). -4.27.2 Selecting Internet protocol version for forwarded ports +4.25.2 Selecting Internet protocol version for forwarded ports This switch allows you to select a specific Internet protocol (IPv4 or IPv6) for the local end of a forwarded port. By default, it is @@ -4427,7 +4499,7 @@ Chapter 4: Configuring PuTTY `Auto' should always give you a port which you can connect to using either protocol. - 4.28 The Bugs and More Bugs panels + 4.26 The Bugs and More Bugs panels Not all SSH servers work properly. Various existing servers have bugs in them, which can make it impossible for a client to talk to @@ -4453,7 +4525,7 @@ Chapter 4: Configuring PuTTY - `Auto': PuTTY will use the server's version number announcement to try to guess whether or not the server has the bug. -4.28.1 `Chokes on SSH-2 ignore messages' +4.26.1 `Chokes on SSH-2 ignore messages' An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server @@ -4468,13 +4540,13 @@ Chapter 4: Configuring PuTTY server, the session will succeed, but keepalives will not work and the session might be less cryptographically secure than it could be. -4.28.2 `Handles SSH-2 key re-exchange badly' +4.26.2 `Handles SSH-2 key re-exchange badly' Some SSH servers cannot cope with repeat key exchange at all, and will ignore attempts by the client to start one. Since PuTTY pauses the session while performing a repeat key exchange, the effect of this would be to cause the session to hang after an hour (unless - you have your rekey timeout set differently; see section 4.20.2 for + you have your rekey timeout set differently; see section 4.18.2 for more about rekeys). Other, very old, SSH servers handle repeat key exchange even more badly, and disconnect upon receiving a repeat key exchange request. @@ -4486,11 +4558,11 @@ Chapter 4: Configuring PuTTY This is an SSH-2-specific bug. -4.28.3 `Chokes on PuTTY's SSH-2 `winadj' requests' +4.26.3 `Chokes on PuTTY's SSH-2 `winadj' requests' PuTTY sometimes sends a special request to SSH servers in the middle of channel data, with the name winadj@putty.projects.tartarus.org - (see section F.1). The purpose of this request is to measure the + (see section G.1). The purpose of this request is to measure the round-trip time to the server, which PuTTY uses to tune its flow control. The server does not actually have to _understand_ the message; it is expected to send back a SSH_MSG_CHANNEL_FAILURE @@ -4507,7 +4579,7 @@ Chapter 4: Configuring PuTTY its `winadj@putty.projects.tartarus.org' request, and will make do without its timing data. -4.28.4 `Replies to requests on closed channels' +4.26.4 `Replies to requests on closed channels' The SSH protocol as published in RFC 4254 has an ambiguity which arises if one side of a connection tries to close a channel, while @@ -4522,13 +4594,13 @@ Chapter 4: Configuring PuTTY the other policy; for example, OpenSSH used to until it was fixed. Because PuTTY sends channel requests with the `want reply' - flag throughout channels' lifetime (see section 4.28.3), it's + flag throughout channels' lifetime (see section 4.26.3), it's possible that when connecting to such a server it might receive a reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of `Received SSH2_MSG_CHANNEL_FAILURE for nonexistent channel 256'. -4.28.5 `Ignores SSH-2 maximum packet size' +4.26.5 `Ignores SSH-2 maximum packet size' When an SSH-2 channel is set up, each end announces the maximum size of data packet that it is willing to receive for that channel. Some @@ -4542,7 +4614,7 @@ Chapter 4: Configuring PuTTY server, the session will work correctly, but download performance will be less than it could be. -4.28.6 `Requires padding on SSH-2 RSA signatures' +4.26.6 `Requires padding on SSH-2 RSA signatures' Versions below 3.3 of OpenSSH require SSH-2 RSA signatures to be padded with zero bytes to the same length as the RSA key modulus. @@ -4559,7 +4631,7 @@ Chapter 4: Configuring PuTTY This is an SSH-2-specific bug. -4.28.7 `Only supports pre-RFC4419 SSH-2 DH GEX' +4.26.7 `Only supports pre-RFC4419 SSH-2 DH GEX' The SSH key exchange method that uses Diffie-Hellman group exchange was redesigned after its original release, to use a slightly more @@ -4574,7 +4646,7 @@ Chapter 4: Configuring PuTTY This is an SSH-2-specific bug. -4.28.8 `Miscomputes SSH-2 HMAC keys' +4.26.8 `Miscomputes SSH-2 HMAC keys' Versions 2.3.0 and below of the SSH server software from ssh.com compute the keys for their HMAC message authentication codes @@ -4589,7 +4661,7 @@ Chapter 4: Configuring PuTTY This is an SSH-2-specific bug. -4.28.9 `Misuses the session ID in SSH-2 PK auth' +4.26.9 `Misuses the session ID in SSH-2 PK auth' Versions below 2.3 of OpenSSH require SSH-2 public-key authentication to be done slightly differently: the data to be @@ -4605,7 +4677,7 @@ Chapter 4: Configuring PuTTY This is an SSH-2-specific bug. -4.28.10 `Miscomputes SSH-2 encryption keys' +4.26.10 `Miscomputes SSH-2 encryption keys' Versions below 2.0.11 of the SSH server software from ssh.com compute the keys for the session encryption incorrectly. This @@ -4619,7 +4691,7 @@ Chapter 4: Configuring PuTTY This is an SSH-2-specific bug. -4.28.11 `Chokes on SSH-1 ignore messages' +4.26.11 `Chokes on SSH-1 ignore messages' An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server @@ -4632,15 +4704,15 @@ Chapter 4: Configuring PuTTY If this bug is detected, PuTTY will stop using ignore messages. This means that keepalives will stop working, and PuTTY will have to fall back to a secondary defence against SSH-1 password- - length eavesdropping. See section 4.28.12. If this bug is enabled + length eavesdropping. See section 4.26.12. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be more vulnerable to eavesdroppers than it could be. -4.28.12 `Refuses all SSH-1 password camouflage' +4.26.12 `Refuses all SSH-1 password camouflage' When talking to an SSH-1 server which cannot deal with ignore - messages (see section 4.28.11), PuTTY will attempt to disguise the + messages (see section 4.26.11), PuTTY will attempt to disguise the length of the user's password by sending additional padding _within_ the password packet. This is technically a violation of the SSH- 1 specification, and so PuTTY will only do it when it cannot use @@ -4660,7 +4732,7 @@ Chapter 4: Configuring PuTTY This is an SSH-1-specific bug. SSH-2 is secure against this type of attack. -4.28.13 `Chokes on SSH-1 RSA authentication' +4.26.13 `Chokes on SSH-1 RSA authentication' Some SSH-1 servers cannot deal with RSA authentication messages at all. If Pageant is running and contains any SSH-1 keys, PuTTY will @@ -4675,12 +4747,57 @@ Chapter 4: Configuring PuTTY This is an SSH-1-specific bug. - 4.29 The Serial panel + 4.27 The `Bare ssh-connection' protocol + + In addition to SSH itself, PuTTY also supports a second protocol + that is derived from SSH. It's listed in the PuTTY GUI under the + name `Bare ssh-connection'. + + This protocol consists of just the innermost of SSH-2's three + layers: it leaves out the cryptography layer providing network + security, and it leaves out the authentication layer where you + provide a username and prove you're allowed to log in as that user. + + It is therefore *completely unsuited to any network connection*. + Don't try to use it over a network! + + The purpose of this protocol is for various specialist circumstances + in which the `connection' is not over a real network, but is a pipe + or IPC channel between different processes running on the _same_ + computer. In these contexts, the operating system will already have + guaranteed that each of the two communicating processes is owned by + the expected user (so that no authentication is necessary), and that + the communications channel cannot be tapped by a hostile user on the + same machine (so that no cryptography is necessary either). Examples + of possible uses involve communicating with a strongly separated + context such as the inside of a container, or a VM, or a different + network namespace. + + Explicit support for this protocol is new in PuTTY 0.75. As of 2021- + 04, the only known server for the bare ssh-connection protocol is + the Unix program `psusan' that is also part of the PuTTY tool suite. + + (However, this protocol is also the same one used between instances + of PuTTY to implement connection sharing: see section 4.17.5. In + fact, in the Unix version of PuTTY, when a sharing upstream records + `Sharing this connection at [pathname]' in the Event Log, it's + possible to connect another instance of PuTTY directly to that Unix + socket, by entering its pathname in the host name box and selecting + `Bare ssh-connection' as the protocol!) + + Many of the options under the SSH panel also affect this protocol, + although options to do with cryptography and authentication do not, + for obvious reasons. + + I repeat, *DON'T TRY TO USE THIS PROTOCOL FOR NETWORK CONNECTIONS!* + That's not what it's for, and it's not at all safe to do it. + + 4.28 The Serial panel The Serial panel allows you to configure options that only apply when PuTTY is connecting to a local serial line. -4.29.1 Selecting a serial line to connect to +4.28.1 Selecting a serial line to connect to The `Serial line to connect to' box allows you to choose which serial line you want PuTTY to talk to, if your computer has more @@ -4693,7 +4810,7 @@ Chapter 4: Configuring PuTTY where it replaces the `Host Name' box (see section 4.1.1) if the connection type is set to `Serial'. -4.29.2 Selecting the speed of your serial line +4.28.2 Selecting the speed of your serial line The `Speed' box allows you to choose the speed (or `baud rate') at which to talk to the serial line. Typical values might be 9600, @@ -4705,18 +4822,18 @@ Chapter 4: Configuring PuTTY where it replaces the `Port' box (see section 4.1.1) if the connection type is set to `Serial'. -4.29.3 Selecting the number of data bits +4.28.3 Selecting the number of data bits The `Data bits' box allows you to choose how many data bits are transmitted in each byte sent or received through the serial line. Typical values are 7 or 8. -4.29.4 Selecting the number of stop bits +4.28.4 Selecting the number of stop bits The `Stop bits' box allows you to choose how many stop bits are used in the serial line protocol. Typical values are 1, 1.5 or 2. -4.29.5 Selecting the serial parity checking scheme +4.28.5 Selecting the serial parity checking scheme The `Parity' box allows you to choose what type of parity checking is used on the serial line. The settings are: @@ -4735,7 +4852,7 @@ Chapter 4: Configuring PuTTY - `Space': an extra parity bit is sent alongside each byte, and always set to 0. -4.29.6 Selecting the serial flow control scheme +4.28.6 Selecting the serial flow control scheme The `Flow control' box allows you to choose what type of flow control checking is used on the serial line. The settings are: @@ -4752,7 +4869,163 @@ Chapter 4: Configuring PuTTY - `DSR/DTR': flow control is done using the DSR and DTR wires on the serial line. - 4.30 Storing configuration in a file + 4.29 The Telnet panel + + The Telnet panel allows you to configure options that only apply to + Telnet sessions. + +4.29.1 `Handling of OLD_ENVIRON ambiguity' + + The original Telnet mechanism for passing environment variables was + badly specified. At the time the standard (RFC 1408) was written, + BSD telnet implementations were already supporting the feature, and + the intention of the standard was to describe the behaviour the BSD + implementations were already using. + + Sadly there was a typing error in the standard when it was issued, + and two vital function codes were specified the wrong way round. BSD + implementations did not change, and the standard was not corrected. + Therefore, it's possible you might find either BSD or RFC-compliant + implementations out there. This switch allows you to choose which + one PuTTY claims to be. + + The problem was solved by issuing a second standard, defining a + new Telnet mechanism called NEW_ENVIRON, which behaved exactly + like the original OLD_ENVIRON but was not encumbered by existing + implementations. Most Telnet servers now support this, and it's + unambiguous. This feature should only be needed if you have trouble + passing environment variables to quite an old server. + +4.29.2 Passive and active Telnet negotiation modes + + In a Telnet connection, there are two types of data passed between + the client and the server: actual text, and _negotiations_ about + which Telnet extra features to use. + + PuTTY can use two different strategies for negotiation: + + - In _active_ mode, PuTTY starts to send negotiations as soon as + the connection is opened. + + - In _passive_ mode, PuTTY will wait to negotiate until it sees a + negotiation from the server. + + The obvious disadvantage of passive mode is that if the server is + also operating in a passive mode, then negotiation will never begin + at all. For this reason PuTTY defaults to active mode. + + However, sometimes passive mode is required in order to successfully + get through certain types of firewall and Telnet proxy server. If + you have confusing trouble with a firewall, you could try enabling + passive mode to see if it helps. + +4.29.3 `Keyboard sends Telnet special commands' + + If this box is checked, several key sequences will have their normal + actions modified: + + - the Backspace key on the keyboard will send the Telnet special + backspace code; + + - Control-C will send the Telnet special Interrupt Process code; + + - Control-Z will send the Telnet special Suspend Process code. + + You probably shouldn't enable this unless you know what you're + doing. + +4.29.4 `Return key sends Telnet New Line instead of ^M' + + Unlike most other remote login protocols, the Telnet protocol has + a special `new line' code that is not the same as the usual line + endings of Control-M or Control-J. By default, PuTTY sends the + Telnet New Line code when you press Return, instead of sending + Control-M as it does in most other protocols. + + Most Unix-style Telnet servers don't mind whether they receive + Telnet New Line or Control-M; some servers do expect New Line, + and some servers prefer to see ^M. If you are seeing surprising + behaviour when you press Return in a Telnet session, you might try + turning this option off to see if it helps. + + 4.30 The Rlogin panel + + The Rlogin panel allows you to configure options that only apply to + Rlogin sessions. + +4.30.1 `Local username' + + Rlogin allows an automated (password-free) form of login by means + of a file called `.rhosts' on the server. You put a line in your + `.rhosts' file saying something like `jbloggs@pc1.example.com', and + then when you make an Rlogin connection the client transmits the + username of the user running the Rlogin client. The server checks + the username and hostname against `.rhosts', and if they match it + does not ask for a password. + + This only works because Unix systems contain a safeguard to stop a + user from pretending to be another user in an Rlogin connection. + Rlogin connections have to come from port numbers below 1024, and + Unix systems prohibit this to unprivileged processes; so when the + server sees a connection from a low-numbered port, it assumes the + client end of the connection is held by a privileged (and therefore + trusted) process, so it believes the claim of who the user is. + + Windows does not have this restriction: _any_ user can initiate + an outgoing connection from a low-numbered port. Hence, the + Rlogin `.rhosts' mechanism is completely useless for securely + distinguishing several different users on a Windows machine. If you + have a `.rhosts' entry pointing at a Windows PC, you should assume + that _anyone_ using that PC can spoof your username in an Rlogin + connection and access your account on the server. + + The `Local username' control allows you to specify what user name + PuTTY should claim you have, in case it doesn't match your Windows + user name (or in case you didn't bother to set up a Windows user + name). + + 4.31 The SUPDUP panel + + The SUPDUP panel allows you to configure options that only apply + to SUPDUP sessions. See section 3.10 for more about the SUPDUP + protocol. + +4.31.1 `Location string' + + In SUPDUP, the client sends a piece of text of its choice to the + server giving the user's location. This is typically displayed in + lists of logged-in users. + + By default, PuTTY just defaults this to "The Internet". If you + want your location to show up as something more specific, you can + configure it here. + +4.31.2 `Extended ASCII Character set' + + This declares what kind of character set extension your terminal + supports. If the server supports it, it will send text using + that character set. `None' means the standard 95 printable ASCII + characters. `ITS' means ASCII extended with printable characters in + the control character range. This character set is documented in the + SUPDUP protocol definition. `WAITS' is similar to `ITS' but uses + some alternative characters in the extended set: most prominently, + it will display arrows instead of `^' and `_', and `}' instead of + `~'. `ITS' extended ASCII is used by ITS and Lisp machines, whilst + `WAITS' is only used by the WAITS operating system from the Stanford + AI Laboratory. + +4.31.3 `**MORE** processing' + + When **MORE** processing is enabled, the server causes output to + pause at the bottom of the screen, until a space is typed. + +4.31.4 `Terminal scrolling' + + This controls whether the terminal will perform scrolling then the + cursor goes below the last line, or if the cursor will return to the + first line. + + 4.32 Storing configuration in a file PuTTY does not currently support storing its configuration in a file instead of the Registry. However, you can work around this with a @@ -4835,9 +5108,9 @@ Chapter 5: Using PSCP to transfer files securely version of PSCP you're using, and gives you a brief summary of how to use PSCP: - Z:\owendadmin>pscp + C:\>pscp PuTTY Secure Copy client - Release 0.73 + Release 0.76 Usage: pscp [options] [user@]host:source target pscp [options] source [source...] [user@]host:target pscp [options] -ls [user@]host:filespec @@ -4853,12 +5126,16 @@ Chapter 5: Using PSCP to transfer files securely -l user connect with specified username -pw passw login with specified password -1 -2 force use of particular SSH protocol version + -ssh -ssh-connection + force use of particular SSH protocol variant -4 -6 force use of IPv4 or IPv6 -C enable compression -i key private key file for user authentication -noagent disable use of Pageant -agent enable use of Pageant - -hostkey aa:bb:cc:... + -no-trivial-auth + disconnect if SSH authentication succeeds trivially + -hostkey keyid manually specify a host key (may be repeated) -batch disable all interactive prompts -no-sanitise-stderr don't strip control chars from standard error @@ -4870,6 +5147,9 @@ Chapter 5: Using PSCP to transfer files securely -sshlog file -sshrawlog file log protocol details to a file + -logoverwrite + -logappend + control what happens when a log file already exists (PSCP's interface is much like the Unix `scp' command, if you're familiar with that.) @@ -4985,7 +5265,7 @@ Chapter 5: Using PSCP to transfer files securely PSCP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer - utility. See section 3.8.3 for a description of these options. (The + utility. See section 3.11.3 for a description of these options. (The ones not supported by PSCP are clearly marked.) PSCP also supports some of its own options. The following sections @@ -5045,7 +5325,7 @@ Chapter 5: Using PSCP to transfer files securely using `-batch', if something goes wrong at connection time, the batch job will fail rather than hang. -5.2.2.6 `-sftp', `-scp' force use of particular protocol +5.2.2.6 `-sftp', `-scp' force use of particular file transfer protocol As mentioned in section 5.2.1, there are two different file transfer protocols in use with SSH. Despite its name, PSCP (like many other @@ -5102,7 +5382,7 @@ Chapter 5: Using PSCP to transfer files securely (see section 5.2.1.2). So you would do this: - Run PuTTY, and create a PuTTY saved session (see section 4.1.2) - which specifies your private key file (see section 4.23.8). You + which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1). @@ -5111,7 +5391,7 @@ Chapter 5: Using PSCP to transfer files securely `sessionname' is replaced by the name of your saved session. Secondly, you can supply the name of a private key file on the - command line, with the `-i' option. See section 3.8.3.18 for more + command line, with the `-i' option. See section 3.11.3.18 for more information. Thirdly, PSCP will attempt to authenticate using Pageant if Pageant @@ -5177,7 +5457,7 @@ Chapter 6: Using PSFTP to transfer files securely PSFTP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer - utility. See section 3.8.3 for a description of these options. (The + utility. See section 3.11.3 for a description of these options. (The ones not supported by PSFTP are clearly marked.) PSFTP also supports some of its own options. The following sections @@ -5684,7 +5964,7 @@ Chapter 6: Using PSFTP to transfer files securely So you might do this: - Run PuTTY, and create a PuTTY saved session (see section 4.1.2) - which specifies your private key file (see section 4.23.8). You + which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1). @@ -5693,7 +5973,7 @@ Chapter 6: Using PSFTP to transfer files securely replaced by the name of your saved session. Secondly, you can supply the name of a private key file on the - command line, with the `-i' option. See section 3.8.3.18 for more + command line, with the `-i' option. See section 3.11.3.18 for more information. Thirdly, PSFTP will attempt to authenticate using Pageant if Pageant @@ -5750,9 +6030,9 @@ Chapter 7: Using the command-line connection tool Plink version of Plink you're using, and gives you a brief summary of how to use Plink: - Z:\sysosd>plink + C:\>plink Plink: command-line connection utility - Release 0.73 + Release 0.76 Usage: plink [options] [user@]host [command] ("host" can also be a PuTTY saved session name) Options: @@ -5762,6 +6042,8 @@ Chapter 7: Using the command-line connection tool Plink -load sessname Load settings from saved session -ssh -telnet -rlogin -raw -serial force use of a particular protocol + -ssh-connection + force use of the bare ssh-connection protocol -P port connect to specified port -l user connect with specified username -batch disable all interactive prompts @@ -5780,15 +6062,17 @@ Chapter 7: Using the command-line connection tool Plink -X -x enable / disable X11 forwarding -A -a enable / disable agent forwarding -t -T enable / disable pty allocation - -1 -2 force use of particular protocol version + -1 -2 force use of particular SSH protocol version -4 -6 force use of IPv4 or IPv6 -C enable compression -i key private key file for user authentication -noagent disable use of Pageant -agent enable use of Pageant + -no-trivial-auth + disconnect if SSH authentication succeeds trivially -noshare disable use of connection sharing -share enable use of connection sharing - -hostkey aa:bb:cc:... + -hostkey keyid manually specify a host key (may be repeated) -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout do/don't strip control chars from standard output/error @@ -5801,6 +6085,9 @@ Chapter 7: Using the command-line connection tool Plink -sshlog file -sshrawlog file log protocol details to a file + -logoverwrite + -logappend + control what happens when a log file already exists -shareexists test whether a connection-sharing upstream exists @@ -5811,7 +6098,7 @@ Chapter 7: Using the command-line connection tool Plink To make a simple interactive connection to a remote server, just type `plink' and then the host name: - Z:\sysosd>plink login.example.com + C:\>plink login.example.com Debian GNU/Linux 2.2 flunky.example.com flunky login: @@ -5825,10 +6112,10 @@ Chapter 7: Using the command-line connection tool Plink this are not the main point of Plink. In order to connect with a different protocol, you can give the - command line options `-ssh', `-telnet', `-rlogin' or `-raw'. To make - an SSH connection, for example: + command line options `-ssh', `-ssh-connection', `-telnet', `- + rlogin', or `-raw'. To make an SSH connection, for example: - Z:\sysosd>plink -ssh login.example.com + C:\>plink -ssh login.example.com login as: If you have already set up a PuTTY saved session, then instead of @@ -5836,14 +6123,14 @@ Chapter 7: Using the command-line connection tool Plink allows you to use public-key authentication, specify a user name, and use most of the other features of PuTTY: - Z:\sysosd>plink my-ssh-session + C:\>plink my-ssh-session Sent username "fred" Authenticating with public key "fred@winbox" Last login: Thu Dec 6 19:25:33 2001 from :0.0 fred@flunky:~$ (You can also use the `-load' command-line option to load a saved - session; see section 3.8.3.1. If you use `-load', the saved session + session; see section 3.11.3.1. If you use `-load', the saved session exists, and it specifies a hostname, you cannot also specify a `host' or `user@host' argument - it will be treated as part of the remote command.) @@ -5873,12 +6160,14 @@ Chapter 7: Using the command-line connection tool Plink enter a password. To avoid being prompted for the server host key when using Plink for - an automated connection, you should first make a _manual_ connection + an automated connection, you can first make a _manual_ connection (using either of PuTTY or Plink) to the same server, verify the host - key (see section 2.2 for more information), and select Yes to add - the host key to the Registry. After that, Plink commands connecting - to that server should not give a host key prompt unless the host key - changes. + key (see section 2.2 for more information), and select `Accept' + to add the host key to the Registry. After that, Plink commands + connecting to that server should not give a host key prompt unless + the host key changes. Alternatively, you can specify the appropriate + host key(s) on Plink's command line every time you use it; see + section 3.11.3.21. To avoid being prompted for a user name, you can: @@ -5896,7 +6185,7 @@ Chapter 7: Using the command-line connection tool Plink - Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies a private key file - (see section 4.23.8). For this to work without prompting, your + (see section 4.21.9). For this to work without prompting, your private key will need to have no passphrase. - Store the private key in Pageant. See chapter 9 for further @@ -5906,18 +6195,18 @@ Chapter 7: Using the command-line connection tool Plink command on the SSH server machine and have it execute automatically with no prompting: - Z:\sysosd>plink login.example.com -l fred echo hello, world + C:\>plink login.example.com -l fred echo hello, world hello, world - Z:\sysosd> + C:\> Or, if you have set up a saved session with all the connection details: - Z:\sysosd>plink mysession echo hello, world + C:\>plink mysession echo hello, world hello, world - Z:\sysosd> + C:\> Then you can set up other programs to run this Plink command and talk to it as if it were a process on the server machine. @@ -5925,7 +6214,7 @@ Chapter 7: Using the command-line connection tool Plink 7.2.3 Plink command line options Plink accepts all the general command line options supported by the - PuTTY tools. See section 3.8.3 for a description of these options. + PuTTY tools. See section 3.11.3 for a description of these options. Plink also supports some of its own options. The following sections describe Plink's specific command-line options. @@ -5952,7 +6241,7 @@ Chapter 7: Using the command-line connection tool Plink 7.2.3.3 `-share': Test and try to share an existing connection. This option tris to detect if an existing connection can be shared - (See section 4.19.5 for more information about SSH connection + (See section 4.17.5 for more information about SSH connection sharing.) and reuses that connection. A Plink invocation of the form: @@ -5973,7 +6262,7 @@ Chapter 7: Using the command-line connection tool Plink This option does not make a new connection; instead it allows testing for the presence of an existing connection that can - be shared. (See section 4.19.5 for more information about SSH + be shared. (See section 4.17.5 for more information about SSH connection sharing.) A Plink invocation of the form: @@ -6007,34 +6296,34 @@ Chapter 7: Using the command-line connection tool Plink output stream going somewhere else is likely to be needed by an 8- bit protocol and must not be tampered with at all.) It also stops happening if you tell Plink to allocate a remote pseudo-terminal - (see section 3.8.3.12 and section 4.25.1), on the basis that in that - situation you often _want_ escape sequences from the server to go to - your terminal. + (see section 3.11.3.12 and section 4.23.1), on the basis that in + that situation you often _want_ escape sequences from the server to + go to your terminal. But in case Plink guesses wrong about whether you want this sanitisation, you can override it in either direction, using one of these options: - `-sanitise-stderr' + `-sanitise-stderr' - Sanitise server data written to Plink's standard error channel, - regardless of terminals and consoles and remote ptys. + Sanitise server data written to Plink's standard error channel, + regardless of terminals and consoles and remote ptys. - `-no-sanitise-stderr' + `-no-sanitise-stderr' - Do not sanitise server data written to Plink's standard error - channel. + Do not sanitise server data written to Plink's standard error + channel. - `-sanitise-stdout' + `-sanitise-stdout' - Sanitise server data written to Plink's standard output channel. + Sanitise server data written to Plink's standard output channel. - `-no-sanitise-stdout' + `-no-sanitise-stdout' - Do not sanitise server data written to Plink's standard output - channel. + Do not sanitise server data written to Plink's standard output + channel. -7.2.3.6 : turn off authentication spoofing protection prompt +7.2.3.6 -no-antispoof: turn off authentication spoofing protection prompt In SSH, some possible server authentication methods require user input (for example, password authentication, or entering a private @@ -6204,7 +6493,7 @@ Chapter 8: Using public keys for SSH authentication PuTTYgen is a key generator. It generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see chapter 9). PuTTYgen - generates RSA, DSA, ECDSA, and Ed25519 keys. + generates RSA, DSA, ECDSA, and EdDSA keys. When you run PuTTYgen you will see a window where you have two main choices: `Generate', to generate a new public/private key pair, or @@ -6220,20 +6509,20 @@ Chapter 8: Using public keys for SSH authentication described in more detail in section 8.2.2 and section 8.2.3. - Then press the `Generate' button, to actually generate the key. - Section 8.2.4 describes this step. + Section 8.2.5 describes this step. - Once you have generated the key, select a comment field (section - 8.2.6) and a passphrase (section 8.2.7). + 8.2.7) and a passphrase (section 8.2.8). - Now you're ready to save the private key to disk; press the - `Save private key' button. (See section 8.2.8). + `Save private key' button. (See section 8.2.9). Your key pair is now ready for use. You may also want to copy the public key to your server, either by copying it out of the `Public key for pasting into OpenSSH authorized_keys file' box (see section - 8.2.10), or by using the `Save public key' button (section 8.2.9). + 8.2.11), or by using the `Save public key' button (section 8.2.10). However, you don't need to do this immediately; if you want, you can - load the private key back into PuTTYgen later (see section 8.2.11) + load the private key back into PuTTYgen later (see section 8.2.13) and the public key will be available for copying and pasting again. Section 8.3 describes the typical process of configuring PuTTY to @@ -6246,7 +6535,8 @@ Chapter 8: Using public keys for SSH authentication which type of key you need. The current version of the SSH protocol, SSH-2, supports several - different key types. PuTTYgen can generate: + different key types, although specific servers may not support all + of them. PuTTYgen can generate: - An RSA key for use with the SSH-2 protocol. @@ -6255,8 +6545,8 @@ Chapter 8: Using public keys for SSH authentication - An ECDSA (elliptic curve DSA) key for use with the SSH-2 protocol. - - An Ed25519 key (another elliptic curve algorithm) for use with - the SSH-2 protocol. + - An EdDSA key (Edwards-curve DSA, another elliptic curve + algorithm) for use with the SSH-2 protocol. PuTTYgen can also generate an RSA key suitable for use with the old SSH-1 protocol (which only supports RSA); for this, you need @@ -6268,15 +6558,66 @@ Chapter 8: Using public keys for SSH authentication The `Number of bits' input box allows you to choose the strength of the key PuTTYgen will generate. - - For RSA, 2048 bits should currently be sufficient for most - purposes. + - For RSA and DSA, 2048 bits should currently be sufficient for + most purposes. - For ECDSA, only 256, 384, and 521 bits are supported. (ECDSA offers equivalent security to RSA with smaller key sizes.) - - For Ed25519, the only valid size is 256 bits. + - For EdDSA, the only valid sizes are 255 bits (these keys are + also known as `Ed25519' and are commonly used) and 448 bits + (`Ed448', which is much less common at the time of writing). + (256 is also accepted for backward compatibility, but the effect + is the same as 255.) - 8.2.4 The `Generate' button + 8.2.4 Selecting the prime generation method + + On the `Key' menu, you can also optionally change the method for + generating the prime numbers used in the generated key. This is + used for RSA and DSA keys only. (The other key types don't require + generating prime numbers at all.) + + The prime-generation method does not affect compatibility: a key + generated with any of these methods will still work with all the + same SSH servers. + + If you don't care about this, it's entirely sensible to leave it on + the default setting. + + The available methods are: + + - Use probable primes (fast) + + - Use proven primes (slower) + + - Use proven primes with even distribution (slowest) + + The `probable primes' method sounds unsafe, but it's the most + commonly used prime-generation strategy. There is in theory a + possibility that it might accidentally generate a number that + isn't prime, but the software does enough checking to make that + probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). + So, in practice, nobody worries about it very much. + + The other methods cause PuTTYgen to use numbers that it is _sure_ + are prime, because it generates the output number together with a + proof of its primality. This takes more effort, but it eliminates + that theoretical risk in the probabilistic method. + + You might choose to switch from probable to proven primes if you + have a local security standard that demands it, or if you don't + trust the probabilistic argument for the safety of the usual method. + + For RSA keys, there's also an option on the `Key' menu to use + `strong' primes as the prime factors of the public key. A `strong' + prime is a prime number chosen to have a particular structure that + makes certain factoring algorithms more difficult to apply, so some + security standards recommend their use. However, the most modern + factoring algorithms are unaffected, so this option is probably not + worth turning on _unless_ you have a local standard that recommends + it. + + 8.2.5 The `Generate' button Once you have chosen the type of key you want, and the strength of the key, press the `Generate' button and PuTTYgen will begin the @@ -6302,7 +6643,7 @@ Chapter 8: Using public keys for SSH authentication When the key generation is complete, a new set of controls will appear in the window to indicate this. - 8.2.5 The `Key fingerprint' box + 8.2.6 The `Key fingerprint' box The `Key fingerprint' box shows you a fingerprint value for the generated key. This is derived cryptographically from the _public_ @@ -6316,7 +6657,14 @@ Chapter 8: Using public keys for SSH authentication list box (see section 9.2.1) and the Unix `ssh-add' utility, will list key fingerprints rather than the whole public key. - 8.2.6 Setting a comment for your key + By default, PuTTYgen will display fingerprints in the `SHA256' + format. If you need to see the fingerprint in the older `MD5' format + (which looks like `aa:bb:cc:...'), you can choose `Show fingerprint + as MD5' from the `Key' menu, but bear in mind that this is less + cryptographically secure; it may be feasible for an attacker to + create a key with the same fingerprint as yours. + + 8.2.7 Setting a comment for your key If you have more than one key and use them for different purposes, you don't need to memorise the key fingerprints in order to tell @@ -6334,7 +6682,7 @@ Chapter 8: Using public keys for SSH authentication the comment later, you can load the private key back into PuTTYgen, change the comment, and save it again. - 8.2.7 Setting a passphrase for your key + 8.2.8 Setting a passphrase for your key The `Key passphrase' and `Confirm passphrase' boxes allow you to choose a passphrase for your key. The passphrase will be used to @@ -6375,7 +6723,7 @@ Chapter 8: Using public keys for SSH authentication _Do not forget your passphrase_. There is no way to recover it. - 8.2.8 Saving your private key to a disk file + 8.2.9 Saving your private key to a disk file Once you have generated a key, set a comment field and set a passphrase, you are ready to save your private key to disk. @@ -6386,15 +6734,19 @@ Chapter 8: Using public keys for SSH authentication This file is in PuTTY's native format (`*.PPK'); it is the one you will need to tell PuTTY to use for authentication (see section - 4.23.8) or tell Pageant to load (see section 9.2.2). + 4.21.9) or tell Pageant to load (see section 9.2.2). + + (You can optionally change some details of the PPK format for your + saved key files; see section 8.2.12. But The defaults should be fine + for most purposes.) - 8.2.9 Saving your public key to a disk file +8.2.10 Saving your public key to a disk file RFC 4716 specifies a standard format for storing SSH-2 public keys on disk. Some SSH servers (such as ssh.com's) require a public key in this format in order to accept authentication with the corresponding private key. (Others, such as OpenSSH, use a different - format; see section 8.2.10.) + format; see section 8.2.11.) To save your public key in the SSH-2 standard format, press the `Save public key' button in PuTTYgen. PuTTYgen will put up a dialog @@ -6410,7 +6762,7 @@ Chapter 8: Using public keys for SSH authentication for pasting' box. This is the only existing standard for SSH-1 public keys. -8.2.10 `Public key for pasting into OpenSSH authorized_keys file' +8.2.11 `Public key for pasting into OpenSSH authorized_keys file' The OpenSSH server, among others, requires your public key to be given to it in a one-line format before it will accept @@ -6426,7 +6778,62 @@ Chapter 8: Using public keys for SSH authentication See section 8.3 for general instructions on configuring public-key authentication once you have generated a key. -8.2.11 Reloading a private key +8.2.12 Parameters for saving key files + + Selecting `Parameters for saving key files...' from the `Key' + menu lets you adjust some aspects of PPK-format private key files + stored on disk. None of these options affect compatibility with SSH + servers. + + In most cases, it's entirely sensible to leave all of these at their + default settings. + +8.2.12.1 PPK file version + + This defaults to version 3, which is fine for most uses. + + You might need to select PPK version 2 if you need your private key + file to be loadable in older versions of PuTTY (0.74 and older), or + in other tools which do not yet support the version 3 format (which + was introduced in 2021). + + The version 2 format is less resistant to brute-force decryption, + and doesn't support any of the following options to control that. + +8.2.12.2 Options affecting passphrase hashing + + All of the following options only affect keys saved with + passphrases. They control how much work is required to decrypt the + key (which happens every time you type its passphrase). This allows + you to trade off the cost of legitimate use of the key against the + resistance of the encrypted key to password-guessing attacks. + + These options only affect PPK version 3. + + Key derivation function + + The variant of the Argon2 key derivation function to use. You + might change this if you consider your exposure to side-channel + attacks to be different to the norm. + + Memory to use for passphrase hash + + The amount of memory needed to decrypt the key, in Kbyte. + + Time to use for passphrase hash + + Controls how much time is required to attempt decrypting the + key. You can either specify an approximate time in milliseconds + (on this machine), or explicitly specify a number of hash passes + (which is what the time is turned into during encryption). + + Parallelism for passphrase hash + + Number of parallelisable threads that can be used to decrypt the + key. The default, 1, forces the process to run single-threaded, + even on machines with multiple cores. + +8.2.13 Reloading a private key PuTTYgen allows you to load an existing private key file into memory. If you do this, you can then change the passphrase and @@ -6441,10 +6848,10 @@ Chapter 8: Using public keys for SSH authentication If you use the Load command to load a foreign key format, it will work, but you will see a message box warning you that the key you - have loaded is not a PuTTY native key. See section 8.2.12 for + have loaded is not a PuTTY native key. See section 8.2.14 for information about importing foreign key formats. -8.2.12 Dealing with private keys in other formats +8.2.14 Dealing with private keys in other formats SSH-2 private keys have no standard format. OpenSSH and ssh.com have different formats, and PuTTY's is different again. So a key @@ -6463,7 +6870,7 @@ Chapter 8: Using public keys for SSH authentication PuTTYgen can also export private keys in OpenSSH format and in ssh.com format. To do so, select one of the `Export' options from the `Conversions' menu. Exporting a key works exactly like saving - it (see section 8.2.8) - you need to have typed your passphrase in + it (see section 8.2.9) - you need to have typed your passphrase in beforehand, and you will be warned if you are about to save a key without a passphrase. @@ -6495,7 +6902,7 @@ Chapter 8: Using public keys for SSH authentication to create this file, if this is the first key you have put in it.) Then switch to the PuTTYgen window, select all of the text in the `Public key for pasting into OpenSSH authorized_keys - file' box (see section 8.2.10), and copy it to the clipboard + file' box (see section 8.2.11), and copy it to the clipboard (`Ctrl+C'). Then, switch back to the PuTTY window and insert the data into the open file, making sure it ends up all on one line. Save the file. @@ -6506,7 +6913,7 @@ Chapter 8: Using public keys for SSH authentication and SSH-2 keys.) - If your server is ssh.com's product and is using SSH-2, you need - to save a _public_ key file from PuTTYgen (see section 8.2.9), + to save a _public_ key file from PuTTYgen (see section 8.2.10), and copy that into the `.ssh2' directory on the server. Then you should go into that `.ssh2' directory, and edit (or create) a file called `authorization'. In this file you should put a line @@ -6530,10 +6937,10 @@ Chapter 8: Using public keys for SSH authentication three ways: - Select the private key in PuTTY's configuration. See section - 4.23.8 for details. + 4.21.9 for details. - Specify the key file on the command line with the `-i' option. - See section 3.8.3.18 for details. + See section 3.11.3.18 for details. - Load the private key into Pageant (see chapter 9). In this case PuTTY will automatically try to use it for authentication if it @@ -6552,8 +6959,9 @@ Chapter 9: Using Pageant for authentication format. See chapter 8 to find out how to generate and use one. When you run Pageant, it will put an icon of a computer wearing a - hat into the System tray. It will then sit and do nothing, until you - load a private key into it. + hat into the System tray. It will then sit and do nothing, until + you load a private key into it. (You may need to use Windows' `Show + hidden icons' arrow to see the Pageant icon.) If you click the Pageant icon with the right mouse button, you will see a menu. Select `View Keys' from this menu. The Pageant main @@ -6581,13 +6989,16 @@ Chapter 9: Using Pageant for authentication passphrase again. (PuTTY can be configured not to try to use Pageant, but it will - try by default. See section 4.23.3 and section 3.8.3.9 for more + try by default. See section 4.21.4 and section 3.11.3.9 for more information.) When you want to shut down Pageant, click the right button on the Pageant icon in the System tray, and select `Exit' from the menu. Closing the Pageant main window does _not_ shut down Pageant. + If you want Pageant to stay running but forget all the keys it has + acquired, select `Remove All Keys' from the System tray menu. + 9.2 The Pageant main window The Pageant main window appears when you left-click on the Pageant @@ -6602,8 +7013,8 @@ Chapter 9: Using Pageant for authentication keys that are currently loaded into Pageant. The list might look something like this: - ssh-rsa 2048 22:d6:69:c9:22:51:ac:cb:b9:15:67:47:f7:65:6d:d7 k1 - ssh-dss 2048 e4:6c:69:f3:4f:fc:cf:fc:96:c0:88:34:a7:1e:59:d7 k2 + ssh-ed25519 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w + ssh-rsa 2048 SHA256:8DFtyHm3kQihgy52nzX96qMcEVOq7/yJmmwQQhBWYFg For each key, the list box will tell you: @@ -6611,18 +7022,29 @@ Chapter 9: Using Pageant for authentication key for use with the SSH-2 protocol), `ssh-dss' (a DSA key for use with the SSH-2 protocol), `ecdsa-sha2-*' (an ECDSA key for use with the SSH-2 protocol), `ssh-ed25519' (an Ed25519 key for - use with the SSH-2 protocol), or `ssh1' (an RSA key for use with - the old SSH-1 protocol). + use with the SSH-2 protocol), `ssh-ed448' (an Ed448 key for use + with the SSH-2 protocol), or `ssh1' (an RSA key for use with the + old SSH-1 protocol). - - The size (in bits) of the key. + - The size (in bits) of the key, for key types that come in + different sizes. - The fingerprint for the public key. This should be the same fingerprint given by PuTTYgen, and (hopefully) also the same fingerprint shown by remote utilities such as `ssh-keygen' when applied to your `authorized_keys' file. + By default this is shown in the `SHA256' format. You can change + to the older `MD5' format (which looks like `aa:bb:cc:...') + with the `Fingerprint type' drop-down, but bear in mind that + this format is less secure and should be avoided for comparison + purposes where possible. + - The comment attached to the key. + - The state of deferred decryption, if enabled for this key. See + section 9.5. + 9.2.2 The `Add Key' button To add a key to Pageant by reading it out of a local disk file, @@ -6678,6 +7100,9 @@ Chapter 9: Using Pageant for authentication If Pageant is already running, this syntax loads keys into the existing Pageant. + You can specify the `--encrypted' option to defer decryption of + these keys; see section 9.5. + 9.3.2 Making Pageant run another program You can arrange for Pageant to start another program once it has @@ -6691,16 +7116,21 @@ Chapter 9: Using Pageant for authentication C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe - 9.3.3 Restricting the Windows process ACL + 9.3.3 Starting with the key list visible + + Start Pageant with the `--keylist' option to show the main window as + soon as it starts up. + + 9.3.4 Restricting the Windows process ACL Pageant supports the same `-restrict-acl' option as the other PuTTY utilities to lock down the Pageant process's access control; see - section 3.8.3.25 for why you might want to do this. + section 3.11.3.27 for why you might want to do this. By default, if Pageant is started with `-restrict-acl', it won't pass this to any PuTTY sessions started from its System Tray submenu. Use `-restrict-putty-acl' to change this. (Again, see - section 3.8.3.25 for details.) + section 3.11.3.27 for details.) 9.4 Using agent forwarding @@ -6714,8 +7144,9 @@ Chapter 9: Using Pageant for authentication To enable agent forwarding, first start Pageant. Then set up a PuTTY SSH session in which `Allow agent forwarding' is enabled (see - section 4.23.6). Open the session as normal. (Alternatively, you can - use the `-A' command line option; see section 3.8.3.10 for details.) + section 4.21.7). Open the session as normal. (Alternatively, you + can use the `-A' command line option; see section 3.11.3.10 for + details.) If this has worked, your applications on the server should now have access to a Unix domain socket which the SSH server will forward @@ -6760,7 +7191,50 @@ Chapter 9: Using Pageant for authentication and then it's available to every machine that has agent forwarding available (not just the ones downstream of the place you added it). - 9.5 Security considerations + 9.5 Loading keys without decrypting them + + You can add keys to Pageant _without_ decrypting them. The key file + will be held in Pageant's memory still encrypted, and when a client + program first tries to use the key, Pageant will display a dialog + box prompting for the passphrase so that the key can be decrypted. + + This works the same way whether the key is used by an instance of + PuTTY running locally, or a remote client connecting to Pageant + through agent forwarding. + + To add a key to Pageant in this encrypted form, press the `Add Key + (encrypted)' button in the Pageant main window, or alternatively + right-click on the Pageant icon in the system tray and select `Add + Key (encrypted)' from there. Pageant will bring up a file dialog, in + just the same way as it would for the plain `Add Key' button. But it + won't ask for a passphrase. Instead, the key will be listed in the + main window with `(encrypted)' after it. + + To start Pageant up in the first place with encrypted keys loaded + into it, you can use the `--encrypted' option on the command line. + For example: + + C:\PuTTY\pageant.exe --encrypted d:\main.ppk + + After a key has been decrypted for the first use, it remains + decrypted, so that it can be used again. The main window will list + the key with `(re-encryptable)' after it. You can revert it to the + previous state, where a passphrase is required, using the `Re- + encrypt' button in the Pageant main window. + + You can also `re-encrypt' all keys that were added encrypted by + choosing `Re-encrypt All Keys' from the System tray menu. (Note that + this does _not_ discard cleartext keys that were not previously + added encrypted!) + + *CAUTION*: When Pageant displays a prompt to decrypt an already- + loaded key, it cannot give keyboard focus to the prompt dialog box. + As far as I know this is a deliberate defensive measure by Windows, + against malicious software. So make sure you click in the prompt + window before typing your passphrase, or else the passphrase might + be sent to somewhere you didn't want to trust with it! + + 9.6 Security considerations Using Pageant for public-key authentication gives you the convenience of being able to open multiple SSH sessions without @@ -6880,7 +7354,7 @@ Chapter 10: Common error messages protocol. If the server genuinely only supports SSH-1, then you need to either - change the `SSH protocol version' setting (see section 4.19.4), or + change the `SSH protocol version' setting (see section 4.17.4), or use the `-1' command-line option; in any case, you should not treat the resulting connection as secure. @@ -6898,7 +7372,7 @@ Chapter 10: Common error messages puts up this warning only for Blowfish, single-DES, and Arcfour encryption. - See section 4.22 for more information on this message. + See section 4.20 for more information on this message. (There are similar messages for other cryptographic primitives, such as host key algorithms.) @@ -6914,7 +7388,7 @@ Chapter 10: Common error messages number of keys loaded into it, since these servers count each offer of a public key as an authentication attempt. This can be worked around by specifying the key that's required for the authentication - in the PuTTY configuration (see section 4.23.8); PuTTY will ignore + in the PuTTY configuration (see section 4.21.9); PuTTY will ignore any other keys Pageant may have, but will ask Pageant to do the authentication, so that you don't have to type your passphrase. @@ -6974,7 +7448,7 @@ Chapter 10: Common error messages You may have tried to load an SSH-2 key in a `foreign' format (OpenSSH or ssh.com) directly into one of the PuTTY tools, in which case you need to import it into PuTTY's native format (`*.PPK') - using PuTTYgen - see section 8.2.12. + using PuTTYgen - see section 8.2.14. Alternatively, you may have specified a key that's inappropriate for the connection you're making. The SSH-2 and the old SSH-1 protocols @@ -7020,14 +7494,14 @@ Chapter 10: Common error messages This error can be caused by buggy SSH-1 servers that fail to cope with the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in - section 4.28.11 and possibly section 4.28.12. + section 4.26.11 and possibly section 4.26.12. 10.11 `No supported authentication methods available' This error indicates that PuTTY has run out of ways to authenticate you to an SSH server. This may be because PuTTY has TIS or keyboard- interactive authentication disabled, in which case see section - 4.23.4 and section 4.23.5. + 4.21.5 and section 4.21.6. 10.12 `Incorrect MAC received on packet' or `Incorrect CRC received on packet' @@ -7047,7 +7521,7 @@ Chapter 10: Common error messages which may not be noticed. Occasionally this has been caused by server bugs. An example is the - bug described at section 4.28.8, although you're very unlikely to + bug described at section 4.26.8, although you're very unlikely to encounter that one these days. In this context MAC stands for Message Authentication Code. It's a @@ -7064,7 +7538,7 @@ Chapter 10: Common error messages If you get this error, one thing you could try would be to fiddle with the setting of `Miscomputes SSH-2 encryption keys' (see section - 4.28.10) or `Ignores SSH-2 maximum packet size' (see section 4.28.5) + 4.26.10) or `Ignores SSH-2 maximum packet size' (see section 4.26.5) on the Bugs panel. 10.14 `PuTTY X11 proxy: _various errors_' @@ -7113,7 +7587,7 @@ Chapter 10: Common error messages to send the data and will then give up and kill the connection. In particular, this can occur even if you didn't type anything, if you are using SSH-2 and PuTTY attempts a key re-exchange. (See section - 4.20.2 for more about key re-exchange.) + 4.18.2 for more about key re-exchange.) (It can also occur if you are using keepalives in your connection. Other people have reported that keepalives _fix_ this error for @@ -7148,8 +7622,8 @@ Chapter 10: Common error messages the server does not provide the service which PuTTY is trying to access. - Check that you are connecting with the correct protocol (SSH, Telnet - or Rlogin), and check that the port number is correct. If that + Check that you are connecting with the correct protocol (SSH, + Telnet, etc), and check that the port number is correct. If that fails, consult the administrator of your server. 10.18 `Network error: Connection timed out' @@ -7169,7 +7643,7 @@ Chapter 10: Common error messages up on receiving a reply from the server.) This can occur if you type things into PuTTY while the network is down, but it can also occur if PuTTY decides of its own accord to send data: due to a repeat key - exchange in SSH-2 (see section 4.20.2) or due to keepalives (section + exchange in SSH-2 (see section 4.18.2) or due to keepalives (section 4.14.1). 10.19 `Network error: Cannot assign requested address' @@ -7191,8 +7665,8 @@ Appendix A: PuTTY FAQ A.1.1 What is PuTTY? - PuTTY is a client program for the SSH, Telnet and Rlogin network - protocols. + PuTTY is a client program for the SSH, Telnet, Rlogin, and SUPDUP + network protocols. These protocols are all used to run a remote session on a computer, over a network. PuTTY implements the client end of that session: the @@ -7273,7 +7747,7 @@ Appendix A: PuTTY FAQ A.2.6 Does PuTTY support storing its settings in a disk file? - Not at present, although section 4.30 in the documentation gives a + Not at present, although section 4.32 in the documentation gives a method of achieving the same effect. A.2.7 Does PuTTY support full-screen mode, like a DOS box? @@ -7814,7 +8288,7 @@ A.6.10 Should I run the 32-bit or the 64-bit version? If you need to use an external DLL for GSSAPI authentication, that DLL may only be available in a 32-bit or 64-bit form, and that will dictate the version of PuTTY you need to use. (You will probably - know if you're doing this; see section 4.24.2 in the documentation.) + know if you're doing this; see section 4.22.2 in the documentation.) A.7 Troubleshooting @@ -8170,7 +8644,7 @@ A.7.20 My SSH-2 session locks up for a few seconds every so often. These delays are inconvenient, but they are there for your protection. If they really cause you a problem, you can choose to turn off periodic rekeying using the `Kex' configuration panel (see - section 4.20), but be aware that you will be sacrificing security + section 4.18), but be aware that you will be sacrificing security for this. (Falling back to SSH-1 would also remove the delays, but would lose a _lot_ more security still. We do not recommend it.) @@ -8205,7 +8679,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my have a buggy server that objects to certain SSH protocol extensions. The SSH protocol recently gained a new `terminal mode', IUTF8, which - PuTTY sends by default; see section 4.25.2. This is the first new + PuTTY sends by default; see section 4.23.2. This is the first new terminal mode since the SSH-2 protocol was defined. While servers are supposed to ignore modes they don't know about, some buggy servers will unceremoniously close the connection if they see @@ -8244,7 +8718,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my If you are using PuTTY on a public PC, or somebody else's PC, you might want to clean this information up when you leave. You can do that automatically, by running the command `putty -cleanup'. See - section 3.8.2 in the documentation for more detail. (Note that this + section 3.11.2 in the documentation for more detail. (Note that this only removes settings for the currently logged-in user on multi-user systems.) @@ -8286,7 +8760,17 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my A.9 Administrative questions - A.9.1 Would you like me to register you a nicer domain name? + A.9.1 Is putty.org your website? + + No, it isn't. putty.org is run by an opportunist who uses it to + advertise their own commercial SSH implementation to people looking + for our free one. We don't own that site, we can't control it, and + we don't advise anyone to use it in preference to our own site. + + The real PuTTY web site, run by the PuTTY team, has always been at + https://www.chiark.greenend.org.uk/~sgtatham/putty/. + + A.9.2 Would you like me to register you a nicer domain name? No, thank you. Even if you can find one (most of them seem to have been registered already, by people who didn't ask whether we @@ -8302,11 +8786,11 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my things. Having it registered for us by a third party who we don't even know is not the best way to achieve this. - A.9.2 Would you like free web hosting for the PuTTY web site? + A.9.3 Would you like free web hosting for the PuTTY web site? We already have some, thanks. - A.9.3 Would you link to my web site from the PuTTY web site? + A.9.4 Would you link to my web site from the PuTTY web site? Only if the content of your web page is of definite direct interest to PuTTY users. If your content is unrelated, or only tangentially @@ -8343,10 +8827,10 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my of the PuTTY web site, we might be interested in linking to you from our Mirrors page. - A.9.4 Why don't you move PuTTY to SourceForge? + A.9.5 Why don't you move PuTTY to SourceForge? Partly, because we don't want to move the web site location (see - question A.9.1). + question A.9.2). Also, security reasons. PuTTY is a security product, and as such it is particularly important to guard the code and the web site against @@ -8361,7 +8845,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my they're not ideal for everyone, and in particular they're not ideal for us. - A.9.5 Why can't I subscribe to the putty-bugs mailing list? + A.9.6 Why can't I subscribe to the putty-bugs mailing list? Because you're not a member of the PuTTY core development team. The putty-bugs mailing list is not a general newsgroup-like discussion @@ -8372,7 +8856,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my overwhelmed by the volume of traffic. It's hard enough to keep up with the list as it is. - A.9.6 If putty-bugs isn't a general-subscription mailing list, what is? + A.9.7 If putty-bugs isn't a general-subscription mailing list, what is? There isn't one, that we know of. @@ -8382,7 +8866,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my have the time to read it. It's probably better to use one of the established newsgroups for this purpose (see section B.1.2). - A.9.7 How can I donate to PuTTY development? + A.9.8 How can I donate to PuTTY development? Please, _please_ don't feel you have to. PuTTY is completely free software, and not shareware. We think it's very important that @@ -8408,7 +8892,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my something worthwhile, ask us first. If you don't like these terms, feel perfectly free not to donate. We don't mind. - A.9.8 Can I have permission to put PuTTY on a cover disk / distribute it + A.9.9 Can I have permission to put PuTTY on a cover disk / distribute it with other software / etc? Yes. For most things, you need not bother asking us explicitly for @@ -8416,7 +8900,7 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my See section B.8 for more details. - A.9.9 Can you sign an agreement indemnifying us against security problems +A.9.10 Can you sign an agreement indemnifying us against security problems in PuTTY? No! @@ -8477,13 +8961,13 @@ A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my then get our code reviewed by a security engineer they're happy with. -A.9.10 Can you sign this form granting us permission to use/distribute +A.9.11 Can you sign this form granting us permission to use/distribute PuTTY? If your form contains any clause along the lines of `the undersigned represents and warrants', we're not going to sign it. This is particularly true if it asks us to warrant that PuTTY is secure; - see question A.9.9 for more discussion of this. But it doesn't + see question A.9.10 for more discussion of this. But it doesn't really matter what we're supposed to be warranting: even if it's something we already believe is true, such as that we don't infringe any third-party copyright, we will not sign a document accepting @@ -8498,10 +8982,10 @@ A.9.10 Can you sign this form granting us permission to use/distribute involve pretending you wrote it or suing us if it goes wrong. We think that really ought to be enough for anybody. - See also question A.9.12 for another reason why we don't want to do + See also question A.9.13 for another reason why we don't want to do this sort of thing. -A.9.11 Can you write us a formal notice of permission to use PuTTY? +A.9.12 Can you write us a formal notice of permission to use PuTTY? We could, in principle, but it isn't clear what use it would be. If you think there's a serious chance of one of the PuTTY copyright @@ -8514,10 +8998,10 @@ A.9.11 Can you write us a formal notice of permission to use PuTTY? document, which wouldn't guarantee you that some other copyright holder might not sue. - See also question A.9.12 for another reason why we don't want to do + See also question A.9.13 for another reason why we don't want to do this sort of thing. -A.9.12 Can you sign _anything_ for us? +A.9.13 Can you sign _anything_ for us? Not unless there's an incredibly good reason. @@ -8536,7 +9020,7 @@ A.9.12 Can you sign _anything_ for us? is simply not well suited to using popular free software, and we urge you to consider this as a flaw in your policy. -A.9.13 If you won't sign anything, can you give us some sort of assurance +A.9.14 If you won't sign anything, can you give us some sort of assurance that you won't make PuTTY closed-source in future? Yes and no. @@ -8578,7 +9062,7 @@ A.9.13 If you won't sign anything, can you give us some sort of assurance would switch to the latter. Therefore, it would be pretty stupid of us to try it.) -A.9.14 Can you provide us with export control information / FIPS +A.9.15 Can you provide us with export control information / FIPS certification for PuTTY? Some people have asked us for an Export Control Classification @@ -8595,7 +9079,7 @@ A.9.14 Can you provide us with export control information / FIPS PuTTY tools. Unless someone else is prepared to do the necessary work and pay any costs, we can't provide this. -A.9.15 As one of our existing software vendors, can you just fill in this +A.9.16 As one of our existing software vendors, can you just fill in this questionnaire for us? We periodically receive requests like this, from organisations @@ -8646,7 +9130,7 @@ A.9.15 As one of our existing software vendors, can you just fill in this about you from free software developers who don't have any idea who you are. Then, only send out these mass mailings to the former. -A.9.16 The `sha1sums' / `sha256sums' / etc files on your download page +A.9.17 The `sha1sums' / `sha256sums' / etc files on your download page don't match the binaries. People report this every so often, and usually the reason turns out @@ -8770,8 +9254,9 @@ Appendix B: Feedback and bug reporting it being automatically copied to all the developers. (If the file contains confidential information, then you could - encrypt it with our Secure Contact Key; see section E.1 for - details.) + encrypt it with our Secure Contact Key; see section F.1 for + details. Please _only_ use this for information that _needs_ to be + confidential.) Some people like to send mail in MS Word format. Please _don't_ send us bug reports, or any other mail, as a Word document. Word @@ -8873,7 +9358,7 @@ Appendix B: Feedback and bug reporting Windows for Intel as this is overwhelmingly the case.) - Tell us what protocol you are connecting with: SSH, Telnet, - Rlogin, or Raw mode, or a serial connection. + Rlogin, SUPDUP, or Raw mode, or a serial connection. - Tell us what kind of server you are connecting to; what OS, and if possible what SSH server (if you're using SSH). You can get @@ -8928,7 +9413,7 @@ Appendix B: Feedback and bug reporting release is available. For this purpose, we provide a GPG key suitable for encryption: the - Secure Contact Key. See section E.1 for details of this. + Secure Contact Key. See section F.1 for details of this. (Of course, vulnerabilities are also bugs, so please do include as much information as possible about them, the same way you would with @@ -8976,7 +9461,7 @@ Appendix B: Feedback and bug reporting changes to the code which your patch would clash with, or something. If you check with the maintainers first, there is a better chance of your code actually being usable. Also, read the - design principles listed in appendix D: if you do not conform to + design principles listed in appendix E: if you do not conform to them, we will probably not be able to accept your patch. B.5 Requesting features that have already been requested @@ -9076,7 +9561,7 @@ Appendix B: Feedback and bug reporting B.8 Asking permission for things - PuTTY is distributed under the MIT Licence (see appendix C for + PuTTY is distributed under the MIT Licence (see appendix D for details). This means you can do almost _anything_ you like with our software, our source code, and our documentation. The only things you aren't allowed to do are to remove our copyright notices or the @@ -9146,17 +9631,405 @@ Appendix B: Feedback and bug reporting The actual address to mail is . -Appendix C: PuTTY Licence +Appendix C: PPK file format +--------------------------- + + This appendix documents the file format used by PuTTY to store + private keys. + + In this appendix, binary data structures are described using data + type representations such as `uint32', `string' and `mpint' as + used in the SSH protocol standards themselves. These are defined + authoritatively by RFC 4251 section 5, `Data Type Representations + Used in the SSH Protocols'. + + C.1 Overview + + A PPK file stores a private key, and the corresponding public key. + Both are contained in the same file. + + The file format can be completely unencrypted, or it can encrypt + the private key. The _public_ key is stored in cleartext in both + cases. (This enables PuTTY to send the public key to an SSH server + to see whether it will accept it, and not bother prompting for the + passphrase unless the server says yes.) + + When the key file is encrypted, the encryption key is derived from a + passphrase. An encrypted PPK file is also tamper-proofed using a MAC + (authentication code), also derived from the same passphrase. The + MAC protects the encrypted private key data, but it also covers the + cleartext parts of the file. So you can't edit the public half of + the key without invalidating the MAC and causing the key file as a + whole to become useless. + + This MAC protects the key file against active cryptographic attacks + in which the public half of a key pair is modified in a controlled + way that allows an attacker to deduce information about the private + half from the resulting corrupted signatures. Any attempt to do + that to a PPK file should be reliably caught by the MAC failing to + validate. + + (Such an attack would only be useful if the key file was stored in a + location where the attacker could modify it without also having full + access to the process that you type passphrases into. But that's not + impossible; for example, if your home directory was on a network + file server, then the file server's administrator could access the + key file but not processes on the client machine.) + + The MAC also covers the _comment_ on the key. This stops an attacker + from swapping keys with each other and editing the comments to + disguise the fact. As a consequence, PuTTYgen cannot edit the + comment on a key unless you decrypt the key with your passphrase + first. + + (The circumstances in which _that_ attack would be useful are even + more restricted. One example might be that the different keys + trigger specific actions on the server you're connecting to and one + of those actions is more useful to the attacker than the other. But + once you have a MAC at all, it's no extra effort to make it cover as + much as possible, and usually sensible.) + + C.2 Outer layer + + The outer layer of a PPK file is text-based. The PuTTY tools will + always use LF line termination when writing PPK files, but will + tolerate CR+LF and CR-only on input. + + The first few lines identify it as a PPK, and give some initial data + about what's stored in it and how. They look like this: + + PuTTY-User-Key-File-version: algorithm-name + Encryption: encryption-type + Comment: key-comment-string + + *version* is a decimal number giving the version number of the file + format itself. The current file format version is 3. + + *algorithm-name* is the SSH protocol identifier for the public key + algorithm that this key is used for (such as `ssh-dss' or `ecdsa- + sha2-nistp384'). + + *encryption-type* indicates whether this key is stored encrypted, + and if so, by what method. Currently the only supported encryption + types are `aes256-cbc' and `none'. + + *key-comment-string* is a free text field giving the comment. This + can contain any byte values other than 13 and 10 (CR and LF). + + The next part of the file gives the public key. This is stored + unencrypted but base64-encoded (RFC 4648), and is preceded by a + header line saying how many lines of base64 data are shown, looking + like this: + + Public-Lines: number-of-lines + that many lines of base64 data + + The base64-encoded data in this blob is formatted in exactly the + same way as an SSH public key sent over the wire in the SSH protocol + itself. That is also the same format as the base64 data stored in + OpenSSH's `authorized_keys' file, except that in a PPK file the + base64 data is split across multiple lines. But if you remove the + newlines from the middle of this section, the resulting base64 blob + is in the right format to go in an `authorized_keys' line. + + If the key is encrypted (i.e. *encryption-type* is not `none'), + then the next thing that appears is a sequence of lines specifying + how the keys for encrypting the file are to be derived from the + passphrase: + + Key-Derivation: argon2-flavour + Argon2-Memory: decimal-integer + Argon2-Passes: decimal-integer + Argon2-Parallelism: decimal-integer + Argon2-Salt: hex-string + + *argon2-flavour* is one of the identifiers `Argon2d', `Argon2i' or + `Argon2id', all describing variants of the Argon2 password-hashing + function. + + The three integer values are used as parameters for Argon2, which + allows you to configure the amount of memory used (in Kbyte), the + number of passes of the algorithm to run (to tune its running time), + and the degree of parallelism required by the hash function. The + salt is decoded into a sequence of binary bytes and used as an + additional input to Argon2. (It is chosen randomly when the key file + is written, so that a guessing attack can't be mounted in parallel + against multiple key files.) + + The next part of the file gives the private key. This is base64- + encoded in the same way: + + Private-Lines: number-of-lines + that many lines of base64 data + + The binary data represented in this base64 blob may be encrypted, + depending on the _encryption-type_ field in the key file header + shown above: + + - If *encryption-type* is `none', then this data is stored in + plain text. + + - If *encryption-type* is `aes256-cbc', then this data is + encrypted using AES, with a 256-bit key length, in the CBC + cipher mode. The key and initialisation vector are derived from + the passphrase: see section C.4. + + In order to encrypt the private key data with AES, it must be + a multiple of 16 bytes (the AES cipher block length). This + is achieved by appending random padding to the data before + encrypting it. When decoding it after decryption, the random + data can be ignored: the internal structure of the data is + enough to tell you when you've reached the end of the meaningful + part. + + Unlike public keys, the binary encoding of private keys is not + specified at all in the SSH standards. See section C.3 for details + of the private key format for each key type supported by PuTTY. + + The final thing in the key file is the MAC: + + Private-MAC: hex-mac-data + + *hex-mac-data* is a hexadecimal-encoded value, 64 digits long (i.e. + 32 bytes), generated using the HMAC-SHA-256 algorithm with the + following binary data as input: + + - string: the *algorithm-name* header field. + + - string: the *encryption-type* header field. + + - string: the *key-comment-string* header field. + + - string: the binary public key data, as decoded from the base64 + lines after the `Public-Lines' header. + + - string: the plaintext of the binary private key data, as decoded + from the base64 lines after the `Private-Lines' header. If that + data was stored encrypted, then the decrypted version of it + is used in this MAC preimage, _including_ the random padding + mentioned above. + + The MAC key is derived from the passphrase: see section C.4. + + C.3 Private key encodings + + This section describes the private key format for each key type + supported by PuTTY. + + Because the PPK format also contains the public key (and both public + and private key are protected by the same MAC to ensure they can't + be made inconsistent), there is no need for the private key section + of the file to repeat data from the public section. So some of these + formats are very short. + + In all cases, a decoding application can begin reading from the + start of the decrypted private key data, and know when it has read + all that it needs. This allows random padding after the meaningful + data to be safely ignored. + + C.3.1 RSA + + RSA keys are stored using an *algorithm-name* of `ssh-rsa'. (Keys + stored like this are also used by the updated RSA signature schemes + that use hashes other than SHA-1.) + + The public key data has already provided the key modulus and the + public encoding exponent. The private data stores: + + - mpint: the private decoding exponent of the key. + + - mpint: one prime factor _p_ of the key. + + - mpint: the other prime factor _q_ of the key. (RSA keys stored + in this format are expected to have exactly two prime factors.) + + - mpint: the multiplicative inverse of _q_ modulo _p_. + + C.3.2 DSA + + DSA keys are stored using an *algorithm-name* of `ssh-dss'. + + The public key data has already provided the key parameters (the + large prime _p_, the small prime _q_ and the group generator _g_), + and the public key _y_. The private key stores: + + - mpint: the private key _x_, which is the discrete logarithm of + _y_ in the group generated by _g_ mod _p_. + + C.3.3 NIST elliptic-curve keys + + NIST elliptic-curve keys are stored using one of the following + *algorithm-name* values, each corresponding to a different elliptic + curve and key size: + + - `ecdsa-sha2-nistp256' + + - `ecdsa-sha2-nistp384' + + - `ecdsa-sha2-nistp521' + + The public key data has already provided the public elliptic curve + point. The private key stores: + + - mpint: the private exponent, which is the discrete log of the + public point. + + C.3.4 EdDSA elliptic-curve keys (Ed25519 and Ed448) + + EdDSA elliptic-curve keys are stored using one of the following + *algorithm-name* values, each corresponding to a different elliptic + curve and key size: + + - `ssh-ed25519' + + - `ssh-ed448' + + The public key data has already provided the public elliptic curve + point. The private key stores: + + - mpint: the private exponent, which is the discrete log of the + public point. + + C.4 Key derivation + + When a key file is encrypted, there are three pieces of key material + that need to be computed from the passphrase: + + - the key for the symmetric cipher used to encrypt the private key + + - the initialisation vector for that cipher encryption + + - the key for the MAC. + + If *encryption-type* is `aes256-cbc', then the symmetric cipher key + is 32 bytes long, and the initialisation vector is 16 bytes (one + cipher block). The length of the MAC key is also chosen to be 32 + bytes. + + If *encryption-type* is `none', then all three of these pieces of + data have zero length. (The MAC is still generated and checked in + the key file format, but it has a zero-length key.) + + If the amount of key material required is not zero, then the + passphrase is fed to the Argon2 key derivation function, in + whichever mode is described in the `Key-Derivation' header in + the key file, with parameters derived from the various `Argon2- + _Parameter_:' headers. + + (If the key is unencrypted, then all those headers are omitted, and + Argon2 is not run at all.) + + Argon2 takes two extra string inputs in addition to the passphrase + and the salt: a secret key, and some `associated data'. In PPK's use + of Argon2, these are both set to the empty string. + + The `tag length' parameter to Argon2 (i.e. the amount of data it + is asked to output) is set to the sum of the lengths of all of the + data items required, i.e. (cipher key length + IV length + MAC key + length). The output data is interpreted as the concatenation of the + cipher key, the IV and the MAC key, in that order. + + So, for `aes256-cbc', the tag length will be 32+16+32 = 80 bytes; of + the 80 bytes of output data, the first 32 bytes are used as the 256- + bit AES key, the next 16 as the CBC IV, and the final 32 bytes as + the HMAC-SHA-256 key. + + C.5 Older versions of the PPK format + + C.5.1 Version 2 + + PPK version 2 was used by PuTTY 0.52 to 0.74 inclusive. + + In PPK version 2, the MAC algorithm used was HMAC-SHA-1 (so the + Private-MAC line contained only 40 hex digits). + + The `Key-Derivation:' header and all the `Argon2-_Parameter_:' + headers were absent. Instead of using Argon2, the key material for + encrypting the private blob was derived from the passphrase in a + totally different way, as follows. + + The cipher key for `aes256-cbc' was constructed by generating two + SHA-1 hashes, concatenating them, and taking the first 32 bytes of + the result. (So you'd get all 20 bytes of the first hash output, and + the first 12 of the second). Each hash preimage was as follows: + + - uint32: a sequence number. This is 0 in the first hash, and 1 in + the second. (The idea was to extend this mechanism to further + hashes by continuing to increment the sequence number, if future + changes required even longer keys.) + + - the passphrase, without any prefix length field. + + In PPK v2, the CBC initialisation vector was all zeroes. + + The MAC key was 20 bytes long, and was a single SHA-1 hash of the + following data: + + - the fixed string `putty-private-key-file-mac-key', without any + prefix length field. + + - the passphrase, without any prefix length field. (If the key is + stored unencrypted, the passphrase was taken to be the empty + string for these purposes.) + + C.5.2 Version 1 + + PPK version 1 was a badly designed format, only used during initial + development, and not recommended for production use. + + PPK version 1 was never used by a released version of PuTTY. It was + only emitted by some early development snapshots between version + 0.51 (which did not support SSH-2 public keys at all) and 0.52 + (which already used version 2 of this file format). I _hope_ there + are no PPK v1 files in use anywhere. But just in case, the old badly + designed format is documented here anyway. + + In PPK version 1, the input to the MAC does not include any of the + header fields or the public key. It is simply the private key data + (still in plaintext and including random padding), all by itself + (without a wrapping string). + + PPK version 1 keys must therefore be rigorously validated after + loading, to ensure that the public and private parts of the key were + consistent with each other. + + PPK version 1 only supported the RSA and DSA key types. For RSA, + this validation can be done using only the provided data (since the + private key blob contains enough information to reconstruct the + public values anyway). But for DSA, that isn't quite enough. + + Hence, PPK version 1 DSA keys extended the private data so that + immediately after _x_ was stored an extra value: + + - string: a SHA-1 hash of the public key data, whose preimage + consists of + + - string: the large prime _p_ + + - string: the small prime _q_ + + - string: the group generator _g_ + + The idea was that checking this hash would verify that the key + parameters had not been tampered with, and then the loading + application could directly verify that _g_^_x_ = _y_. + + In an _unencrypted_ version 1 key file, the MAC is replaced by a + plain SHA-1 hash of the private key data. This is indicated by the + `Private-MAC:' header being replaced with `Private-Hash:' instead. + +Appendix D: PuTTY Licence ------------------------- - PuTTY is copyright 1997-2019 Simon Tatham. + PuTTY is copyright 1997-2021 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav - Kuzmich, Nico Williams, Viktor Dukhovni, and CORE SDI S.A. + Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars + Brinkhoff, and CORE SDI S.A. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files @@ -9177,14 +10050,14 @@ Appendix C: PuTTY Licence CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -Appendix D: PuTTY hacking guide +Appendix E: PuTTY hacking guide ------------------------------- This appendix lists a selection of the design principles applying to the PuTTY source code. If you are planning to send code contributions, you should read this first. - D.1 Cross-OS portability + E.1 Cross-OS portability Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac @@ -9287,7 +10160,7 @@ Appendix D: PuTTY hacking guide standardised wire protocol or a binary file format, they should be spelled '\012' and '\015' respectively. - D.2 Multiple backends treated equally + E.2 Multiple backends treated equally PuTTY is not an SSH client with some other stuff tacked on the side. PuTTY is a generic, multiple-backend, remote VT-terminal client @@ -9309,7 +10182,7 @@ Appendix D: PuTTY hacking guide happens that only one back end actually does, that's just the way it is, but it shouldn't be relied upon by any code. - D.3 Multiple sessions per process on some platforms + E.3 Multiple sessions per process on some platforms Some ports of PuTTY - notably the in-progress Mac port - are constrained by the operating system to run as a single process @@ -9318,13 +10191,12 @@ Appendix D: PuTTY hacking guide Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular - login session: `flags' defines properties that are expected to apply - equally to _all_ the sessions run by a single PuTTY process, the - random number state in sshrand.c and the timer list in timing.c - serve all sessions equally, and so on. But most data is specific to - a particular network session, and is therefore stored in dynamically - allocated data structures, and pointers to these structures are - passed around between functions. + login session. The random number state in sshrand.c, the timer list + in timing.c and the queue of top-level callbacks in callback.c serve + all sessions equally. But most data is specific to a particular + network session, and is therefore stored in dynamically allocated + data structures, and pointers to these structures are passed around + between functions. Platform-specific code can reverse this decision if it likes. The Windows code, for historical reasons, stores most of its data as @@ -9333,7 +10205,7 @@ Appendix D: PuTTY hacking guide changes to the platform-independent code should avoid introducing global variables, unless they are genuinely cross-session. - D.4 C, not C++ + E.4 C, not C++ PuTTY is written entirely in C, not in C++. @@ -9360,7 +10232,7 @@ Appendix D: PuTTY hacking guide changes force the Unix or Windows ports to be compiled as C++, they will be unacceptable to us. - D.5 Security-conscious coding + E.5 Security-conscious coding PuTTY is a network application and a security application. Assume your code will end up being fed deliberately malicious data by @@ -9373,7 +10245,7 @@ Appendix D: PuTTY hacking guide allocate buffers of the right size for the string they construct. Use these wherever possible. - D.6 Independence of specific compiler + E.6 Independence of specific compiler Windows PuTTY can currently be compiled with any of three Windows compilers: MS Visual C, the Cygwin / mingw32 GNU tools, and clang @@ -9401,12 +10273,12 @@ Appendix D: PuTTY hacking guide one compiler, are disallowed. (_snprintf in particular should be unnecessary, since we provide - dupprintf; see section D.5.) + dupprintf; see section E.5.) Compiler independence should apply on all platforms, of course, not just on Windows. - D.7 Small code size + E.7 Small code size PuTTY is tiny, compared to many other Windows applications. And it's easy to install: it depends on no DLLs, no other applications, @@ -9437,7 +10309,7 @@ Appendix D: PuTTY hacking guide graceful degradation: if a DLL can't be found, then PuTTY should run happily and just not supply the feature that depended on it. - D.8 Single-threaded code + E.8 Single-threaded code PuTTY and its supporting tools, or at least the vast majority of them, run in only one OS thread. @@ -9467,7 +10339,7 @@ Appendix D: PuTTY hacking guide One important consequence of this: PuTTY has only one thread in which to do everything. That `everything' may include managing - more than one login session (section D.3), managing multiple data + more than one login session (section E.3), managing multiple data channels within an SSH session, responding to GUI events even when nothing is happening on the network, and responding to network requests from the server (such as repeat key exchange) even when the @@ -9475,7 +10347,7 @@ Appendix D: PuTTY hacking guide configuration dialog box. This means that _almost none_ of the PuTTY code can safely block. - D.9 Keystrokes sent to the server wherever possible + E.9 Keystrokes sent to the server wherever possible In almost all cases, PuTTY sends keystrokes to the server. Even weird keystrokes that you think should be hot keys controlling @@ -9494,7 +10366,7 @@ Appendix D: PuTTY hacking guide server - so make sure that there's an alternative way to invoke whatever PuTTY feature it controls. - D.10 640x480 friendliness in configuration panels + E.10 640x480 friendliness in configuration panels There's a reason we have lots of tiny configuration panels instead of a few huge ones, and that reason is that not everyone has a @@ -9508,7 +10380,7 @@ Appendix D: PuTTY hacking guide boxes and you find yourself wanting to increase the size of the whole box, _don't_. Split it into more panels instead. - D.11 Automatically generated Makefiles + E.11 Automatically generated Makefiles PuTTY is intended to compile on multiple platforms, and with multiple compilers. It would be horrifying to try to maintain a @@ -9545,7 +10417,7 @@ Appendix D: PuTTY hacking guide archive saying this, but many people don't seem to read it, so it's worth repeating here.) - D.12 Coroutines in the SSH code + E.12 Coroutines in the SSH code Large parts of the code in the various SSH modules (in fact most of the protocol layers) are structured using a set of macros that @@ -9584,7 +10456,374 @@ Appendix D: PuTTY hacking guide function is called, it can reliably free the memory if there is any, and not crash if there isn't. - D.13 Single compilation of each source file + E.13 Explicit vtable structures to implement traits + + A lot of PuTTY's code is written in a style that looks structurally + rather like an object-oriented language, in spite of PuTTY being a + pure C program. + + For example, there's a single data type called ssh_hash, which is + an abstraction of a secure hash function, and a bunch of functions + called things like ssh_hash__foo_ that do things with those data + types. But in fact, PuTTY supports many different hash functions, + and each one has to provide its own implementation of those + functions. + + In C++ terms, this is rather like having a single abstract base + class, and multiple concrete subclasses of it, each of which fills + in all the pure virtual methods in a way that's compatible with the + data fields of the subclass. The implementation is more or less the + same, as well: in C, we do explicitly in the source code what the + C++ compiler will be doing behind the scenes at compile time. + + But perhaps a closer analogy in functional terms is the Rust concept + of a `trait', or the Java idea of an `interface'. C++ supports a + multi-level hierarchy of inheritance, whereas PuTTY's system - like + traits or interfaces - has only two levels, one describing a generic + object of a type (e.g. a hash function) and another describing a + specific implementation of that type (e.g. SHA-256). + + The PuTTY code base has a standard idiom for doing this in C, as + follows. + + Firstly, we define two struct types for our trait. One of them + describes a particular _kind_ of implementation of that trait, + and it's full of (mostly) function pointers. The other describes + a specific _instance_ of an implementation of that trait, and it + will contain a pointer to a const instance of the first type. For + example: + + typedef struct MyAbstraction MyAbstraction; + typedef struct MyAbstractionVtable MyAbstractionVtable; + + struct MyAbstractionVtable { + MyAbstraction *(*new)(const MyAbstractionVtable *vt); + void (*free)(MyAbstraction *); + void (*modify)(MyAbstraction *, unsigned some_parameter); + unsigned (*query)(MyAbstraction *, unsigned some_parameter); + }; + + struct MyAbstraction { + const MyAbstractionVtable *vt; + }; + + Here, we imagine that MyAbstraction might be some kind of object + that contains mutable state. The associated vtable structure shows + what operations you can perform on a MyAbstraction: you can create + one (dynamically allocated), free one you already have, or call the + example methods `modify' (to change the state of the object in some + way) and `query' (to return some value derived from the object's + current state). + + (In most cases, the vtable structure has a name ending in `vtable'. + But for historical reasons a lot of the crypto primitives that + use this scheme - ciphers, hash functions, public key methods and + so on - instead have names ending in `alg', on the basis that the + primitives they implement are often referred to as `encryption + algorithms', `hash algorithms' and so forth.) + + Now, to define a concrete instance of this trait, you'd define a + struct that contains a MyAbstraction field, plus any other data it + might need: + + struct MyImplementation { + unsigned internal_data[16]; + SomeOtherType *dynamic_subthing; + + MyAbstraction myabs; + }; + + Next, you'd implement all the necessary methods for that + implementation of the trait, in this kind of style: + + static MyAbstraction *myimpl_new(const MyAbstractionVtable *vt) + { + MyImplementation *impl = snew(MyImplementation); + memset(impl, 0, sizeof(*impl)); + impl->dynamic_subthing = allocate_some_other_type(); + impl->myabs.vt = vt; + return &impl->myabs; + } + + static void myimpl_free(MyAbstraction *myabs) + { + MyImplementation *impl = container_of(myabs, MyImplementation, myabs); + free_other_type(impl->dynamic_subthing); + sfree(impl); + } + + static void myimpl_modify(MyAbstraction *myabs, unsigned param) + { + MyImplementation *impl = container_of(myabs, MyImplementation, myabs); + impl->internal_data[param] += do_something_with(impl->dynamic_subthing); + } + + static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) + { + MyImplementation *impl = container_of(myabs, MyImplementation, myabs); + return impl->internal_data[param]; + } + + Having defined those methods, now we can define a const instance of + the vtable structure containing pointers to them: + + const MyAbstractionVtable MyImplementation_vt = { + .new = myimpl_new, + .free = myimpl_free, + .modify = myimpl_modify, + .query = myimpl_query, + }; + + _In principle_, this is all you need. Client code can construct a + new instance of a particular implementation of MyAbstraction by + digging out the new method from the vtable and calling it (with + the vtable itself as a parameter), which returns a MyAbstraction * + pointer that identifies a newly created instance, in which the vt + field will contain a pointer to the same vtable structure you passed + in. And once you have an instance object, say MyAbstraction *myabs, + you can dig out one of the other method pointers from the vtable it + points to, and call that, passing the object itself as a parameter. + + But in fact, we don't do that, because it looks pretty ugly at all + the call sites. Instead, what we generally do in this code base is + to write a set of static inline wrapper functions in the same header + file that defined the MyAbstraction structure types, like this: + + static MyAbstraction *myabs_new(const MyAbstractionVtable *vt) + { return vt->new(vt); } + static void myabs_free(MyAbstraction *myabs) + { myabs->vt->free(myabs); } + static void myimpl_modify(MyAbstraction *myabs, unsigned param) + { myabs->vt->modify(myabs, param); } + static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) + { return myabs->vt->query(myabs, param); } + + And now call sites can use those reasonably clean-looking wrapper + functions, and shouldn't ever have to directly refer to the vt field + inside any myabs object they're holding. For example, you might + write something like this: + + MyAbstraction *myabs = myabs_new(&MyImplementation_vtable); + myabs_update(myabs, 10); + unsigned output = myabs_query(myabs, 2); + myabs_free(myabs); + + and then all this code can use a different implementation of the + same abstraction by just changing which vtable pointer it passed in + in the first line. + + Some things to note about this system: + + - The implementation instance type (here `MyImplementation' + contains the abstraction type (`MyAbstraction') as one of its + fields. But that field is not necessarily at the start of the + structure. So you can't just _cast_ pointers back and forth + between the two types. Instead: + + - You `up-cast' from implementation to abstraction by taking + the address of the MyAbstraction field. You can see the + example new method above doing this, returning &impl->myabs. + All new methods do this on return. + + - Going in the other direction, each method that was + passed a generic MyAbstraction *myabs parameter has to + recover a pointer to the specific implementation type + MyImplementation *impl. The idiom for doing that is to use + the `container_of' macro, also seen in the Linux kernel + code. Generally, container_of(p, Type, field) says: `I'm + confident that the pointer value `p' is pointing to the + field called `field' within a larger struct of type Type. + Please return me the pointer to the containing structure.' + So in this case, we take the `myabs' pointer passed to the + function, and `down-cast' it into a pointer to the larger + and more specific structure type MyImplementation, by + adjusting the pointer value based on the offset within that + structure of the field called `myabs'. + + This system is flexible enough to permit `multiple inheritance', + or rather, multiple _implementation_: having one object type + implement more than one trait. For example, the Proxy type + implements both the Socket trait and the Plug trait that + connects to it, because it has to act as an adapter between + another instance of each of those types. + + It's also perfectly possible to have the same object implement + the _same_ trait in two different ways. At the time of writing + this I can't think of any case where we actually do this, but a + theoretical example might be if you needed to support a trait + like Comparable in two ways that sorted by different criteria. + There would be no difficulty doing this in the PuTTY system: + simply have your implementation struct contain two (or more) + fields of the same abstraction type. The fields will have + different names, which makes it easy to explicitly specify which + one you're returning a pointer to during up-casting, or which + one you're down-casting from using container_of. And then both + sets of implementation methods can recover a pointer to the same + containing structure. + + - Unlike in C++, all objects in PuTTY that use this system are + dynamically allocated. The `constructor' functions (whether + they're virtualised across the whole abstraction or specific to + each implementation) always allocate memory and return a pointer + to it. The `free' method (our analogue of a destructor) always + expects the input pointer to be dynamically allocated, and frees + it. As a result, client code doesn't need to know how large + the implementing object type is, because it will never need to + allocate it (on the stack or anywhere else). + + - Unlike in C++, the abstraction's `vtable' structure does not + only hold methods that you can call on an instance object. It + can also hold several other kinds of thing: + + - Methods that you can call _without_ an instance object, + given only the vtable structure identifying a particular + implementation of the trait. You might think of these as + `static methods', as in C++, except that they're _virtual_ + - the same code can call the static method of a different + `class' given a different vtable pointer. So they're more + like `virtual static methods', which is a concept C++ + doesn't have. An example is the pubkey_bits method in + ssh_keyalg. + + - The most important case of a `virtual static method' is the + new method that allocates and returns a new object. You can + think of it as a `virtual constructor' - another concept C++ + doesn't have. (However, not all types need one of these: see + below.) + + - The vtable can also contain constant data relevant to the + class as a whole - `virtual constant data'. For example, a + cryptographic hash function will contain an integer field + giving the length of the output hash, and most crypto + primitives will contain a string field giving the identifier + used in the SSH protocol that describes that primitive. + + The effect of all of this is that you can make other pieces of + code able to use any instance of one of these types, by passing + it an actual vtable as a parameter. For example, the hash_simple + function takes an ssh_hashalg vtable pointer specifying any + hash algorithm you like, and internally, it creates an object + of that type, uses it, and frees it. In C++, you'd probably + do this using a template, which would mean you had multiple + specialisations of hash_simple - and then it would be much more + difficult to decide _at run time_ which one you needed to use. + Here, hash_simple is still just one function, and you can decide + as late as you like which vtable to pass to it. + + - The abstract _instance_ structure can also contain publicly + visible data fields (this time, usually treated as mutable) + which are common to all implementations of the trait. For + example, BinaryPacketProtocol has lots of these. + + - Not all abstractions of this kind want virtual constructors. It + depends on how different the implementations are. + + With a crypto primitive like a hash algorithm, the constructor + call looks the same for every implementing type, so it makes + sense to have a standardised virtual constructor in the + vtable and a ssh_hash_new wrapper function which can make an + instance of whatever vtable you pass it. And then you make + all the vtable objects themselves globally visible throughout + the source code, so that any module can call (for example) + ssh_hash_new(&ssh_sha256). + + But with other kinds of object, the constructor for each + implementing type has to take a different set of parameters. + For example, implementations of Socket are not generally + interchangeable at construction time, because constructing + different kinds of socket require totally different kinds of + address parameter. In that situation, it makes more sense to + keep the vtable structure itself private to the implementing + source file, and instead, publish an ordinary constructing + function that allocates and returns an instance of that + particular subtype, taking whatever parameters are appropriate + to that subtype. + + - If you do have virtual constructors, you can choose whether + they take a vtable pointer as a parameter (as shown above), or + an _existing_ instance object. In the latter case, they can + refer to the object itself as well as the vtable. For example, + you could have a trait come with a virtual constructor called + `clone', meaning `Make a copy of this object, no matter which + implementation it is.' + + - Sometimes, a single vtable structure type can be shared + between two completely different object types, and contain + all the methods for both. For example, ssh_compression_alg + contains methods to create, use and free ssh_compressor and + ssh_decompressor objects, which are not interchangeable - but + putting their methods in the same vtable means that it's easy to + create a matching pair of objects that are compatible with each + other. + + - Passing the vtable itself as an argument to the new method is + not compulsory: if a given new implementation is only used by + a single vtable, then that function can simply hard-code the + vtable pointer that it writes into the object it constructs. + But passing the vtable is more flexible, because it allows + a single constructor function to be shared between multiple + slightly different object types. For example, SHA-384 and SHA- + 512 share the same new method and the same implementation data + type, because they're very nearly the same hash algorithm - but + a couple of the other methods in their vtables are different, + because the `reset' function has to set up the initial algorithm + state differently, and the `digest' method has to write out a + different amount of data. + + One practical advantage of having the myabs__foo_ family of + inline wrapper functions in the header file is that if you + change your mind later about whether the vtable needs to be + passed to new, you only have to update the myabs_new wrapper, + and then the existing call sites won't need changing. + + - Another piece of `stunt object orientation' made possible by + this scheme is that you can write two vtables that both use the + same structure layout for the implementation object, and have + an object _transform from one to the other_ part way through + its lifetime, by overwriting its own vtable pointer field. For + example, the sesschan type that handles the server side of an + SSH terminal session will sometimes transform in mid-lifetime + into an SCP or SFTP file-transfer channel in this way, at the + point where the client sends an `exec' or `subsystem' request + that indicates that that's what it wants to do with the channel. + + This concept would be difficult to arrange in C++. In Rust, + it wouldn't even _make sense_, because in Rust, objects + implementing a trait don't even contain a vtable pointer at + all - instead, the `trait object' type (identifying a specific + instance of some implementation of a given trait) consists of + a pair of pointers, one to the object itself and one to the + vtable. In that model, the only way you could make an existing + object turn into a different trait would be to know where all + the pointers to it were stored elsewhere in the program, and + persuade all their owners to rewrite them. + + - Another stunt you can do is to have a vtable that doesn't have + a corresponding implementation structure at all, because the + only methods implemented in it are the constructors, and they + always end up returning an implementation of some other vtable. + For example, some of PuTTY's crypto primitives have a hardware- + accelerated version and a pure software version, and decide at + run time which one to use (based on whether the CPU they're + running on supports the necessary acceleration instructions). + So, for example, there are vtables for ssh_sha256_sw and + ssh_sha256_hw, each of which has its own data layout and its own + implementations of all the methods; and then there's a top-level + vtable ssh_sha256, which only provides the `new' method, and + implements it by calling the `new' method on one or other of the + subtypes depending on what it finds out about the machine it's + running on. That top-level selector vtable is nearly always the + one used by client code. (Except for the test suite, which has + to instantiate both of the subtypes in order to make sure they + both pass the tests.) + + As a result, the top-level selector vtable ssh_sha256 doesn't + need to implement any method that takes an ssh_cipher * + parameter, because no ssh_cipher object is ever constructed + whose vt field points to &ssh_sha256: they all point to one of + the other two full implementation vtables. + + E.14 Single compilation of each source file The PuTTY build system for any given platform works on the following very simple model: @@ -9602,7 +10841,7 @@ Appendix D: PuTTY hacking guide hostile jurisdictions), the _wrong_ way to do it is by adding #ifdefs in (say) proxy.c. This would require separate compilation of proxy.c for PuTTY and PuTTYtel, which means that the entire - Makefile-generation architecture (see section D.11) would have to + Makefile-generation architecture (see section E.11) would have to be significantly redesigned. Unless you are prepared to do that redesign yourself, _and_ guarantee that it will still port to any future platforms we might decide to run on, you should not attempt @@ -9623,7 +10862,7 @@ Appendix D: PuTTY hacking guide to use), and also in misc.c (the Windows-specific `Minefield' memory diagnostic system). It should be used sparingly, though, if at all. - D.14 Do as we say, not as we do + E.15 Do as we say, not as we do The current PuTTY code probably does not conform strictly to _all_ of the principles listed above. There may be the occasional SSH- @@ -9636,7 +10875,7 @@ Appendix D: PuTTY hacking guide and we would welcome patches that fix any existing problems. Please try to help us make our code better, not worse! -Appendix E: PuTTY download keys and signatures +Appendix F: PuTTY download keys and signatures ---------------------------------------------- We create GPG signatures for all the PuTTY files distributed from @@ -9661,10 +10900,10 @@ Appendix E: PuTTY download keys and signatures anything to do with keys used with SSH - they are purely for verifying the origin of files distributed by the PuTTY team.) - E.1 Public keys + F.1 Public keys We maintain multiple keys, stored with different levels of security - due to being used in different ways. See section E.2 below for + due to being used in different ways. See section F.2 below for details. The keys we provide are: @@ -9715,13 +10954,13 @@ Appendix E: PuTTY download keys and signatures RSA, 3072-bit. Key ID: 657D487977F95C98. Fingerprint: A680 0082 2998 6E46 22CA 0E43 657D 4879 77F9 5C98 - E.2 Security details + F.2 Security details The various keys have various different security levels. This section explains what those security levels are, and how far you can expect to trust each key. - E.2.1 The Development Snapshots key + F.2.1 The Development Snapshots key The Development Snapshots private key is stored _without a passphrase_. This is necessary, because the snapshots are generated @@ -9756,7 +10995,7 @@ Appendix E: PuTTY download keys and signatures machines. But when you see a signature, you should always be certain of precisely what it guarantees and precisely what it does not. - E.2.2 The Releases key + F.2.2 The Releases key The Releases key is more secure: because it is only used at release time, to sign each release by hand, we can store it encrypted. @@ -9765,13 +11004,13 @@ Appendix E: PuTTY download keys and signatures local machines. So an attacker wanting to steal it would have to also steal the passphrase. - E.2.3 The Secure Contact Key + F.2.3 The Secure Contact Key The Secure Contact Key is stored with a similar level of security to the Release Key: it is stored with a passphrase, and no automated script has access to it. - E.2.4 The Master Keys + F.2.4 The Master Keys The Master Key signs almost nothing. Its purpose is to bind the other keys together and certify that they are all owned by the same @@ -9791,7 +11030,7 @@ Appendix E: PuTTY download keys and signatures can still be reasonably confident that an attacker would find it hard to substitute fake keys on all the public keyservers at once. - E.3 Key rollover + F.3 Key rollover Our current keys were generated in August 2018. @@ -9878,7 +11117,7 @@ Appendix E: PuTTY download keys and signatures version: 1024D/165E56F77D3E4A00). Fingerprint: 63DD 8EF8 32F5 D777 9FF0 2947 165E 56F7 7D3E 4A00 -Appendix F: SSH-2 names specified for PuTTY +Appendix G: SSH-2 names specified for PuTTY ------------------------------------------- There are various parts of the SSH-2 protocol where things @@ -9886,7 +11125,7 @@ Appendix F: SSH-2 names specified for PuTTY @putty.projects.tartarus.org are reserved for allocation by the PuTTY team. Allocated names are documented here. - F.1 Connection protocol channel request names + G.1 Connection protocol channel request names These names can be sent in a SSH_MSG_CHANNEL_REQUEST message. @@ -9908,9 +11147,9 @@ Appendix F: SSH-2 names specified for PuTTY request and respond with SSH_MSG_CHANNEL_FAILURE. (Some SSH servers get confused by this message, so there is a - bug-compatibility mode for disabling it. See section 4.28.3.) + bug-compatibility mode for disabling it. See section 4.26.3.) - F.2 Key exchange method names + G.2 Key exchange method names rsa-sha1-draft-00@putty.projects.tartarus.org @@ -9938,7 +11177,7 @@ Appendix F: SSH-2 names specified for PuTTY RFC 4432. They have been superseded by rsa1024-sha1 and rsa2048- sha256. - F.3 Encryption algorithm names + G.3 Encryption algorithm names arcfour128-draft-00@putty.projects.tartarus.org @@ -9947,4 +11186,62 @@ Appendix F: SSH-2 names specified for PuTTY These were used in drafts of what eventually became RFC 4345. They have been superseded by arcfour128 and arcfour256. -[PuTTY release 0.73] + G.4 Agent extension request names + + The SSH agent protocol, which is only specified in an Internet- + Draft at the time of writing (draft-miller-ssh-agent), + defines an extension mechanism. These names can be sent in an + SSH_AGENTC_EXTENSION message. + + add-ppk@putty.projects.tartarus.org + + The payload is a single SSH-2 string containing a keypair in + the PPK format defined in appendix C. Compared to the standard + SSH_AGENTC_ADD_IDENTITY, this extension allows adding keys + in encrypted form, with the agent requesting a decryption + passphrase from the user on demand, and able to revert the key + to encrypted form. + + reencrypt@putty.projects.tartarus.org + + The payload is a single SSH-2 string specifying a public key + blob, as in SSH_AGENTC_REMOVE_IDENTITY. Requests that the agent + forget any cleartext form of a specific key. + + Returns SSH_AGENT_SUCCESS if the agent ended up holding the + key only in encrypted form (even if it was already encrypted); + returns SSH_AGENT_EXTENSION_FAILURE if not (if it wasn't held by + the agent at all, or only in cleartext form). + + reencrypt-all@putty.projects.tartarus.org + + No payload. Requests that the agent forget the cleartext form of + any keys for which it holds an encrypted form. + + If the agent holds any keys with an encrypted form (or no keys + at all), returns SSH_AGENT_SUCCESS to indicate that no such keys + are now held in cleartext form, followed by a uint32 specifying + how many keys remain in cleartext form (because the agent didn't + hold an encrypted form for them). If the agent holds nothing but + keys in cleartext form, returns SSH_AGENT_EXTENSION_FAILURE. + + list-extended@putty.projects.tartarus.org + + No payload. Returns SSH_AGENT_SUCCESS followed by a list of + identities similar to SSH_AGENT_IDENTITIES_ANSWER, except that + each key has an extra SSH-2 string at the end. Currently that + string contains a single uint32 flags word, with the following + bits defined: + + Bit 0 + + If set, key is held with an encrypted form (so that the + `reencrypt' extension can do something useful with it). + + Bit 1 + + If set, key's cleartext form is not currently held (so the + user will have to supply a passphrase before the key can be + used). + +[PuTTY release 0.76] diff --git a/doc/puttygen.1 b/doc/puttygen.1 index fcb7f1e..70fee3f 100644 --- a/doc/puttygen.1 +++ b/doc/puttygen.1 @@ -7,16 +7,17 @@ .SH "SYNOPSIS" .PP .nf -\fBputtygen\fP\ (\ \fIkeyfile\fP\ |\ \fB\-t\fP\ \fIkeytype\fP\ [\ \fB\-b\fP\ \fIbits\fP\ ]\ ) -\ \ \ \ \ \ \ \ \ [\ \fB\-C\fP\ \fInew\-comment\fP\ ]\ [\ \fB\-P\fP\ ]\ [\ \fB\-q\fP\ ] -\ \ \ \ \ \ \ \ \ [\ \fB\-O\fP\ \fIoutput\-type\fP\ |\ \fB\-l\fP\ |\ \fB\-L\fP\ |\ \fB\-p\fP\ ] +\fBputtygen\fP\ (\ \fIkeyfile\fP\ |\ \fB\-t\fP\ \fIkeytype\fP\ [\ \fB\-b\fP\ \fIbits\fP\ ]\ [\ \fB\-\-primes\fP\ \fImethod\fP\ ]\ [\ \fB\-q\fP\ ]\ ) +\ \ \ \ \ \ \ \ \ [\ \fB\-C\fP\ \fInew\-comment\fP\ ]\ [\ \fB\-P\fP\ ]\ [\ \fB\-\-reencrypt\fP\ ] +\ \ \ \ \ \ \ \ \ [\ \fB\-O\fP\ \fIoutput\-type\fP\ |\ \fB\-l\fP\ |\ \fB\-L\fP\ |\ \fB\-p\fP\ |\ \fB\-\-dump\fP\ ]\ [\ \fB\-E\fP\ \fIfptype\fP\ ] +\ \ \ \ \ \ \ \ \ \ \ \ [\ \fB\-\-ppk\-param\fP\ \fIkey\fP\fB=\fP\fIvalue\fP\fB,\fP...\ ] \ \ \ \ \ \ \ \ \ [\ \fB\-o\fP\ \fIoutput\-file\fP\ ] .fi .SH "DESCRIPTION" .PP \fBputtygen\fP is a tool to generate and manipulate SSH public and private key pairs. It is part of the PuTTY suite, although it can also interoperate with the key formats used by some other SSH clients. .PP -When you run \fBputtygen\fP, it does three things. Firstly, it either loads an existing key file (if you specified \fIkeyfile\fP), or generates a new key (if you specified \fIkeytype\fP). Then, it optionally makes modifications to the key (changing the comment and/or the passphrase); finally, it outputs the key, or some information about the key, to a file. +When you run \fBputtygen\fP, it does three things. Firstly, it either loads an existing key file (if you specified \fIkeyfile\fP), or generates a new key (if you specified \fIkeytype\fP). Then, it optionally makes modifications to the key (such as changing the comment and/or the passphrase); finally, it outputs the key, or some information about the key, to a file. .PP All three of these phases are controlled by the options described in the following section. .SH "OPTIONS" @@ -25,7 +26,7 @@ In the first phase, \fBputtygen\fP either loads or generates a key. Note that ge .PP The options to control this phase are: .IP "\fIkeyfile\fP" -Specify a key file to be loaded. +Specify a key file to be loaded. (Use `\fB-\fP' to read a key file from standard input.) .RS .PP Usually this will be a private key, which can be in the (de facto standard) SSH-1 key format, or in PuTTY's SSH-2 key format, or in either of the SSH-2 private key formats used by OpenSSH and ssh.com's implementation. @@ -33,9 +34,21 @@ Usually this will be a private key, which can be in the (de facto standard) SSH- You can also specify a file containing only a \fIpublic\fP key here. The operations you can do are limited to outputting another public key format or a fingerprint. Public keys can be in RFC 4716 or OpenSSH format, or the standard SSH-1 format. .RE .IP "\fB\-t\fP \fIkeytype\fP" -Specify a type of key to generate. The acceptable values here are \fBrsa\fP, \fBdsa\fP, \fBecdsa\fP, and \fBed25519\fP (to generate SSH-2 keys), and \fBrsa1\fP (to generate SSH-1 keys). +Specify a type of key to generate. The acceptable values here are \fBrsa\fP, \fBdsa\fP, \fBecdsa\fP, \fBeddsa\fP, \fBed25519\fP, and \fBed448\fP (to generate SSH-2 keys), and \fBrsa1\fP (to generate SSH-1 keys). .IP "\fB\-b\fP \fIbits\fP" -Specify the size of the key to generate, in bits. Default is 2048. +Specify the size of the key to generate, in bits. Default for \fBrsa\fP and \fBdsa\fP keys is 2048. +.IP "\fB\-\-primes\fP \fImethod\fP" +Method for generating prime numbers. The acceptable values here are \fBprobable\fP (the default), \fBproven\fP, and \fBproven-even\fP; the later methods are slower. (Various synonyms for these method names are also accepted.) +.RS +.PP +The `probable primes' method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much. +.PP +The other methods cause PuTTYgen to use numbers that it is \fIsure\fP are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method. +.PP +You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method. +.RE +.IP "\fB\-\-strong-rsa\fP" +When generating an RSA key, make sure the prime factors of the key modulus are `strong primes'. A strong prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on \fIunless\fP you have a local standard that recommends it. .IP "\fB\-q\fP" Suppress the progress display when generating a new key. .IP "\fB\-\-old\-passphrase\fP \fIfile\fP" @@ -48,25 +61,63 @@ In the second phase, \fBputtygen\fP optionally alters properties of the key it h Specify a comment string to describe the key. This comment string will be used by PuTTY to identify the key to you (when asking you to enter the passphrase, for example, so that you know which passphrase to type). .IP "\fB\-P\fP" Indicate that you want to change the key's passphrase. This is automatic when you are generating a new key, but not when you are modifying an existing key. +.IP "\fB\-\-reencrypt\fP" +For an existing private key saved with a passphrase, refresh the encryption without changing the passphrase. +.RS +.PP +This is most likely to be useful with the \fB\-\-ppk-param\fP option, to change some aspect of the key file\*(Aqs format or encryption. +.RE +.IP "\fB\-\-ppk-param\fP \fIkey\fP\fB=\fP\fIvalue\fP\fB,\fP..." +When saving a PPK file (the default \fBprivate\fP output type for SSH-2 keys), adjust details of the on-disk format. +.RS +.PP +Aspects to change are specified as a series of \fIkey\fP\fB=\fP\fIvalue\fP pairs separated by commas. The \fIkey\fPs are: +.IP "\fBversion\fP" +The PPK format version. Possible values are \fB3\fP (the default) and \fB2\fP (which is less resistant to brute-force decryption, but which you might need if your key needs to be used by old versions of PuTTY tools, or other PPK consumers). +.RS +.PP +The following \fIkey\fPs only affect PPK version 3 files. +.RE +.IP "\fBkdf\fP" +The variant of the Argon2 key derivation function to use. Options are \fBargon2id\fP (default, and recommended), \fBargon2i\fP, and \fBargon2d\fP. +.RS +.PP +You might change this if you consider your exposure to side-channel attacks to be different to the norm. +.RE +.IP "\fBmemory\fP" +The amount of memory needed to decrypt the key, in Kbyte. Default is 8192 (i.e., 8 Mbyte). +.IP "\fBtime\fP" +Approximate time, on this machine, required to attempt decrypting the key, in milliseconds. Default is 100 (ms). +.IP "\fBpasses\fP" +Alternative to \fBtime\fP: explicitly specify the number of hash passes required to attempt decrypting the key. +.IP "\fBparallelism\fP" +Number of parallelisable threads that can be used to decrypt the key. Default is 1 (force decryption to run single-threaded). +.RE .PP In the third phase, \fBputtygen\fP saves the key or information about it. The options to control this are: .IP "\fB\-O\fP \fIoutput\-type\fP" Specify the type of output you want \fBputtygen\fP to produce. Acceptable options are: .RS .IP "\fBprivate\fP" -Save the private key in a format usable by PuTTY. This will either be the standard SSH-1 key format, or PuTTY's own SSH-2 key format. +Save the private key in a format usable by PuTTY. This will either be the standard SSH-1 key format, or PuTTY's own SSH-2 key format (`PPK'). This is the default. .IP "\fBpublic\fP" Save the public key only. For SSH-1 keys, the standard public key format will be used (`\fB1024 37 5698745\fP...'). For SSH-2 keys, the public key will be output in the format specified by RFC 4716, which is a multi-line text file beginning with the line `\fB---- BEGIN SSH2 PUBLIC KEY ----\fP'. .IP "\fBpublic-openssh\fP" Save the public key only, in a format usable by OpenSSH. For SSH-1 keys, this output format behaves identically to \fBpublic\fP. For SSH-2 keys, the public key will be output in the OpenSSH format, which is a single line (`\fBssh-rsa AAAAB3NzaC1yc2\fP...'). .IP "\fBfingerprint\fP" -Print the fingerprint of the public key. All fingerprinting algorithms are believed compatible with OpenSSH. +Print a fingerprint of the public key. The \fB-E\fP option lets you specify which fingerprinting algorithm to use. All algorithms are believed compatible with OpenSSH. .IP "\fBprivate-openssh\fP" Save an SSH-2 private key in OpenSSH's format, using the oldest format available to maximise backward compatibility. This option is not permitted for SSH-1 keys. .IP "\fBprivate-openssh-new\fP" As \fBprivate-openssh\fP, except that it forces the use of OpenSSH\*(Aqs newer format even for RSA, DSA, and ECDSA keys. .IP "\fBprivate-sshcom\fP" Save an SSH-2 private key in ssh.com's format. This option is not permitted for SSH-1 keys. +.IP "\fBtext\fP" +Save a textual dump of the numeric components comprising the key (both the public and private parts, if present). Useful for debugging, or for using PuTTYgen as a key generator for applications other than SSH. +.RS +.PP +The output consists of a series of \fBname=value\fP lines, where each \fBvalue\fP is either a C-like string literal in double quotes, or a hexadecimal number starting with \fB0x...\fP +.RE .PP If no output type is specified, the default is \fBprivate\fP. .RE @@ -78,6 +129,10 @@ Synonym for `\fB-O fingerprint\fP'. Synonym for `\fB-O public-openssh\fP'. .IP "\fB\-p\fP" Synonym for `\fB-O public\fP'. +.IP "\fB\-\-dump\fP" +Synonym for `\fB-O text\fP'. +.IP "\fB-E\fP \fIfptype\fP" +Specify the algorithm to use if generating a fingerprint. The options are \fBsha256\fP (the default) and \fBmd5\fP. .IP "\fB\-\-new\-passphrase\fP \fIfile\fP" Specify a file name; the first line will be read from this file (removing any trailing newline) and used as the new passphrase. If the file is empty then the saved key will be unencrypted. \fBCAUTION:\fP If the passphrase is important, the file should be stored on a temporary filesystem or else securely erased after use. .PP @@ -126,7 +181,7 @@ To convert a key \fIfrom\fP another format (\fBputtygen\fP will automatically de puttygen\ my\-ssh.com\-key\ \-o\ mykey.ppk .fi .PP -To display the fingerprint of a key (some key types require a passphrase to extract even this much information): +To display the SHA-256 fingerprint of a key (some key types require a passphrase to extract even this much information): .PP .nf puttygen\ \-l\ mykey.ppk diff --git a/doc/puttytel.1 b/doc/puttytel.1 index 45feba1..9153769 100644 --- a/doc/puttytel.1 +++ b/doc/puttytel.1 @@ -3,7 +3,7 @@ .TH "puttytel" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP -\fBputtytel\fP \- GUI Telnet and Rlogin client for X +\fBputtytel\fP \- GUI Telnet, Rlogin, and SUPDUP client for X .SH "SYNOPSIS" .PP .nf @@ -11,7 +11,7 @@ .fi .SH "DESCRIPTION" .PP -\fBputtytel\fP is a graphical Telnet and Rlogin client for X. It is a direct port of the Windows Telnet and Rlogin client of the same name, and a cut-down cryptography-free version of PuTTY. +\fBputtytel\fP is a graphical Telnet, Rlogin, and SUPDUP client for X. It is a direct port of the Windows Telnet, Rlogin, and SUPDUP client of the same name, and a cut-down cryptography-free version of PuTTY. .SH "OPTIONS" .PP The command-line options supported by \fBputtytel\fP are: @@ -67,7 +67,7 @@ Display a message summarizing the available options. Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. .IP "\fB\-load\fP \fIsession\fP" Load a saved session by name. This allows you to run a saved session straight from the command line without having to go through the configuration box first. -.IP "\fB\-telnet\fP, \fB\-rlogin\fP, \fB\-raw\fP" +.IP "\fB\-telnet\fP, \fB\-rlogin\fP, \fB\-supdup\fP, \fB\-raw\fP" Select the protocol \fBputtytel\fP will use to make the connection. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. diff --git a/doc/sshnames.but b/doc/sshnames.but index dd46f99..6cf82f7 100644 --- a/doc/sshnames.but +++ b/doc/sshnames.but @@ -65,3 +65,66 @@ They have been superseded by \cw{rsa1024-sha1} and \cw{rsa2048-sha256}. \dd These were used in drafts of what eventually became RFC\_4345. They have been superseded by \cw{arcfour128} and \cw{arcfour256}. + +\H{sshnames-agent} Agent extension request names + +The SSH agent protocol, which is only specified in an Internet-Draft +at the time of writing +(\W{https://tools.ietf.org/html/draft-miller-ssh-agent}\cw{draft-miller-ssh-agent}), +defines an extension mechanism. These names can be sent in an +\cw{SSH_AGENTC_EXTENSION} message. + +\dt \cw{add-ppk@putty.projects.tartarus.org} + +\dd The payload is a single SSH-2 \cw{string} containing a keypair in +the PPK format defined in \k{ppk}. Compared to the standard +\cw{SSH_AGENTC_ADD_IDENTITY}, this extension allows adding keys in +encrypted form, with the agent requesting a decryption passphrase from +the user on demand, and able to revert the key to encrypted form. + +\dt \cw{reencrypt@putty.projects.tartarus.org} + +\dd The payload is a single SSH-2 \cw{string} specifying a public key +blob, as in \cw{SSH_AGENTC_REMOVE_IDENTITY}. Requests that the agent +forget any cleartext form of a specific key. + +\lcont{ +Returns \cw{SSH_AGENT_SUCCESS} if the agent ended up holding the key +only in encrypted form (even if it was already encrypted); returns +\cw{SSH_AGENT_EXTENSION_FAILURE} if not (if it wasn't held by the +agent at all, or only in cleartext form). +} + +\dt \cw{reencrypt-all@putty.projects.tartarus.org} + +\dd No payload. Requests that the agent forget the cleartext form of +any keys for which it holds an encrypted form. + +\lcont{ +If the agent holds any keys with an encrypted form (or no keys at all), +returns \cw{SSH_AGENT_SUCCESS} to indicate that no such keys are now +held in cleartext form, followed by a \cw{uint32} specifying how many keys +remain in cleartext form (because the agent didn't hold an encrypted +form for them). If the agent holds nothing but keys in cleartext form, +returns \cw{SSH_AGENT_EXTENSION_FAILURE}. +} + +\dt \cw{list-extended@putty.projects.tartarus.org} + +\dd No payload. Returns \cw{SSH_AGENT_SUCCESS} followed by a list of +identities similar to \cw{SSH_AGENT_IDENTITIES_ANSWER}, except that +each key has an extra SSH-2 \cw{string} at the end. Currently that +\cw{string} contains a single \cw{uint32} flags word, with the +following bits defined: + +\lcont{ +\dt Bit 0 + +\dd If set, key is held with an encrypted form (so that the +\c{reencrypt} extension can do something useful with it). + +\dt Bit 1 + +\dd If set, key's cleartext form is not currently held (so the +user will have to supply a passphrase before the key can be used). +} diff --git a/doc/udp.but b/doc/udp.but index 406b469..b3570d5 100644 --- a/doc/udp.but +++ b/doc/udp.but @@ -142,12 +142,11 @@ potentially managing multiple sessions. Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular -login session: \c{flags} defines properties that are expected to -apply equally to \e{all} the sessions run by a single PuTTY process, -the random number state in \cw{sshrand.c} and the timer list in -\cw{timing.c} serve all sessions equally, and so on. But most data -is specific to a particular network session, and is therefore stored -in dynamically allocated data structures, and pointers to these +login session. The random number state in \cw{sshrand.c}, the timer +list in \cw{timing.c} and the queue of top-level callbacks in +\cw{callback.c} serve all sessions equally. But most data is specific +to a particular network session, and is therefore stored in +dynamically allocated data structures, and pointers to these structures are passed around between functions. Platform-specific code can reverse this decision if it likes. The @@ -412,6 +411,386 @@ ensure that when you free that memory you reset the pointer field to is called, it can reliably free the memory if there is any, and not crash if there isn't. +\H{udp-traits} Explicit vtable structures to implement traits + +A lot of PuTTY's code is written in a style that looks structurally +rather like an object-oriented language, in spite of PuTTY being a +pure C program. + +For example, there's a single data type called \cw{ssh_hash}, which is +an abstraction of a secure hash function, and a bunch of functions +called things like \cw{ssh_hash_}\e{foo} that do things with those +data types. But in fact, PuTTY supports many different hash functions, +and each one has to provide its own implementation of those functions. + +In C++ terms, this is rather like having a single abstract base class, +and multiple concrete subclasses of it, each of which fills in all the +pure virtual methods in a way that's compatible with the data fields +of the subclass. The implementation is more or less the same, as well: +in C, we do explicitly in the source code what the C++ compiler will +be doing behind the scenes at compile time. + +But perhaps a closer analogy in functional terms is the Rust concept +of a \q{trait}, or the Java idea of an \q{interface}. C++ supports a +multi-level hierarchy of inheritance, whereas PuTTY's system \dash +like traits or interfaces \dash has only two levels, one describing a +generic object of a type (e.g. a hash function) and another describing +a specific implementation of that type (e.g. SHA-256). + +The PuTTY code base has a standard idiom for doing this in C, as +follows. + +Firstly, we define two \cw{struct} types for our trait. One of them +describes a particular \e{kind} of implementation of that trait, and +it's full of (mostly) function pointers. The other describes a +specific \e{instance} of an implementation of that trait, and it will +contain a pointer to a \cw{const} instance of the first type. For +example: + +\c typedef struct MyAbstraction MyAbstraction; +\c typedef struct MyAbstractionVtable MyAbstractionVtable; +\c +\c struct MyAbstractionVtable { +\c MyAbstraction *(*new)(const MyAbstractionVtable *vt); +\c void (*free)(MyAbstraction *); +\c void (*modify)(MyAbstraction *, unsigned some_parameter); +\c unsigned (*query)(MyAbstraction *, unsigned some_parameter); +\c }; +\c +\c struct MyAbstraction { +\c const MyAbstractionVtable *vt; +\c }; + +Here, we imagine that \cw{MyAbstraction} might be some kind of object +that contains mutable state. The associated vtable structure shows +what operations you can perform on a \cw{MyAbstraction}: you can +create one (dynamically allocated), free one you already have, or call +the example methods \q{modify} (to change the state of the object in +some way) and \q{query} (to return some value derived from the +object's current state). + +(In most cases, the vtable structure has a name ending in \cq{vtable}. +But for historical reasons a lot of the crypto primitives that use +this scheme \dash ciphers, hash functions, public key methods and so +on \dash instead have names ending in \cq{alg}, on the basis that the +primitives they implement are often referred to as \q{encryption +algorithms}, \q{hash algorithms} and so forth.) + +Now, to define a concrete instance of this trait, you'd define a +\cw{struct} that contains a \cw{MyAbstraction} field, plus any other +data it might need: + +\c struct MyImplementation { +\c unsigned internal_data[16]; +\c SomeOtherType *dynamic_subthing; +\c +\c MyAbstraction myabs; +\c }; + +Next, you'd implement all the necessary methods for that +implementation of the trait, in this kind of style: + +\c static MyAbstraction *myimpl_new(const MyAbstractionVtable *vt) +\c { +\c MyImplementation *impl = snew(MyImplementation); +\c memset(impl, 0, sizeof(*impl)); +\c impl->dynamic_subthing = allocate_some_other_type(); +\c impl->myabs.vt = vt; +\c return &impl->myabs; +\c } +\c +\c static void myimpl_free(MyAbstraction *myabs) +\c { +\c MyImplementation *impl = container_of(myabs, MyImplementation, myabs); +\c free_other_type(impl->dynamic_subthing); +\c sfree(impl); +\c } +\c +\c static void myimpl_modify(MyAbstraction *myabs, unsigned param) +\c { +\c MyImplementation *impl = container_of(myabs, MyImplementation, myabs); +\c impl->internal_data[param] += do_something_with(impl->dynamic_subthing); +\c } +\c +\c static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) +\c { +\c MyImplementation *impl = container_of(myabs, MyImplementation, myabs); +\c return impl->internal_data[param]; +\c } + +Having defined those methods, now we can define a \cw{const} instance +of the vtable structure containing pointers to them: + +\c const MyAbstractionVtable MyImplementation_vt = { +\c .new = myimpl_new, +\c .free = myimpl_free, +\c .modify = myimpl_modify, +\c .query = myimpl_query, +\c }; + +\e{In principle}, this is all you need. Client code can construct a +new instance of a particular implementation of \cw{MyAbstraction} by +digging out the \cw{new} method from the vtable and calling it (with +the vtable itself as a parameter), which returns a \cw{MyAbstraction +*} pointer that identifies a newly created instance, in which the +\cw{vt} field will contain a pointer to the same vtable structure you +passed in. And once you have an instance object, say \cw{MyAbstraction +*myabs}, you can dig out one of the other method pointers from the +vtable it points to, and call that, passing the object itself as a +parameter. + +But in fact, we don't do that, because it looks pretty ugly at all the +call sites. Instead, what we generally do in this code base is to +write a set of \cw{static inline} wrapper functions in the same header +file that defined the \cw{MyAbstraction} structure types, like this: + +\c static MyAbstraction *myabs_new(const MyAbstractionVtable *vt) +\c { return vt->new(vt); } +\c static void myabs_free(MyAbstraction *myabs) +\c { myabs->vt->free(myabs); } +\c static void myimpl_modify(MyAbstraction *myabs, unsigned param) +\c { myabs->vt->modify(myabs, param); } +\c static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) +\c { return myabs->vt->query(myabs, param); } + +And now call sites can use those reasonably clean-looking wrapper +functions, and shouldn't ever have to directly refer to the \cw{vt} +field inside any \cw{myabs} object they're holding. For example, you +might write something like this: + +\c MyAbstraction *myabs = myabs_new(&MyImplementation_vtable); +\c myabs_update(myabs, 10); +\c unsigned output = myabs_query(myabs, 2); +\c myabs_free(myabs); + +and then all this code can use a different implementation of the same +abstraction by just changing which vtable pointer it passed in in the +first line. + +Some things to note about this system: + +\b The implementation instance type (here \cq{MyImplementation} +contains the abstraction type (\cq{MyAbstraction}) as one of its +fields. But that field is not necessarily at the start of the +structure. So you can't just \e{cast} pointers back and forth between +the two types. Instead: + +\lcont{ + +\b You \q{up-cast} from implementation to abstraction by taking the +address of the \cw{MyAbstraction} field. You can see the example +\cw{new} method above doing this, returning \cw{&impl->myabs}. All +\cw{new} methods do this on return. + +\b Going in the other direction, each method that was passed a generic +\cw{MyAbstraction *myabs} parameter has to recover a pointer to the +specific implementation type \cw{MyImplementation *impl}. The idiom +for doing that is to use the \cq{container_of} macro, also seen in the +Linux kernel code. Generally, \cw{container_of(p, Type, field)} says: +\q{I'm confident that the pointer value \cq{p} is pointing to the +field called \cq{field} within a larger \cw{struct} of type \cw{Type}. +Please return me the pointer to the containing structure.} So in this +case, we take the \cq{myabs} pointer passed to the function, and +\q{down-cast} it into a pointer to the larger and more specific +structure type \cw{MyImplementation}, by adjusting the pointer value +based on the offset within that structure of the field called +\cq{myabs}. + +This system is flexible enough to permit \q{multiple inheritance}, or +rather, multiple \e{implementation}: having one object type implement +more than one trait. For example, the \cw{Proxy} type implements both +the \cw{Socket} trait and the \cw{Plug} trait that connects to it, +because it has to act as an adapter between another instance of each +of those types. + +It's also perfectly possible to have the same object implement the +\e{same} trait in two different ways. At the time of writing this I +can't think of any case where we actually do this, but a theoretical +example might be if you needed to support a trait like \cw{Comparable} +in two ways that sorted by different criteria. There would be no +difficulty doing this in the PuTTY system: simply have your +implementation \cw{struct} contain two (or more) fields of the same +abstraction type. The fields will have different names, which makes it +easy to explicitly specify which one you're returning a pointer to +during up-casting, or which one you're down-casting from using +\cw{container_of}. And then both sets of implementation methods can +recover a pointer to the same containing structure. + +} + +\b Unlike in C++, all objects in PuTTY that use this system are +dynamically allocated. The \q{constructor} functions (whether they're +virtualised across the whole abstraction or specific to each +implementation) always allocate memory and return a pointer to it. The +\q{free} method (our analogue of a destructor) always expects the +input pointer to be dynamically allocated, and frees it. As a result, +client code doesn't need to know how large the implementing object +type is, because it will never need to allocate it (on the stack or +anywhere else). + +\b Unlike in C++, the abstraction's \q{vtable} structure does not only +hold methods that you can call on an instance object. It can also +hold several other kinds of thing: + +\lcont{ + +\b Methods that you can call \e{without} an instance object, given +only the vtable structure identifying a particular implementation of +the trait. You might think of these as \q{static methods}, as in C++, +except that they're \e{virtual} \dash the same code can call the +static method of a different \q{class} given a different vtable +pointer. So they're more like \q{virtual static methods}, which is a +concept C++ doesn't have. An example is the \cw{pubkey_bits} method in +\cw{ssh_keyalg}. + +\b The most important case of a \q{virtual static method} is the +\cw{new} method that allocates and returns a new object. You can think +of it as a \q{virtual constructor} \dash another concept C++ doesn't +have. (However, not all types need one of these: see below.) + +\b The vtable can also contain constant data relevant to the class as +a whole \dash \q{virtual constant data}. For example, a cryptographic +hash function will contain an integer field giving the length of the +output hash, and most crypto primitives will contain a string field +giving the identifier used in the SSH protocol that describes that +primitive. + +The effect of all of this is that you can make other pieces of code +able to use any instance of one of these types, by passing it an +actual vtable as a parameter. For example, the \cw{hash_simple} +function takes an \cw{ssh_hashalg} vtable pointer specifying any hash +algorithm you like, and internally, it creates an object of that type, +uses it, and frees it. In C++, you'd probably do this using a +template, which would mean you had multiple specialisations of +\cw{hash_simple} \dash and then it would be much more difficult to +decide \e{at run time} which one you needed to use. Here, +\cw{hash_simple} is still just one function, and you can decide as +late as you like which vtable to pass to it. + +} + +\b The abstract \e{instance} structure can also contain publicly +visible data fields (this time, usually treated as mutable) which are +common to all implementations of the trait. For example, +\cw{BinaryPacketProtocol} has lots of these. + +\b Not all abstractions of this kind want virtual constructors. It +depends on how different the implementations are. + +\lcont{ + +With a crypto primitive like a hash algorithm, the constructor call +looks the same for every implementing type, so it makes sense to have +a standardised virtual constructor in the vtable and a +\cw{ssh_hash_new} wrapper function which can make an instance of +whatever vtable you pass it. And then you make all the vtable objects +themselves globally visible throughout the source code, so that any +module can call (for example) \cw{ssh_hash_new(&ssh_sha256)}. + +But with other kinds of object, the constructor for each implementing +type has to take a different set of parameters. For example, +implementations of \cw{Socket} are not generally interchangeable at +construction time, because constructing different kinds of socket +require totally different kinds of address parameter. In that +situation, it makes more sense to keep the vtable structure itself +private to the implementing source file, and instead, publish an +ordinary constructing function that allocates and returns an instance +of that particular subtype, taking whatever parameters are appropriate +to that subtype. + +} + +\b If you do have virtual constructors, you can choose whether they +take a vtable pointer as a parameter (as shown above), or an +\e{existing} instance object. In the latter case, they can refer to +the object itself as well as the vtable. For example, you could have a +trait come with a virtual constructor called \q{clone}, meaning +\q{Make a copy of this object, no matter which implementation it is.} + +\b Sometimes, a single vtable structure type can be shared between two +completely different object types, and contain all the methods for +both. For example, \cw{ssh_compression_alg} contains methods to +create, use and free \cw{ssh_compressor} and \cw{ssh_decompressor} +objects, which are not interchangeable \dash but putting their methods +in the same vtable means that it's easy to create a matching pair of +objects that are compatible with each other. + +\b Passing the vtable itself as an argument to the \cw{new} method is +not compulsory: if a given \cw{new} implementation is only used by a +single vtable, then that function can simply hard-code the vtable +pointer that it writes into the object it constructs. But passing the +vtable is more flexible, because it allows a single constructor +function to be shared between multiple slightly different object +types. For example, SHA-384 and SHA-512 share the same \cw{new} method +and the same implementation data type, because they're very nearly the +same hash algorithm \dash but a couple of the other methods in their +vtables are different, because the \q{reset} function has to set up +the initial algorithm state differently, and the \q{digest} method has +to write out a different amount of data. + +\lcont{ + +One practical advantage of having the \cw{myabs_}\e{foo} family of +inline wrapper functions in the header file is that if you change your +mind later about whether the vtable needs to be passed to \cw{new}, +you only have to update the \cw{myabs_new} wrapper, and then the +existing call sites won't need changing. + +} + +\b Another piece of \q{stunt object orientation} made possible by this +scheme is that you can write two vtables that both use the same +structure layout for the implementation object, and have an object +\e{transform from one to the other} part way through its lifetime, by +overwriting its own vtable pointer field. For example, the +\cw{sesschan} type that handles the server side of an SSH terminal +session will sometimes transform in mid-lifetime into an SCP or SFTP +file-transfer channel in this way, at the point where the client sends +an \cq{exec} or \cq{subsystem} request that indicates that that's what +it wants to do with the channel. + +\lcont{ + +This concept would be difficult to arrange in C++. In Rust, it +wouldn't even \e{make sense}, because in Rust, objects implementing a +trait don't even contain a vtable pointer at all \dash instead, the +\q{trait object} type (identifying a specific instance of some +implementation of a given trait) consists of a pair of pointers, one +to the object itself and one to the vtable. In that model, the only +way you could make an existing object turn into a different trait +would be to know where all the pointers to it were stored elsewhere in +the program, and persuade all their owners to rewrite them. + +} + +\b Another stunt you can do is to have a vtable that doesn't have a +corresponding implementation structure at all, because the only +methods implemented in it are the constructors, and they always end up +returning an implementation of some other vtable. For example, some of +PuTTY's crypto primitives have a hardware-accelerated version and a +pure software version, and decide at run time which one to use (based +on whether the CPU they're running on supports the necessary +acceleration instructions). So, for example, there are vtables for +\cw{ssh_sha256_sw} and \cw{ssh_sha256_hw}, each of which has its own +data layout and its own implementations of all the methods; and then +there's a top-level vtable \cw{ssh_sha256}, which only provides the +\q{new} method, and implements it by calling the \q{new} method on one +or other of the subtypes depending on what it finds out about the +machine it's running on. That top-level selector vtable is nearly +always the one used by client code. (Except for the test suite, which +has to instantiate both of the subtypes in order to make sure they +both pass the tests.) + +\lcont{ + +As a result, the top-level selector vtable \cw{ssh_sha256} doesn't +need to implement any method that takes an \cw{ssh_cipher *} +parameter, because no \cw{ssh_cipher} object is ever constructed whose +\cw{vt} field points to \cw{&ssh_sha256}: they all point to one of the +other two full implementation vtables. + +} + \H{udp-compile-once} Single compilation of each source file The PuTTY build system for any given platform works on the following diff --git a/doc/using.but b/doc/using.but index 82a9033..02a6780 100644 --- a/doc/using.but +++ b/doc/using.but @@ -520,6 +520,37 @@ connection made by another copy of PuTTY, you might find the which host key it should be expecting. See \k{config-loghost} for details of this. +\H{using-serial} Connecting to a local serial line + +PuTTY can connect directly to a local serial line as an alternative +to making a network connection. In this mode, text typed into the +PuTTY window will be sent straight out of your computer's serial +port, and data received through that port will be displayed in the +PuTTY window. You might use this mode, for example, if your serial +port is connected to another computer which has a serial connection. + +To make a connection of this type, simply select \q{Serial} from the +\q{Connection type} radio buttons on the \q{Session} configuration +panel (see \k{config-hostname}). The \q{Host Name} and \q{Port} +boxes will transform into \q{Serial line} and \q{Speed}, allowing +you to specify which serial line to use (if your computer has more +than one) and what speed (baud rate) to use when transferring data. +For further configuration options (data bits, stop bits, parity, +flow control), you can use the \q{Serial} configuration panel (see +\k{config-serial}). + +After you start up PuTTY in serial mode, you might find that you +have to make the first move, by sending some data out of the serial +line in order to notify the device at the other end that someone is +there for it to talk to. This probably depends on the device. If you +start up a PuTTY serial session and nothing appears in the window, +try pressing Return a few times and see if that helps. + +A serial line provides no well defined means for one end of the +connection to notify the other that the connection is finished. +Therefore, PuTTY in serial mode will remain connected until you +close the window using the close button. + \H{using-rawprot} Making \i{raw TCP connections} A lot of \I{debugging Internet protocols}Internet protocols are @@ -551,36 +582,61 @@ protocol}\q{Raw}, from the \q{Protocol} buttons in the \q{Session} configuration panel. (See \k{config-hostname}.) You can then enter a host name and a port number, and make the connection. -\H{using-serial} Connecting to a local serial line +\H{using-telnet} Connecting using the \i{Telnet} protocol -PuTTY can connect directly to a local serial line as an alternative -to making a network connection. In this mode, text typed into the -PuTTY window will be sent straight out of your computer's serial -port, and data received through that port will be displayed in the -PuTTY window. You might use this mode, for example, if your serial -port is connected to another computer which has a serial connection. +PuTTY can use the Telnet protocol to connect to a server. -To make a connection of this type, simply select \q{Serial} from the -\q{Connection type} radio buttons on the \q{Session} configuration -panel (see \k{config-hostname}). The \q{Host Name} and \q{Port} -boxes will transform into \q{Serial line} and \q{Speed}, allowing -you to specify which serial line to use (if your computer has more -than one) and what speed (baud rate) to use when transferring data. -For further configuration options (data bits, stop bits, parity, -flow control), you can use the \q{Serial} configuration panel (see -\k{config-serial}). +Telnet was perhaps the most popular remote login protocol before SSH +was introduced. It was general enough to be used by multiple server +operating systems (Unix and VMS in particular), and supported many +optional protocol extensions providing extra support for particular +server features. -After you start up PuTTY in serial mode, you might find that you -have to make the first move, by sending some data out of the serial -line in order to notify the device at the other end that someone is -there for it to talk to. This probably depends on the device. If you -start up a PuTTY serial session and nothing appears in the window, -try pressing Return a few times and see if that helps. +Unlike SSH, Telnet runs over an unsecured network connection, so it is +a very bad idea to use it over the hostile Internet (though it is +still used to some extent as of 2020). -A serial line provides no well defined means for one end of the -connection to notify the other that the connection is finished. -Therefore, PuTTY in serial mode will remain connected until you -close the window using the close button. +\H{using-rlogin} Connecting using the \i{Rlogin} protocol + +PuTTY can use the Rlogin protocol to connect to a server. + +Rlogin was similar to Telnet in concept, but more focused on +connections between Unix machines. It supported a feature for +passwordless login, based on use of \q{privileged ports} (ports with +numbers below 1024, which Unix traditionally does not allow users +other than \cw{root} to allocate). Ultimately, based on the server +trusting that the client's IP address was owned by the Unix machine it +claimed to be, and that that machine would guard its privileged ports +appropriately. + +Like Telnet, Rlogin runs over an unsecured network connection. + +\H{using-supdup} Connecting using the \i{SUPDUP} protocol + +PuTTY can use the SUPDUP protocol to connect to a server. + +SUPDUP is a login protocol used mainly by PDP-10 and Lisp machines +during the period 1975-1990. Like Telnet and Rlogin, it is unsecured, +so modern systems almost never support it. + +To make a connection of this type, select \q{SUPDUP} from the +\q{Connection type} radio buttons on the \q{Session} panel (see +\k{config-hostname}). For further configuration options (character +set, more processing, scrolling), you can use the \q{SUPDUP} +configuration panel (see \k{config-supdup}). + +In SUPDUP, terminal emulation is more integrated with the network +protocol than in other protocols such as SSH. The SUPDUP protocol can +thus only be used with PuTTY proper, not with the command-line tool +Plink. + +The SUPDUP protocol does not support changing the terminal dimensions, +so this capability is disabled during a SUPDUP session. + +SUPDUP provides no well defined means for one end of the connection to +notify the other that the connection is finished. Therefore, PuTTY in +SUPDUP mode will remain connected until you close the window using the +close button. \H{using-cmdline} The PuTTY command line @@ -590,13 +646,13 @@ window}, or a \i{Windows shortcut}). \S{using-cmdline-session} Starting a session from the command line -\I\c{-ssh}\I\c{-telnet}\I\c{-rlogin}\I\c{-raw}\I\c{-serial}These +\I\c{-ssh}\I\c{-ssh-connection}\I\c{-telnet}\I\c{-rlogin}\I\c{-supdup}\I\c{-raw}\I\c{-serial}These options allow you to bypass the configuration window and launch straight into a session. To start a connection to a server called \c{host}: -\c putty.exe [-ssh | -telnet | -rlogin | -raw] [user@]host +\c putty.exe [-ssh | -ssh-connection | -telnet | -rlogin | -supdup | -raw] [user@]host If this syntax is used, settings are taken from the \i{Default Settings} (see \k{config-saving}); \c{user} overrides these settings if @@ -662,23 +718,31 @@ must be the very first thing on the command line. This form of the option is deprecated.) \S2{using-cmdline-protocol} Selecting a protocol: \c{-ssh}, -\c{-telnet}, \c{-rlogin}, \c{-raw} \c{-serial} +\c{-ssh-connection}, \c{-telnet}, \c{-rlogin}, \c{-supdup}, +\c{-raw}, \c{-serial} To choose which protocol you want to connect with, you can use one of these options: \b \i\c{-ssh} selects the SSH protocol. +\b \i\c{-ssh-connection} selects the bare ssh-connection protocol. +(This is only useful in specialised circumstances; see \k{config-psusan} +for more information.) + \b \i\c{-telnet} selects the Telnet protocol. \b \i\c{-rlogin} selects the Rlogin protocol. +\b \i\c{-supdup} selects the SUPDUP protocol. + \b \i\c{-raw} selects the raw protocol. \b \i\c{-serial} selects a serial connection. -These options are not available in the file transfer tools PSCP and -PSFTP (which only work with the SSH protocol). +Most of these options are not available in the file transfer tools +PSCP and PSFTP (which only work with the SSH protocol and the bare +ssh-connection protocol). These options are equivalent to the \i{protocol selection} buttons in the Session panel of the PuTTY configuration box (see @@ -782,8 +846,9 @@ security. If you possibly can, we recommend you set up public-key authentication instead. See \k{pubkey} for details. Note that the \c{-pw} option only works when you are using the SSH -protocol. Due to fundamental limitations of Telnet and Rlogin, these -protocols do not support automated password authentication. +protocol. Due to fundamental limitations of Telnet, Rlogin, and +SUPDUP, these protocols do not support automated password +authentication. \S2{using-cmdline-agentauth} \i\c{-agent} and \i\c{-noagent}: control use of Pageant for authentication @@ -949,6 +1014,15 @@ This option is equivalent to the \q{Private key file for authentication} box in the Auth panel of the PuTTY configuration box (see \k{config-ssh-privkey}). +\S2{using-cmdline-no-trivial-auth} \i\c{-no-trivial-auth}: disconnect +if SSH authentication succeeds trivially + +This option causes PuTTY to abandon an SSH session if the server +accepts authentication without ever having asked for any kind of +password or signature or token. + +See \k{config-ssh-notrivialauth} for why you might want this. + \S2{using-cmdline-loghost} \i\c{-loghost}: specify a \i{logical host name} @@ -1003,7 +1077,7 @@ For example, \cq{-sercfg 19200,8,n,1,N} denotes a baud rate of 19200, 8 data bits, no parity, 1 stop bit and no flow control. \S2{using-cmdline-sshlog} \i\c{-sessionlog}, \i\c{-sshlog}, -\i\c{-sshrawlog}: specify session logging +\i\c{-sshrawlog}: enable session logging These options cause the PuTTY network tools to write out a \i{log file}. Each of them expects a file name as an argument, e.g. @@ -1019,6 +1093,14 @@ different logging modes, all available from the GUI too: For more information on logging configuration, see \k{config-logging}. +\S2{using-cmdline-logfileexists} \i\c{-logoverwrite}, \i\c{-logappend}: +control behaviour with existing log file + +If logging has been enabled (in the saved configuration, or by another +command-line option), and the specified log file already exists, these +options tell the PuTTY network tools what to do so that they don't +have to ask the user. See \k{config-logfileexists} for details. + \S2{using-cmdline-proxycmd} \i\c{-proxycmd}: specify a local proxy command @@ -1060,7 +1142,7 @@ proxy command, you'll need to arrange to pass them the \c{-restrict-acl} option yourself, if that's what you want.) If Pageant is started with the \c{-restrict-acl} option, and you use -it to launch a PuTTY session from its System Tray submenu, then +it to launch a PuTTY session from its \ii{System Tray} submenu, then Pageant will \e{not} default to starting the PuTTY subprocess with a restricted ACL. This is because PuTTY is more likely to suffer reduced functionality as a result of restricted ACLs (e.g. screen reader diff --git a/doc/vstr.but b/doc/vstr.but index 2cac9d8..f97ad6b 100644 --- a/doc/vstr.but +++ b/doc/vstr.but @@ -1 +1 @@ -\versionid PuTTY release 0.73 +\versionid PuTTY release 0.76 diff --git a/ecc.c b/ecc.c index 82fd14d..72f9bfe 100644 --- a/ecc.c +++ b/ecc.c @@ -486,10 +486,10 @@ static void ecc_weierstrass_normalise(WeierstrassPoint *wp) mp_int *zinv3 = monty_mul(wc->mc, zinv2, zinv); monty_mul_into(wc->mc, wp->X, wp->X, zinv2); monty_mul_into(wc->mc, wp->Y, wp->Y, zinv3); + monty_mul_into(wc->mc, wp->Z, wp->Z, zinv); mp_free(zinv); mp_free(zinv2); mp_free(zinv3); - mp_copy_into(wp->Z, monty_identity(wc->mc)); } void ecc_weierstrass_get_affine( @@ -759,8 +759,8 @@ static void ecc_montgomery_normalise(MontgomeryPoint *mp) MontgomeryCurve *mc = mp->mc; mp_int *zinv = monty_invert(mc->mc, mp->Z); monty_mul_into(mc->mc, mp->X, mp->X, zinv); + monty_mul_into(mc->mc, mp->Z, mp->Z, zinv); mp_free(zinv); - mp_copy_into(mp->Z, monty_identity(mc->mc)); } MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *B, mp_int *n) @@ -833,6 +833,11 @@ void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x) *x = monty_export(mc->mc, mp->X); } +unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp) +{ + return mp_eq_integer(mp->Z, 0); +} + /* ---------------------------------------------------------------------- * Twisted Edwards curves. */ @@ -1083,8 +1088,8 @@ static void ecc_edwards_normalise(EdwardsPoint *ep) mp_int *zinv = monty_invert(ec->mc, ep->Z); monty_mul_into(ec->mc, ep->X, ep->X, zinv); monty_mul_into(ec->mc, ep->Y, ep->Y, zinv); + monty_mul_into(ec->mc, ep->Z, ep->Z, zinv); mp_free(zinv); - mp_copy_into(ep->Z, monty_identity(ec->mc)); monty_mul_into(ec->mc, ep->T, ep->X, ep->Y); } diff --git a/ecc.h b/ecc.h index 13118b2..96eebdf 100644 --- a/ecc.h +++ b/ecc.h @@ -170,6 +170,11 @@ MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *, mp_int *); */ void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x); +/* + * Test whether a point is the curve identity. + */ +unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp); + /* ---------------------------------------------------------------------- * Twisted Edwards curves. * diff --git a/errsock.c b/errsock.c index a01e7ca..6f26629 100644 --- a/errsock.c +++ b/errsock.c @@ -45,17 +45,14 @@ static SocketPeerInfo *sk_error_peer_info(Socket *s) } static const SocketVtable ErrorSocket_sockvt = { - sk_error_plug, - sk_error_close, - NULL /* write */, - NULL /* write_oob */, - NULL /* write_eof */, - NULL /* set_frozen */, - sk_error_socket_error, - sk_error_peer_info, + .plug = sk_error_plug, + .close = sk_error_close, + .socket_error = sk_error_socket_error, + .peer_info = sk_error_peer_info, + /* other methods are NULL */ }; -static Socket *new_error_socket_internal(char *errmsg, Plug *plug) +Socket *new_error_socket_consume_string(Plug *plug, char *errmsg) { ErrorSocket *es = snew(ErrorSocket); es->sock.vt = &ErrorSocket_sockvt; @@ -73,5 +70,5 @@ Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...) msg = dupvprintf(fmt, ap); va_end(ap); - return new_error_socket_internal(msg, plug); + return new_error_socket_consume_string(plug, msg); } diff --git a/windows/cdecode.c b/far2l/cdecode.c similarity index 100% rename from windows/cdecode.c rename to far2l/cdecode.c diff --git a/libb64-1.2/include/b64/cdecode.h b/far2l/cdecode.h similarity index 100% rename from libb64-1.2/include/b64/cdecode.h rename to far2l/cdecode.h diff --git a/windows/cencode.c b/far2l/cencode.c similarity index 100% rename from windows/cencode.c rename to far2l/cencode.c diff --git a/libb64-1.2/include/b64/cencode.h b/far2l/cencode.h similarity index 100% rename from libb64-1.2/include/b64/cencode.h rename to far2l/cencode.h diff --git a/fuzzterm.c b/fuzzterm.c index f108558..f53c0a9 100644 --- a/fuzzterm.c +++ b/fuzzterm.c @@ -2,8 +2,8 @@ #include #include -#define PUTTY_DO_GLOBALS #include "putty.h" +#include "dialog.h" #include "terminal.h" /* For Unix in particular, but harmless if this main() is reused elsewhere */ @@ -88,48 +88,36 @@ static void fuzz_request_resize(TermWin *tw, int w, int h) {} static void fuzz_set_title(TermWin *tw, const char *title) {} static void fuzz_set_icon_title(TermWin *tw, const char *icontitle) {} static void fuzz_set_minimised(TermWin *tw, bool minimised) {} -static bool fuzz_is_minimised(TermWin *tw) { return false; } static void fuzz_set_maximised(TermWin *tw, bool maximised) {} static void fuzz_move(TermWin *tw, int x, int y) {} static void fuzz_set_zorder(TermWin *tw, bool top) {} -static bool fuzz_palette_get(TermWin *tw, int n, int *r, int *g, int *b) -{ return false; } -static void fuzz_palette_set(TermWin *tw, int n, int r, int g, int b) {} -static void fuzz_palette_reset(TermWin *tw) {} -static void fuzz_get_pos(TermWin *tw, int *x, int *y) { *x = *y = 0; } -static void fuzz_get_pixels(TermWin *tw, int *x, int *y) { *x = *y = 0; } -static const char *fuzz_get_title(TermWin *tw, bool icon) { return "moo"; } -static bool fuzz_is_utf8(TermWin *tw) { return true; } +static void fuzz_palette_set(TermWin *tw, unsigned start, unsigned ncolours, + const rgb *colours) {} +static void fuzz_palette_get_overrides(TermWin *tw, Terminal *term) {} static const TermWinVtable fuzz_termwin_vt = { - fuzz_setup_draw_ctx, - fuzz_draw_text, - fuzz_draw_cursor, - fuzz_draw_trust_sigil, - fuzz_char_width, - fuzz_free_draw_ctx, - fuzz_set_cursor_pos, - fuzz_set_raw_mouse_mode, - fuzz_set_scrollbar, - fuzz_bell, - fuzz_clip_write, - fuzz_clip_request_paste, - fuzz_refresh, - fuzz_request_resize, - fuzz_set_title, - fuzz_set_icon_title, - fuzz_set_minimised, - fuzz_is_minimised, - fuzz_set_maximised, - fuzz_move, - fuzz_set_zorder, - fuzz_palette_get, - fuzz_palette_set, - fuzz_palette_reset, - fuzz_get_pos, - fuzz_get_pixels, - fuzz_get_title, - fuzz_is_utf8, + .setup_draw_ctx = fuzz_setup_draw_ctx, + .draw_text = fuzz_draw_text, + .draw_cursor = fuzz_draw_cursor, + .draw_trust_sigil = fuzz_draw_trust_sigil, + .char_width = fuzz_char_width, + .free_draw_ctx = fuzz_free_draw_ctx, + .set_cursor_pos = fuzz_set_cursor_pos, + .set_raw_mouse_mode = fuzz_set_raw_mouse_mode, + .set_scrollbar = fuzz_set_scrollbar, + .bell = fuzz_bell, + .clip_write = fuzz_clip_write, + .clip_request_paste = fuzz_clip_request_paste, + .refresh = fuzz_refresh, + .request_resize = fuzz_request_resize, + .set_title = fuzz_set_title, + .set_icon_title = fuzz_set_icon_title, + .set_minimised = fuzz_set_minimised, + .set_maximised = fuzz_set_maximised, + .move = fuzz_move, + .set_zorder = fuzz_set_zorder, + .palette_set = fuzz_palette_set, + .palette_get_overrides = fuzz_palette_get_overrides, }; void ldisc_send(Ldisc *ldisc, const void *buf, int len, bool interactive) {} @@ -142,39 +130,43 @@ void timer_change_notify(unsigned long next) { } /* needed by config.c and sercfg.c */ -void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton) { } -int dlg_radiobutton_get(union control *ctrl, void *dlg) { return 0; } -void dlg_checkbox_set(union control *ctrl, void *dlg, int checked) { } -int dlg_checkbox_get(union control *ctrl, void *dlg) { return 0; } -void dlg_editbox_set(union control *ctrl, void *dlg, char const *text) { } -char *dlg_editbox_get(union control *ctrl, void *dlg) { return dupstr("moo"); } -void dlg_listbox_clear(union control *ctrl, void *dlg) { } -void dlg_listbox_del(union control *ctrl, void *dlg, int index) { } -void dlg_listbox_add(union control *ctrl, void *dlg, char const *text) { } -void dlg_listbox_addwithid(union control *ctrl, void *dlg, +void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton) { } +int dlg_radiobutton_get(union control *ctrl, dlgparam *dp) { return 0; } +void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked) { } +bool dlg_checkbox_get(union control *ctrl, dlgparam *dp) { return false; } +void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text) { } +char *dlg_editbox_get(union control *ctrl, dlgparam *dp) +{ return dupstr("moo"); } +void dlg_listbox_clear(union control *ctrl, dlgparam *dp) { } +void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index) { } +void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text) { } +void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp, char const *text, int id) { } -int dlg_listbox_getid(union control *ctrl, void *dlg, int index) { return 0; } -int dlg_listbox_index(union control *ctrl, void *dlg) { return -1; } -int dlg_listbox_issel(union control *ctrl, void *dlg, int index) { return 0; } -void dlg_listbox_select(union control *ctrl, void *dlg, int index) { } -void dlg_text_set(union control *ctrl, void *dlg, char const *text) { } -void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn) { } -Filename *dlg_filesel_get(union control *ctrl, void *dlg) { return NULL; } -void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn) { } -FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg) { return NULL; } -void dlg_update_start(union control *ctrl, void *dlg) { } -void dlg_update_done(union control *ctrl, void *dlg) { } -void dlg_set_focus(union control *ctrl, void *dlg) { } -void dlg_label_change(union control *ctrl, void *dlg, char const *text) { } -union control *dlg_last_focused(union control *ctrl, void *dlg) { return NULL; } -void dlg_beep(void *dlg) { } -void dlg_error_msg(void *dlg, const char *msg) { } -void dlg_end(void *dlg, int value) { } -void dlg_coloursel_start(union control *ctrl, void *dlg, +int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index) +{ return 0; } +int dlg_listbox_index(union control *ctrl, dlgparam *dp) { return -1; } +bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index) +{ return false; } +void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index) { } +void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text) { } +void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn) { } +Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp) { return NULL; } +void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fn) { } +FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp) { return NULL; } +void dlg_update_start(union control *ctrl, dlgparam *dp) { } +void dlg_update_done(union control *ctrl, dlgparam *dp) { } +void dlg_set_focus(union control *ctrl, dlgparam *dp) { } +void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text) { } +union control *dlg_last_focused(union control *ctrl, dlgparam *dp) +{ return NULL; } +void dlg_beep(dlgparam *dp) { } +void dlg_error_msg(dlgparam *dp, const char *msg) { } +void dlg_end(dlgparam *dp, int value) { } +void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b) { } -bool dlg_coloursel_results(union control *ctrl, void *dlg, +bool dlg_coloursel_results(union control *ctrl, dlgparam *dp, int *r, int *g, int *b) { return false; } -void dlg_refresh(union control *ctrl, void *dlg) { } +void dlg_refresh(union control *ctrl, dlgparam *dp) { } bool dlg_is_visible(union control *ctrl, dlgparam *dp) { return false; } const char *const appname = "FuZZterm"; diff --git a/icons/macicon.py b/icons/macicon.py index b7fed6b..3b9ff75 100755 --- a/icons/macicon.py +++ b/icons/macicon.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Generate Mac OS X .icns files, or at least the simple subformats # that don't involve JPEG encoding and the like. @@ -10,6 +10,8 @@ import struct import subprocess +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + # The file format has a typical IFF-style (type, length, data) chunk # structure, with one outer chunk containing subchunks for various # different icon sizes and formats. @@ -33,18 +35,18 @@ def make_mono_icon(size, rgba): for index in range(len(rgba))] # Encode in 1-bit big-endian format. - data = "" + data = b'' for i in range(0, len(pixels), 8): byte = 0 for j in range(8): if pixels[i+j] >= 0x80: byte |= 0x80 >> j - data += chr(byte) + data += bytes(byte) # This size-32 chunk id is an anomaly in what would otherwise be a # consistent system of using {s,l,h,t} for {16,32,48,128}-pixel # icon sizes. - chunkid = { 16: "ics#", 32: "ICN#", 48: "ich#" }[size] + chunkid = { 16: b"ics#", 32: b"ICN#", 48: b"ich#" }[size] return make_chunk(chunkid, data) # Mask for full-colour icons: a chunk containing an 8 bpp alpha @@ -52,9 +54,9 @@ def make_mono_icon(size, rgba): def make_colour_mask(size, rgba): assert len(rgba) == size * size - data = "".join(map(lambda pix: chr(pix[3]), rgba)) + data = bytes(map(lambda pix: pix[3], rgba)) - chunkid = { 16: "s8mk", 32: "l8mk", 48: "h8mk", 128: "t8mk" }[size] + chunkid = { 16: b"s8mk", 32: b"l8mk", 48: b"h8mk", 128: b"t8mk" }[size] return make_chunk(chunkid, data) # Helper routine for deciding when to start and stop run-length @@ -69,19 +71,18 @@ def runof3(string, position): def make_colour_icon(size, rgba): assert len(rgba) == size * size - data = "" + data = b"" # Mysterious extra zero header word appearing only in the size-128 # icon chunk. libicns doesn't know what it's for, and neither do # I. if size == 128: - data += "\0\0\0\0" + data += b"\0\0\0\0" # Handle R,G,B channels in sequence. (Ignore the alpha channel; it # goes into the separate mask chunk constructed above.) for chan in range(3): - pixels = "".join([chr(rgba[index][chan]) - for index in range(len(rgba))]) + pixels = bytes([rgba[index][chan] for index in range(len(rgba))]) # Run-length encode each channel using the following format: # * byte 0x80-0xFF followed by one literal byte means repeat @@ -98,15 +99,15 @@ def make_colour_icon(size, rgba): pos < len(pixels) and pixels[pos] == pixval): pos += 1 - data += chr(0x80 + pos-start - 3) + pixval + data += bytes(0x80 + pos-start - 3) + pixval else: while (pos - start < 128 and pos < len(pixels) and not runof3(pixels, pos)): pos += 1 - data += chr(0x00 + pos-start - 1) + pixels[start:pos] + data += bytes(0x00 + pos-start - 1) + pixels[start:pos] - chunkid = { 16: "is32", 32: "il32", 48: "ih32", 128: "it32" }[size] + chunkid = { 16: b"is32", 32: b"il32", 48: b"ih32", 128: b"it32" }[size] return make_chunk(chunkid, data) # Load an image file from disk and turn it into a simple list of @@ -117,10 +118,10 @@ def make_colour_icon(size, rgba): # here that the file is in RGBA .pam format (as mkicon.py will have # generated it). def load_rgba(filename): - with open(filename) as f: - assert f.readline() == "P7\n" + with open(filename, "rb") as f: + assert f.readline() == b"P7\n" for line in iter(f.readline, ''): - words = line.rstrip("\n").split() + words = line.decode("ASCII").rstrip("\n").split() if words[0] == "WIDTH": width = int(words[1]) elif words[0] == "HEIGHT": @@ -135,10 +136,10 @@ def load_rgba(filename): assert width == height data = f.read() assert len(data) == width*height*4 - rgba = [map(ord, data[i:i+4]) for i in range(0, len(data), 4)] + rgba = [list(data[i:i+4]) for i in range(0, len(data), 4)] return width, rgba -data = "" +data = b"" # Trivial argument format: each argument is a filename prefixed with # "mono:", "colour:" or "output:". The first two indicate image files @@ -157,7 +158,7 @@ def load_rgba(filename): else: assert False, "bad argument '%s'" % arg -data = make_chunk("icns", data) +data = make_chunk(b"icns", data) -with open(outfile, "w") as f: +with open(outfile, "wb") as f: f.write(data) diff --git a/icons/mkicon.py b/icons/mkicon.py index d7d474a..c50082d 100755 --- a/icons/mkicon.py +++ b/icons/mkicon.py @@ -2,9 +2,12 @@ from __future__ import division +import sys import decimal import math +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + # Python code which draws the PuTTY icon components at a range of # sizes. diff --git a/import.c b/import.c index effc65b..553fa75 100644 --- a/import.c +++ b/import.c @@ -13,12 +13,12 @@ #include "mpint.h" #include "misc.h" -static bool openssh_pem_encrypted(const Filename *file); -static bool openssh_new_encrypted(const Filename *file); +static bool openssh_pem_encrypted(BinarySource *src); +static bool openssh_new_encrypted(BinarySource *src); static ssh2_userkey *openssh_pem_read( - const Filename *file, const char *passphrase, const char **errmsg_p); + BinarySource *src, const char *passphrase, const char **errmsg_p); static ssh2_userkey *openssh_new_read( - const Filename *file, const char *passphrase, const char **errmsg_p); + BinarySource *src, const char *passphrase, const char **errmsg_p); static bool openssh_auto_write( const Filename *file, ssh2_userkey *key, const char *passphrase); static bool openssh_pem_write( @@ -26,9 +26,9 @@ static bool openssh_pem_write( static bool openssh_new_write( const Filename *file, ssh2_userkey *key, const char *passphrase); -static bool sshcom_encrypted(const Filename *file, char **comment); +static bool sshcom_encrypted(BinarySource *src, char **comment); static ssh2_userkey *sshcom_read( - const Filename *file, const char *passphrase, const char **errmsg_p); + BinarySource *src, const char *passphrase, const char **errmsg_p); static bool sshcom_write( const Filename *file, ssh2_userkey *key, const char *passphrase); @@ -59,50 +59,97 @@ int import_target_type(int type) return SSH_KEYTYPE_SSH2; } +static inline char *bsgetline(BinarySource *src) +{ + ptrlen line = get_chomped_line(src); + if (get_err(src)) + return NULL; + return mkstr(line); +} + /* * Determine whether a foreign key is encrypted. */ -bool import_encrypted(const Filename *filename, int type, char **comment) +bool import_encrypted_s(const Filename *filename, BinarySource *src, + int type, char **comment) { if (type == SSH_KEYTYPE_OPENSSH_PEM) { /* OpenSSH PEM format doesn't contain a key comment at all */ *comment = dupstr(filename_to_str(filename)); - return openssh_pem_encrypted(filename); + return openssh_pem_encrypted(src); } else if (type == SSH_KEYTYPE_OPENSSH_NEW) { /* OpenSSH new format does, but it's inside the encrypted * section for some reason */ *comment = dupstr(filename_to_str(filename)); - return openssh_new_encrypted(filename); + return openssh_new_encrypted(src); } else if (type == SSH_KEYTYPE_SSHCOM) { - return sshcom_encrypted(filename, comment); + return sshcom_encrypted(src, comment); } return false; } +bool import_encrypted(const Filename *filename, int type, char **comment) +{ + LoadedFile *lf = lf_load_keyfile(filename, NULL); + if (!lf) + return false; /* couldn't even open the file */ + + bool toret = import_encrypted_s(filename, BinarySource_UPCAST(lf), + type, comment); + lf_free(lf); + return toret; +} + /* * Import an SSH-1 key. */ +int import_ssh1_s(BinarySource *src, int type, + RSAKey *key, char *passphrase, const char **errmsg_p) +{ + return 0; +} + int import_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase, const char **errmsg_p) { - return 0; + LoadedFile *lf = lf_load_keyfile(filename, errmsg_p); + if (!lf) + return false; + + int toret = import_ssh1_s(BinarySource_UPCAST(lf), + type, key, passphrase, errmsg_p); + lf_free(lf); + return toret; } /* * Import an SSH-2 key. */ -ssh2_userkey *import_ssh2(const Filename *filename, int type, - char *passphrase, const char **errmsg_p) +ssh2_userkey *import_ssh2_s(BinarySource *src, int type, + char *passphrase, const char **errmsg_p) { if (type == SSH_KEYTYPE_OPENSSH_PEM) - return openssh_pem_read(filename, passphrase, errmsg_p); + return openssh_pem_read(src, passphrase, errmsg_p); else if (type == SSH_KEYTYPE_OPENSSH_NEW) - return openssh_new_read(filename, passphrase, errmsg_p); + return openssh_new_read(src, passphrase, errmsg_p); if (type == SSH_KEYTYPE_SSHCOM) - return sshcom_read(filename, passphrase, errmsg_p); + return sshcom_read(src, passphrase, errmsg_p); return NULL; } +ssh2_userkey *import_ssh2(const Filename *filename, int type, + char *passphrase, const char **errmsg_p) +{ + LoadedFile *lf = lf_load_keyfile(filename, errmsg_p); + if (!lf) + return false; + + ssh2_userkey *toret = import_ssh2_s(BinarySource_UPCAST(lf), + type, passphrase, errmsg_p); + lf_free(lf); + return toret; +} + /* * Export an SSH-1 key. */ @@ -300,11 +347,10 @@ void BinarySink_put_mp_ssh2_from_string(BinarySink *bs, ptrlen str) #define put_mp_ssh2_from_string(bs, str) \ BinarySink_put_mp_ssh2_from_string(BinarySink_UPCAST(bs), str) -static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename, +static struct openssh_pem_key *load_openssh_pem_key(BinarySource *src, const char **errmsg_p) { struct openssh_pem_key *ret; - FILE *fp = NULL; char *line = NULL; const char *errmsg; char *p; @@ -315,17 +361,10 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename, ret = snew(struct openssh_pem_key); ret->keyblob = strbuf_new_nm(); - fp = f_open(filename, "r", false); - if (!fp) { - errmsg = "unable to open key file"; - goto error; - } - - if (!(line = fgetline(fp))) { + if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line); if (!strstartswith(line, "-----BEGIN ") || !strendswith(line, "PRIVATE KEY-----")) { errmsg = "file does not begin with OpenSSH key header"; @@ -359,11 +398,10 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename, headers_done = false; while (1) { - if (!(line = fgetline(fp))) { + if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line); if (strstartswith(line, "-----END ") && strendswith(line, "PRIVATE KEY-----")) { sfree(line); @@ -445,9 +483,6 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename, line = NULL; } - fclose(fp); - fp = NULL; - if (!ret->keyblob || ret->keyblob->len == 0) { errmsg = "key body not present"; goto error; @@ -477,13 +512,12 @@ static struct openssh_pem_key *load_openssh_pem_key(const Filename *filename, sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; - if (fp) fclose(fp); return NULL; } -static bool openssh_pem_encrypted(const Filename *filename) +static bool openssh_pem_encrypted(BinarySource *src) { - struct openssh_pem_key *key = load_openssh_pem_key(filename, NULL); + struct openssh_pem_key *key = load_openssh_pem_key(src, NULL); bool ret; if (!key) @@ -516,9 +550,9 @@ static void openssh_pem_derivekey( h = ssh_hash_new(&ssh_md5); put_datapl(h, passphrase); put_data(h, iv, 8); - ssh_hash_final(h, keybuf); + ssh_hash_digest(h, keybuf); - h = ssh_hash_new(&ssh_md5); + ssh_hash_reset(h); put_data(h, keybuf, 16); put_datapl(h, passphrase); put_data(h, iv, 8); @@ -526,9 +560,9 @@ static void openssh_pem_derivekey( } static ssh2_userkey *openssh_pem_read( - const Filename *filename, const char *passphrase, const char **errmsg_p) + BinarySource *filesrc, const char *passphrase, const char **errmsg_p) { - struct openssh_pem_key *key = load_openssh_pem_key(filename, errmsg_p); + struct openssh_pem_key *key = load_openssh_pem_key(filesrc, errmsg_p); ssh2_userkey *retkey; const ssh_keyalg *alg; BinarySource src[1]; @@ -538,8 +572,10 @@ static ssh2_userkey *openssh_pem_read( strbuf *blob = strbuf_new_nm(); int privptr = 0, publen; - if (!key) + if (!key) { + strbuf_free(blob); return NULL; + } if (key->encrypted) { unsigned char keybuf[32]; @@ -1087,11 +1123,10 @@ struct openssh_new_key { strbuf *keyblob; }; -static struct openssh_new_key *load_openssh_new_key(const Filename *filename, +static struct openssh_new_key *load_openssh_new_key(BinarySource *filesrc, const char **errmsg_p) { struct openssh_new_key *ret; - FILE *fp = NULL; char *line = NULL; const char *errmsg; char *p; @@ -1104,17 +1139,10 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename, ret = snew(struct openssh_new_key); ret->keyblob = strbuf_new_nm(); - fp = f_open(filename, "r", false); - if (!fp) { - errmsg = "unable to open key file"; - goto error; - } - - if (!(line = fgetline(fp))) { + if (!(line = bsgetline(filesrc))) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line); if (0 != strcmp(line, "-----BEGIN OPENSSH PRIVATE KEY-----")) { errmsg = "file does not begin with OpenSSH new-style key header"; goto error; @@ -1124,11 +1152,10 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename, line = NULL; while (1) { - if (!(line = fgetline(fp))) { + if (!(line = bsgetline(filesrc))) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line); if (0 == strcmp(line, "-----END OPENSSH PRIVATE KEY-----")) { sfree(line); line = NULL; @@ -1163,9 +1190,6 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename, line = NULL; } - fclose(fp); - fp = NULL; - if (ret->keyblob->len == 0) { errmsg = "key body not present"; goto error; @@ -1213,20 +1237,19 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename, goto error; } break; - case ON_K_BCRYPT: - { - BinarySource opts[1]; + case ON_K_BCRYPT: { + BinarySource opts[1]; - BinarySource_BARE_INIT_PL(opts, str); - ret->kdfopts.bcrypt.salt = get_string(opts); - ret->kdfopts.bcrypt.rounds = get_uint32(opts); + BinarySource_BARE_INIT_PL(opts, str); + ret->kdfopts.bcrypt.salt = get_string(opts); + ret->kdfopts.bcrypt.rounds = get_uint32(opts); - if (get_err(opts)) { - errmsg = "failed to parse bcrypt options string"; - goto error; - } + if (get_err(opts)) { + errmsg = "failed to parse bcrypt options string"; + goto error; } break; + } } /* @@ -1284,13 +1307,12 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename, sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; - if (fp) fclose(fp); return NULL; } -static bool openssh_new_encrypted(const Filename *filename) +static bool openssh_new_encrypted(BinarySource *src) { - struct openssh_new_key *key = load_openssh_new_key(filename, NULL); + struct openssh_new_key *key = load_openssh_new_key(src, NULL); bool ret; if (!key) @@ -1303,9 +1325,9 @@ static bool openssh_new_encrypted(const Filename *filename) } static ssh2_userkey *openssh_new_read( - const Filename *filename, const char *passphrase, const char **errmsg_p) + BinarySource *filesrc, const char *passphrase, const char **errmsg_p) { - struct openssh_new_key *key = load_openssh_new_key(filename, errmsg_p); + struct openssh_new_key *key = load_openssh_new_key(filesrc, errmsg_p); ssh2_userkey *retkey = NULL; ssh2_userkey *retval = NULL; const char *errmsg; @@ -1703,11 +1725,10 @@ struct sshcom_key { strbuf *keyblob; }; -static struct sshcom_key *load_sshcom_key(const Filename *filename, +static struct sshcom_key *load_sshcom_key(BinarySource *src, const char **errmsg_p) { struct sshcom_key *ret; - FILE *fp; char *line = NULL; int hdrstart, len; const char *errmsg; @@ -1720,16 +1741,10 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, ret->comment[0] = '\0'; ret->keyblob = strbuf_new_nm(); - fp = f_open(filename, "r", false); - if (!fp) { - errmsg = "unable to open key file"; - goto error; - } - if (!(line = fgetline(fp))) { + if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line); if (0 != strcmp(line, "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----")) { errmsg = "file does not begin with ssh.com key header"; goto error; @@ -1740,11 +1755,10 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, headers_done = false; while (1) { - if (!(line = fgetline(fp))) { + if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line); if (!strcmp(line, "---- END SSH2 ENCRYPTED PRIVATE KEY ----")) { sfree(line); line = NULL; @@ -1769,12 +1783,11 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, char *line2; int line2len; - line2 = fgetline(fp); + line2 = bsgetline(src); if (!line2) { errmsg = "unexpected end of file"; goto error; } - strip_crlf(line2); line2len = strlen(line2); line = sresize(line, len + line2len + 1, char); @@ -1787,7 +1800,6 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, line2 = NULL; } p = line + hdrstart; - strip_crlf(p); if (!strcmp(line, "Comment")) { /* Strip quotes in comment if present. */ if (p[0] == '"' && p[strlen(p)-1] == '"') { @@ -1831,14 +1843,10 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, goto error; } - fclose(fp); if (errmsg_p) *errmsg_p = NULL; return ret; error: - if (fp) - fclose(fp); - if (line) { smemclr(line, strlen(line)); sfree(line); @@ -1853,9 +1861,9 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename, return NULL; } -static bool sshcom_encrypted(const Filename *filename, char **comment) +static bool sshcom_encrypted(BinarySource *filesrc, char **comment) { - struct sshcom_key *key = load_sshcom_key(filename, NULL); + struct sshcom_key *key = load_sshcom_key(filesrc, NULL); BinarySource src[1]; ptrlen str; bool answer = false; @@ -1932,15 +1940,15 @@ static void sshcom_derivekey(ptrlen passphrase, uint8_t *keybuf) h = ssh_hash_new(&ssh_md5); put_datapl(h, passphrase); - ssh_hash_final(ssh_hash_copy(h), keybuf); + ssh_hash_digest_nondestructive(h, keybuf); put_data(h, keybuf, 16); ssh_hash_final(h, keybuf + 16); } static ssh2_userkey *sshcom_read( - const Filename *filename, const char *passphrase, const char **errmsg_p) + BinarySource *filesrc, const char *passphrase, const char **errmsg_p) { - struct sshcom_key *key = load_sshcom_key(filename, errmsg_p); + struct sshcom_key *key = load_sshcom_key(filesrc, errmsg_p); const char *errmsg; BinarySource src[1]; ptrlen str, ciphertext; diff --git a/install-sh b/install-sh index 59990a1..8175c64 100755 --- a/install-sh +++ b/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2014-09-12.12; # UTC +scriptversion=2018-03-11.20; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -271,15 +271,18 @@ do fi dst=$dst_arg - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. + # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst - dst=$dstdir/`basename "$src"` + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac dstdir_status=0 else dstdir=`dirname "$dst"` @@ -288,6 +291,11 @@ do fi fi + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + obsolete_mkdir_used=false if test $dstdir_status != 0; then @@ -324,14 +332,16 @@ do # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) - # $RANDOM is not portable (e.g. dash); use it when possible to - # lower collision chance + # Note that $RANDOM variable is not portable (e.g. dash); Use it + # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 - # As "mkdir -p" follows symlinks and we work in /tmp possibly; so - # create the $tmpdir first (and fail if unsuccessful) to make sure - # that nobody tries to guess the $tmpdir name. + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p' feature. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 @@ -434,8 +444,8 @@ do else # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 @@ -500,9 +510,9 @@ do done # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/ldisc.c b/ldisc.c index 8eb8017..f097c04 100644 --- a/ldisc.c +++ b/ldisc.c @@ -130,7 +130,6 @@ void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive) int keyflag = 0; assert(ldisc->term); - assert(len); if (interactive) { /* diff --git a/libb64-1.2/AUTHORS b/libb64-1.2/AUTHORS deleted file mode 100644 index af68737..0000000 --- a/libb64-1.2/AUTHORS +++ /dev/null @@ -1,7 +0,0 @@ -libb64: Base64 Encoding/Decoding Routines -====================================== - -Authors: -------- - -Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com diff --git a/libb64-1.2/BENCHMARKS b/libb64-1.2/BENCHMARKS deleted file mode 100644 index fd34056..0000000 --- a/libb64-1.2/BENCHMARKS +++ /dev/null @@ -1,85 +0,0 @@ --- Intro - -Some people have expressed opinions about how -fast libb64's encoding and decoding routines -are, as compared to some other BASE64 packages -out there. - -This document shows the result of a short and sweet -benchmark, which takes a large-ish file and -encodes/decodes it a number of times. -The winner is the executable that does this task the quickest. - --- Platform - -The tests were all run on a Fujitsu-Siemens laptop, -with a Pentium M processor running at 2GHz, with -1GB of RAM, running Ubuntu 10.4. - --- Packages - -The following BASE64 packages were used in this benchmark: - -- libb64-1.2 (libb64-base64) - From libb64.sourceforge.net - Size of executable: 18808 bytes - Compiled with: - CFLAGS += -O3 - BUFFERSIZE = 16777216 - -- base64-1.5 (fourmilab-base64) - From http://www.fourmilab.ch/webtools/base64/ - Size of executable: 20261 bytes - Compiled with Default package settings - -- coreutils 7.4-2ubuntu2 (coreutils-base64) - From http://www.gnu.org/software/coreutils/ - Size of executable: 38488 bytes - Default binary distributed with Ubuntu 10.4 - --- Input File - -Using blender-2.49b-linux-glibc236-py25-i386.tar.bz2 -from http://www.blender.org/download/get-blender/ -Size: 18285329 bytes -(approx. 18MB) - --- Method - -Encode and Decode the Input file 50 times in a loop, -using a simple shell script, and get the running time. - --- Results - -$ time ./benchmark-libb64.sh -real 0m28.389s -user 0m14.077s -sys 0m12.309s - -$ time ./benchmark-fourmilab.sh -real 1m43.160s -user 1m23.769s -sys 0m8.737s - -$ time ./benchmark-coreutils.sh -real 0m36.288s -user 0m24.746s -sys 0m8.181s - -28.389 for 18MB * 50 -= 28.389 for 900 - --- Conclusion - -libb64 is the fastest encoder/decoder, and -has the smallest executable size. - -On average it will encode and decode at roughly 31.7MB/second. - -The closest "competitor" is base64 from GNU coreutils, which -reaches only 24.8MB/second. - --- -14/06/2010 -chris.venter@gmail.com - diff --git a/libb64-1.2/CHANGELOG b/libb64-1.2/CHANGELOG deleted file mode 100644 index 6fd1c17..0000000 --- a/libb64-1.2/CHANGELOG +++ /dev/null @@ -1,25 +0,0 @@ -libb64: Base64 Encoding/Decoding Routines -====================================== - -## Changelog ## - -Version 1.2 Release -------------------- -Removed the b64dec, b64enc, encoder and decoder programs in favour of -a better example, called base64, which encodes and decodes -depending on its arguments. - -Created a solution for Microsoft Visual Studio C++ Express 2010 -edition, which simply builds the base64 example as a console application. - -Version 1.1 Release -------------------- -Modified encode.h to (correctly) read from the iostream argument, -instead of std::cin. -Thanks to Peter K. Lee for the heads-up. - -No API changes. - -Version 1.0 Release -------------------- -The current content is the changeset. diff --git a/libb64-1.2/INSTALL b/libb64-1.2/INSTALL deleted file mode 100644 index b05f5db..0000000 --- a/libb64-1.2/INSTALL +++ /dev/null @@ -1,44 +0,0 @@ -libb64: Base64 Encoding/Decoding Routines -====================================== - -Requirements: ------------- -This piece of software has minimal requirements. - -I have tested it on the following systems: - -- a Linux machine, with the following specs: -(this was the original development machine) - * FedoraCore 4 - * kernel v. 2.6.11 (stock FC4 kernel) - * gcc version 4.0.1 20050727 (Red Hat 4.0.1-5) - * glibc-2.3.5-10 - * make v. 3.80 - * some arb version of makedepend - -- Windows XP machine - * MSYS 1.0 - * MinGW 5.1.4 - * gcc version 3.4.5 (mingw-vista special r3) - -- Windows XP machine (same as above) - * Microsoft Visual Studio 2010, Version 10.0.30319.1 RTMRel - -Barring any serious screwups on my part, this code should compile and run sweetly -under Cygwin and other systems too. If you DO get it running under some weird arch/os setup, -send me a mail, please. - -Compiling: ---------- -There is no configure. It would be overkill for something so simple... -Run make in the root directory. - -Installing: ----------- -Since the current targets are a standalone executable and a static library -(fancy name for archive) with some headers, an install script has not been implemented yet. -Simply copy the executable into your path, and use it. - --- -peace out -Chris diff --git a/libb64-1.2/LICENSE b/libb64-1.2/LICENSE deleted file mode 100644 index a6b5606..0000000 --- a/libb64-1.2/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright-Only Dedication (based on United States law) -or Public Domain Certification - -The person or persons who have associated work with this document (the -"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of -his knowledge, the work of authorship identified is in the public domain of the -country from which the work is published, or (b) hereby dedicates whatever -copyright the dedicators holds in the work of authorship identified below (the -"Work") to the public domain. A certifier, moreover, dedicates any copyright -interest he may have in the associated work, and for these purposes, is -described as a "dedicator" below. - -A certifier has taken reasonable steps to verify the copyright status of this -work. Certifier recognizes that his good faith efforts may not shield him from -liability if in fact the work certified is not in the public domain. - -Dedicator makes this dedication for the benefit of the public at large and to -the detriment of the Dedicator's heirs and successors. Dedicator intends this -dedication to be an overt act of relinquishment in perpetuity of all present -and future rights under copyright law, whether vested or contingent, in the -Work. Dedicator understands that such relinquishment of all rights includes -the relinquishment of all rights to enforce (by lawsuit or otherwise) those -copyrights in the Work. - -Dedicator recognizes that, once placed in the public domain, the Work may be -freely reproduced, distributed, transmitted, used, modified, built upon, or -otherwise exploited by anyone for any purpose, commercial or non-commercial, -and in any way, including by methods that have not yet been invented or -conceived. \ No newline at end of file diff --git a/libb64-1.2/Makefile b/libb64-1.2/Makefile deleted file mode 100644 index fc41310..0000000 --- a/libb64-1.2/Makefile +++ /dev/null @@ -1,25 +0,0 @@ -all: all_src all_base64 - -all_src: - $(MAKE) -C src -all_base64: all_src - $(MAKE) -C base64 - -clean: clean_src clean_base64 clean_include - rm -f *~ *.bak - -clean_include: - rm -f include/b64/*~ - -clean_src: - $(MAKE) -C src clean; -clean_base64: - $(MAKE) -C base64 clean; - -distclean: clean distclean_src distclean_base64 - -distclean_src: - $(MAKE) -C src distclean; -distclean_base64: - $(MAKE) -C base64 distclean; - diff --git a/libb64-1.2/README b/libb64-1.2/README deleted file mode 100644 index be93b7b..0000000 --- a/libb64-1.2/README +++ /dev/null @@ -1,138 +0,0 @@ -b64: Base64 Encoding/Decoding Routines -====================================== - -Overview: --------- -libb64 is a library of ANSI C routines for fast encoding/decoding data into and -from a base64-encoded format. C++ wrappers are included, as well as the source -code for standalone encoding and decoding executables. - -base64 consists of ASCII text, and is therefore a useful encoding for storing -binary data in a text file, such as xml, or sending binary data over text-only -email. - -References: ----------- -* Wikipedia article: - http://en.wikipedia.org/wiki/Base64 -* base64, another implementation of a commandline en/decoder: - http://www.fourmilab.ch/webtools/base64/ - -Why? ---- -I did this because I need an implementation of base64 encoding and decoding, -without any licensing problems. Most OS implementations are released under -either the GNU/GPL, or a BSD-variant, which is not what I require. - -Also, the chance to actually use the co-routine implementation in code is rare, -and its use here is fitting. I couldn't pass up the chance. -For more information on this technique, see "Coroutines in C", by Simon Tatham, -which can be found online here: -http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html - -So then, under which license do I release this code? On to the next section... - -License: -------- -This work is released under into the Public Domain. -It basically boils down to this: I put this work in the public domain, and you -can take it and do whatever you want with it. - -An example of this "license" is the Creative Commons Public Domain License, a -copy of which can be found in the LICENSE file, and also online at -http://creativecommons.org/licenses/publicdomain/ - -Commandline Use: ---------------- -There is a new executable available, it is simply called base64. -It can encode and decode files, as instructed by the user. - -To encode a file: -$ ./base64 -e filea fileb -fileb will now be the base64-encoded version of filea. - -To decode a file: -$ ./base64 -d fileb filec -filec will now be identical to filea. - -Programming: ------------ -Some C++ wrappers are provided as well, so you don't have to get your hands -dirty. Encoding from standard input to standard output is as simple as - - #include - #include - int main() - { - base64::encoder E; - E.encode(std::cin, std::cout); - return 0; - } - -Both standalone executables and a static library is provided in the package, - -Implementation: --------------- -It is DAMN fast, if I may say so myself. The C code uses a little trick which -has been used to implement coroutines, of which one can say that this -implementation is an example. - -(To see how the libb64 codebase compares with some other BASE64 implementations -available, see the BENCHMARKS file) - -The trick involves the fact that a switch-statement may legally cross into -sub-blocks. A very thorough and enlightening essay on co-routines in C, using -this method, can be found in the above mentioned "Coroutines in C", by Simon -Tatham: http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html - -For example, an RLE decompressing routine, adapted from the article: -1 static int STATE = 0; -2 static int len, c; -3 switch (STATE) -4 { -5 while (1) -6 { -7 c = getchar(); -8 if (c == EOF) return EOF; -9 if (c == 0xFF) { -10 len = getchar(); -11 c = getchar(); -12 while (len--) -13 { -14 STATE = 0; -15 return c; -16 case 0: -17 } -18 } else -19 STATE = 1; -20 return c; -21 case 1: -22 } -23 } -24 } - -As can be seen from this example, a coroutine depends on a state variable, -which it sets directly before exiting (lines 14 and 119). The next time the -routine is entered, the switch moves control to the specific point directly -after the previous exit (lines 16 and 21).hands - -(As an aside, in the mentioned article the combination of the top-level switch, -the various setting of the state, the return of a value, and the labelling of -the exit point is wrapped in #define macros, making the structure of the -routine even clearer.) - -The obvious problem with any such routine is the static keyword. -Any static variables in a function spell doom for multithreaded applications. -Also, in situations where this coroutine is used by more than one other -coroutines, the consistency is disturbed. - -What is needed is a structure for storing these variabled, which is passed to -the routine seperately. This obviously breaks the modularity of the function, -since now the caller has to worry about and care for the internal state of the -routine (the callee). This allows for a fast, multithreading-enabled -implementation, which may (obviously) be wrapped in a C++ object for ease of -use. - -The base64 encoding and decoding functionality in this package is implemented -in exactly this way, providing both a high-speed high-maintanence C interface, -and a wrapped C++ which is low-maintanence and only slightly less performant. diff --git a/libb64-1.2/TODO b/libb64-1.2/TODO deleted file mode 100644 index e69de29..0000000 diff --git a/libb64-1.2/base64/Makefile b/libb64-1.2/base64/Makefile deleted file mode 100644 index 30a2c5c..0000000 --- a/libb64-1.2/base64/Makefile +++ /dev/null @@ -1,56 +0,0 @@ -BINARIES = base64 - -# Build flags (uncomment one) -############################# -# Release build flags -CFLAGS += -O3 -############################# -# Debug build flags -#CFLAGS += -g -############################# - -# select a buffersize -# a larger size should be faster, but takes more runtime memory -#BUFFERSIZE = 4096 -#BUFFERSIZE = 65536 -BUFFERSIZE = 16777216 - -SOURCES = base64.cc - -TARGETS = $(BINARIES) - -LINK.o = g++ - -CFLAGS += -Werror -pedantic -CFLAGS += -DBUFFERSIZE=$(BUFFERSIZE) -CFLAGS += -I../include - -CXXFLAGS += $(CFLAGS) - -vpath %.h ../include/b64 -vpath %.a ../src - -.PHONY : clean - -all: $(TARGETS) #strip - -base64: libb64.a - -strip: - strip $(BINARIES) *.exe - -clean: clean_VisualStudioProject - rm -f *.exe* *.o $(TARGETS) *.bak *~ -clean_VisualStudioProject: - $(MAKE) -C VisualStudioProject clean - -distclean: clean distclean_VisualStudioProject - rm -f depend -distclean_VisualStudioProject: clean_VisualStudioProject - $(MAKE) -C VisualStudioProject distclean - -depend: $(SOURCES) - makedepend -f- $(CFLAGS) $(SOURCES) 2> /dev/null 1> depend - --include depend - diff --git a/libb64-1.2/base64/VisualStudioProject/Makefile b/libb64-1.2/base64/VisualStudioProject/Makefile deleted file mode 100644 index 047fcb9..0000000 --- a/libb64-1.2/base64/VisualStudioProject/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -DEBRIS = base64.sdf base64.suo base64.vcxproj.user - -all: - -clean: - rm -rf Debug Release - -distclean: clean - rm -f $(DEBRIS) - - \ No newline at end of file diff --git a/libb64-1.2/base64/VisualStudioProject/base64.sln b/libb64-1.2/base64/VisualStudioProject/base64.sln deleted file mode 100644 index b357a9c..0000000 --- a/libb64-1.2/base64/VisualStudioProject/base64.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C++ Express 2010 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base64", "base64.vcxproj", "{0B094121-DC64-4D74-AFA0-750B83F800D0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0B094121-DC64-4D74-AFA0-750B83F800D0}.Debug|Win32.ActiveCfg = Debug|Win32 - {0B094121-DC64-4D74-AFA0-750B83F800D0}.Debug|Win32.Build.0 = Debug|Win32 - {0B094121-DC64-4D74-AFA0-750B83F800D0}.Release|Win32.ActiveCfg = Release|Win32 - {0B094121-DC64-4D74-AFA0-750B83F800D0}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/libb64-1.2/base64/VisualStudioProject/base64.vcxproj b/libb64-1.2/base64/VisualStudioProject/base64.vcxproj deleted file mode 100644 index 530aec4..0000000 --- a/libb64-1.2/base64/VisualStudioProject/base64.vcxproj +++ /dev/null @@ -1,92 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {0B094121-DC64-4D74-AFA0-750B83F800D0} - Win32Proj - base64 - - - - Application - true - Unicode - - - Application - false - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - BUFFERSIZE=16777216;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - H:\builds\libb64\working.libb64\include;%(AdditionalIncludeDirectories) - - - Console - true - - - - - Level3 - - - MaxSpeed - true - true - BUFFERSIZE=16777216;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - H:\builds\libb64\working.libb64\include;%(AdditionalIncludeDirectories) - - - Console - true - true - true - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/libb64-1.2/base64/VisualStudioProject/base64.vcxproj.filters b/libb64-1.2/base64/VisualStudioProject/base64.vcxproj.filters deleted file mode 100644 index 34a1105..0000000 --- a/libb64-1.2/base64/VisualStudioProject/base64.vcxproj.filters +++ /dev/null @@ -1,36 +0,0 @@ - - - - - {12b9f2a2-b899-409a-a507-8cefe9c39b25} - - - {ce5598bd-67f3-430f-890b-cefa880e9405} - - - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/libb64-1.2/base64/base64.cc b/libb64-1.2/base64/base64.cc deleted file mode 100644 index acab416..0000000 --- a/libb64-1.2/base64/base64.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* -base64.cc - c++ source to a base64 reference encoder and decoder - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#include -#include - -#include -#include -#include - -#include - -// Function which prints the usage of this executable -void usage() -{ - std::cerr<< \ - "base64: Encodes and Decodes files using base64\n" \ - "Usage: base64 [-e|-d] [input] [output]\n" \ - " Where [-e] will encode the input file into the output file,\n" \ - " [-d] will decode the input file into the output file, and\n" \ - " [input] and [output] are the input and output files, respectively.\n"; -} -// Function which prints the usage of this executable, plus a short message -void usage(const std::string& message) -{ - usage(); - std::cerr<<"Incorrect invocation of base64:\n"; - std::cerr< - -namespace base64 -{ - extern "C" - { - #include "cdecode.h" - } - - struct decoder - { - base64_decodestate _state; - int _buffersize; - - decoder(int buffersize_in = BUFFERSIZE) - : _buffersize(buffersize_in) - {} - - int decode(char value_in) - { - return base64_decode_value(value_in); - } - - int decode(const char* code_in, const int length_in, char* plaintext_out) - { - return base64_decode_block(code_in, length_in, plaintext_out, &_state); - } - - void decode(std::istream& istream_in, std::ostream& ostream_in) - { - base64_init_decodestate(&_state); - // - const int N = _buffersize; - char* code = new char[N]; - char* plaintext = new char[N]; - int codelength; - int plainlength; - - do - { - istream_in.read((char*)code, N); - codelength = istream_in.gcount(); - plainlength = decode(code, codelength, plaintext); - ostream_in.write((const char*)plaintext, plainlength); - } - while (istream_in.good() && codelength > 0); - // - base64_init_decodestate(&_state); - - delete [] code; - delete [] plaintext; - } - }; - -} // namespace base64 - - - -#endif // BASE64_DECODE_H - diff --git a/libb64-1.2/include/b64/encode.h b/libb64-1.2/include/b64/encode.h deleted file mode 100644 index 5d807d9..0000000 --- a/libb64-1.2/include/b64/encode.h +++ /dev/null @@ -1,77 +0,0 @@ -// :mode=c++: -/* -encode.h - c++ wrapper for a base64 encoding algorithm - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ -#ifndef BASE64_ENCODE_H -#define BASE64_ENCODE_H - -#include - -namespace base64 -{ - extern "C" - { - #include "cencode.h" - } - - struct encoder - { - base64_encodestate _state; - int _buffersize; - - encoder(int buffersize_in = BUFFERSIZE) - : _buffersize(buffersize_in) - {} - - int encode(char value_in) - { - return base64_encode_value(value_in); - } - - int encode(const char* code_in, const int length_in, char* plaintext_out) - { - return base64_encode_block(code_in, length_in, plaintext_out, &_state); - } - - int encode_end(char* plaintext_out) - { - return base64_encode_blockend(plaintext_out, &_state); - } - - void encode(std::istream& istream_in, std::ostream& ostream_in) - { - base64_init_encodestate(&_state); - // - const int N = _buffersize; - char* plaintext = new char[N]; - char* code = new char[2*N]; - int plainlength; - int codelength; - - do - { - istream_in.read(plaintext, N); - plainlength = istream_in.gcount(); - // - codelength = encode(plaintext, plainlength, code); - ostream_in.write(code, codelength); - } - while (istream_in.good() && plainlength > 0); - - codelength = encode_end(code); - ostream_in.write(code, codelength); - // - base64_init_encodestate(&_state); - - delete [] code; - delete [] plaintext; - } - }; - -} // namespace base64 - -#endif // BASE64_ENCODE_H - diff --git a/libb64-1.2/src/Makefile b/libb64-1.2/src/Makefile deleted file mode 100644 index 28b2382..0000000 --- a/libb64-1.2/src/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -LIBRARIES = libb64.a - -# Build flags (uncomment one) -############################# -# Release build flags -CFLAGS += -O3 -############################# -# Debug build flags -#CFLAGS += -g -############################# - -SOURCES = cdecode.c cencode.c - -TARGETS = $(LIBRARIES) - -LINK.o = gcc - -CFLAGS += -Werror -pedantic -CFLAGS += -I../include - -vpath %.h ../include/b64 - -.PHONY : clean - -all: $(TARGETS) #strip - -libb64.a: cencode.o cdecode.o - $(AR) $(ARFLAGS) $@ $^ - -strip: - strip $(BINARIES) *.exe - -clean: - rm -f *.exe* *.o $(TARGETS) *.bak *~ - -distclean: clean - rm -f depend - -depend: $(SOURCES) - makedepend -f- $(CFLAGS) $(SOURCES) 2> /dev/null 1> depend - --include depend - diff --git a/libb64-1.2/src/cdecode.c b/libb64-1.2/src/cdecode.c deleted file mode 100644 index 34509c9..0000000 --- a/libb64-1.2/src/cdecode.c +++ /dev/null @@ -1,88 +0,0 @@ -/* -cdecoder.c - c source to a base64 decoding algorithm implementation - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#include - -int base64_decode_value(char value_in) -{ - static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; - static const char decoding_size = sizeof(decoding); - value_in -= 43; - if (value_in < 0 || value_in > decoding_size) return -1; - return decoding[(int)value_in]; -} - -void base64_init_decodestate(base64_decodestate* state_in) -{ - state_in->step = step_a; - state_in->plainchar = 0; -} - -int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in) -{ - const char* codechar = code_in; - char* plainchar = plaintext_out; - char fragment; - - *plainchar = state_in->plainchar; - - switch (state_in->step) - { - while (1) - { - case step_a: - do { - if (codechar == code_in+length_in) - { - state_in->step = step_a; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while (fragment < 0); - *plainchar = (fragment & 0x03f) << 2; - case step_b: - do { - if (codechar == code_in+length_in) - { - state_in->step = step_b; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while (fragment < 0); - *plainchar++ |= (fragment & 0x030) >> 4; - *plainchar = (fragment & 0x00f) << 4; - case step_c: - do { - if (codechar == code_in+length_in) - { - state_in->step = step_c; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while (fragment < 0); - *plainchar++ |= (fragment & 0x03c) >> 2; - *plainchar = (fragment & 0x003) << 6; - case step_d: - do { - if (codechar == code_in+length_in) - { - state_in->step = step_d; - state_in->plainchar = *plainchar; - return plainchar - plaintext_out; - } - fragment = (char)base64_decode_value(*codechar++); - } while (fragment < 0); - *plainchar++ |= (fragment & 0x03f); - } - } - /* control should not reach here */ - return plainchar - plaintext_out; -} - diff --git a/libb64-1.2/src/cencode.c b/libb64-1.2/src/cencode.c deleted file mode 100644 index 03ba5b6..0000000 --- a/libb64-1.2/src/cencode.c +++ /dev/null @@ -1,109 +0,0 @@ -/* -cencoder.c - c source to a base64 encoding algorithm implementation - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#include - -const int CHARS_PER_LINE = 72; - -void base64_init_encodestate(base64_encodestate* state_in) -{ - state_in->step = step_A; - state_in->result = 0; - state_in->stepcount = 0; -} - -char base64_encode_value(char value_in) -{ - static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - if (value_in > 63) return '='; - return encoding[(int)value_in]; -} - -int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in) -{ - const char* plainchar = plaintext_in; - const char* const plaintextend = plaintext_in + length_in; - char* codechar = code_out; - char result; - char fragment; - - result = state_in->result; - - switch (state_in->step) - { - while (1) - { - case step_A: - if (plainchar == plaintextend) - { - state_in->result = result; - state_in->step = step_A; - return codechar - code_out; - } - fragment = *plainchar++; - result = (fragment & 0x0fc) >> 2; - *codechar++ = base64_encode_value(result); - result = (fragment & 0x003) << 4; - case step_B: - if (plainchar == plaintextend) - { - state_in->result = result; - state_in->step = step_B; - return codechar - code_out; - } - fragment = *plainchar++; - result |= (fragment & 0x0f0) >> 4; - *codechar++ = base64_encode_value(result); - result = (fragment & 0x00f) << 2; - case step_C: - if (plainchar == plaintextend) - { - state_in->result = result; - state_in->step = step_C; - return codechar - code_out; - } - fragment = *plainchar++; - result |= (fragment & 0x0c0) >> 6; - *codechar++ = base64_encode_value(result); - result = (fragment & 0x03f) >> 0; - *codechar++ = base64_encode_value(result); - - ++(state_in->stepcount); - if (state_in->stepcount == CHARS_PER_LINE/4) - { - *codechar++ = '\n'; - state_in->stepcount = 0; - } - } - } - /* control should not reach here */ - return codechar - code_out; -} - -int base64_encode_blockend(char* code_out, base64_encodestate* state_in) -{ - char* codechar = code_out; - - switch (state_in->step) - { - case step_B: - *codechar++ = base64_encode_value(state_in->result); - *codechar++ = '='; - *codechar++ = '='; - break; - case step_C: - *codechar++ = base64_encode_value(state_in->result); - *codechar++ = '='; - break; - case step_A: - break; - } - *codechar++ = '\n'; - - return codechar - code_out; -} - diff --git a/licence.h b/licence.h index f95652f..9b9335b 100644 --- a/licence.h +++ b/licence.h @@ -6,9 +6,9 @@ */ #define LICENCE_TEXT(parsep) \ - "PuTTY is copyright 1997-2019 Simon Tatham." \ + "PuTTY is copyright 1997-2021 Simon Tatham." \ parsep \ - "Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, and CORE SDI S.A." \ + "Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, Colin Watson, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A." \ parsep \ "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:" \ parsep \ @@ -16,4 +16,4 @@ parsep \ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." -#define SHORT_COPYRIGHT_DETAILS "1997-2019 Simon Tatham" +#define SHORT_COPYRIGHT_DETAILS "1997-2021 Simon Tatham" diff --git a/logging.c b/logging.c index 173ed08..31cbccf 100644 --- a/logging.c +++ b/logging.c @@ -58,7 +58,7 @@ static void logwrite(LogContext *ctx, ptrlen data) * Convenience wrapper on logwrite() which printf-formats the * string. */ -static void logprintf(LogContext *ctx, const char *fmt, ...) +static PRINTF_LIKE(2, 3) void logprintf(LogContext *ctx, const char *fmt, ...) { va_list ap; char *data; @@ -344,7 +344,7 @@ void log_packet(LogContext *ctx, int direction, int type, /* If we're about to stop omitting, it's time to say how * much we omitted. */ if ((blktype != PKTLOG_OMIT) && omitted) { - logprintf(ctx, " (%d byte%s omitted)\r\n", + logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n", omitted, (omitted==1?"":"s")); omitted = 0; } @@ -352,7 +352,8 @@ void log_packet(LogContext *ctx, int direction, int type, /* (Re-)initialise dumpdata as necessary * (start of row, or if we've just stopped omitting) */ if (!output_pos && !omitted) - sprintf(dumpdata, " %08zx%*s\r\n", p-(p%16), 1+3*16+2+16, ""); + sprintf(dumpdata, " %08"SIZEx"%*s\r\n", + p-(p%16), 1+3*16+2+16, ""); /* Deal with the current byte. */ if (blktype == PKTLOG_OMIT) { @@ -363,7 +364,7 @@ void log_packet(LogContext *ctx, int direction, int type, c = 'X'; sprintf(smalldata, "XX"); } else { /* PKTLOG_EMIT */ - c = ((unsigned char *)data)[p]; + c = ((const unsigned char *)data)[p]; sprintf(smalldata, "%02x", c); } dumpdata[10+2+3*(p%16)] = smalldata[0]; @@ -387,7 +388,7 @@ void log_packet(LogContext *ctx, int direction, int type, /* Tidy up */ if (omitted) - logprintf(ctx, " (%d byte%s omitted)\r\n", + logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n", omitted, (omitted==1?"":"s")); logflush(ctx); } diff --git a/mainchan.c b/mainchan.c index 7722d4e..8653ad0 100644 --- a/mainchan.c +++ b/mainchan.c @@ -26,29 +26,29 @@ static bool mainchan_rcvd_exit_signal_numeric( Channel *chan, int signum, bool core_dumped, ptrlen msg); static void mainchan_request_response(Channel *chan, bool success); -static const struct ChannelVtable mainchan_channelvt = { - mainchan_free, - mainchan_open_confirmation, - mainchan_open_failure, - mainchan_send, - mainchan_send_eof, - mainchan_set_input_wanted, - mainchan_log_close_msg, - chan_default_want_close, - mainchan_rcvd_exit_status, - mainchan_rcvd_exit_signal, - mainchan_rcvd_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - mainchan_request_response, +static const ChannelVtable mainchan_channelvt = { + .free = mainchan_free, + .open_confirmation = mainchan_open_confirmation, + .open_failed = mainchan_open_failure, + .send = mainchan_send, + .send_eof = mainchan_send_eof, + .set_input_wanted = mainchan_set_input_wanted, + .log_close_msg = mainchan_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = mainchan_rcvd_exit_status, + .rcvd_exit_signal = mainchan_rcvd_exit_signal, + .rcvd_exit_signal_numeric = mainchan_rcvd_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = mainchan_request_response, }; typedef enum MainChanType { @@ -142,7 +142,7 @@ static void mainchan_open_confirmation(Channel *chan) struct X11FakeAuth *x11auth; bool retry_cmd_now = false; - if (conf_get_bool(mc->conf, CONF_x11_forward)) {; + if (conf_get_bool(mc->conf, CONF_x11_forward)) { char *x11_setup_err; if ((x11disp = x11_setup_display( conf_get_str(mc->conf, CONF_x11_display), @@ -236,7 +236,6 @@ static void mainchan_request_response(Channel *chan, bool success) if (success) { ppl_logevent("Agent forwarding enabled"); - ssh_enable_agent_fwd(mc->cl); } else { ppl_logevent("Agent forwarding refused"); } diff --git a/marshal.c b/marshal.c index a9b1074..ff9bb85 100644 --- a/marshal.c +++ b/marshal.c @@ -218,6 +218,53 @@ const char *BinarySource_get_asciz(BinarySource *src) return start; } +static ptrlen BinarySource_get_chars_internal( + BinarySource *src, const char *set, bool include) +{ + const char *start = here; + while (avail(1)) { + bool present = NULL != strchr(set, *(const char *)consume(0)); + if (present != include) + break; + (void) consume(1); + } + const char *end = here; + return make_ptrlen(start, end - start); +} + +ptrlen BinarySource_get_chars(BinarySource *src, const char *include_set) +{ + return BinarySource_get_chars_internal(src, include_set, true); +} + +ptrlen BinarySource_get_nonchars(BinarySource *src, const char *exclude_set) +{ + return BinarySource_get_chars_internal(src, exclude_set, false); +} + +ptrlen BinarySource_get_chomped_line(BinarySource *src) +{ + const char *start, *end; + + if (src->err) + return make_ptrlen(here, 0); + + start = here; + end = memchr(start, '\n', src->len - src->pos); + if (end) + advance(end + 1 - start); + else + advance(src->len - src->pos); + end = here; + + if (end > start && end[-1] == '\n') + end--; + if (end > start && end[-1] == '\r') + end--; + + return make_ptrlen(start, end - start); +} + ptrlen BinarySource_get_pstring(BinarySource *src) { const unsigned char *ucp; @@ -235,6 +282,17 @@ ptrlen BinarySource_get_pstring(BinarySource *src) return make_ptrlen(consume(len), len); } +void BinarySource_REWIND_TO__(BinarySource *src, size_t pos) +{ + if (pos <= src->len) { + src->pos = pos; + src->err = BSE_NO_ERROR; /* clear any existing error */ + } else { + src->pos = src->len; + src->err = BSE_OUT_OF_DATA; /* new error if we rewind out of range */ + } +} + static void stdio_sink_write(BinarySink *bs, const void *data, size_t len) { stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink); diff --git a/marshal.h b/marshal.h index 817620e..108d8ae 100644 --- a/marshal.h +++ b/marshal.h @@ -255,6 +255,10 @@ static inline void BinarySource_INIT__(BinarySource *src, ptrlen data) (object)->binarysource_) #define BinarySource_COPIED(obj) \ ((obj)->binarysource_->binarysource_ = (obj)->binarysource_) +#define BinarySource_REWIND_TO(src, pos) \ + BinarySource_REWIND_TO__((src)->binarysource_, pos) +#define BinarySource_REWIND(src) \ + BinarySource_REWIND_TO__((src)->binarysource_, 0) #define get_data(src, len) \ BinarySource_get_data(BinarySource_UPCAST(src), len) @@ -272,6 +276,12 @@ static inline void BinarySource_INIT__(BinarySource *src, ptrlen data) BinarySource_get_string(BinarySource_UPCAST(src)) #define get_asciz(src) \ BinarySource_get_asciz(BinarySource_UPCAST(src)) +#define get_chars(src, include) \ + BinarySource_get_chars(BinarySource_UPCAST(src), include) +#define get_nonchars(src, exclude) \ + BinarySource_get_nonchars(BinarySource_UPCAST(src), exclude) +#define get_chomped_line(src) \ + BinarySource_get_chomped_line(BinarySource_UPCAST(src)) #define get_pstring(src) \ BinarySource_get_pstring(BinarySource_UPCAST(src)) #define get_mp_ssh1(src) \ @@ -282,6 +292,8 @@ static inline void BinarySource_INIT__(BinarySource *src, ptrlen data) BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order) #define get_rsa_ssh1_priv(src, rsa) \ BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa) +#define get_rsa_ssh1_priv_agent(src) \ + BinarySource_get_rsa_ssh1_priv_agent(BinarySource_UPCAST(src)) #define get_err(src) (BinarySource_UPCAST(src)->err) #define get_avail(src) (BinarySource_UPCAST(src)->len - \ @@ -299,10 +311,15 @@ unsigned long BinarySource_get_uint32(BinarySource *); uint64_t BinarySource_get_uint64(BinarySource *); ptrlen BinarySource_get_string(BinarySource *); const char *BinarySource_get_asciz(BinarySource *); +ptrlen BinarySource_get_chars(BinarySource *, const char *include_set); +ptrlen BinarySource_get_nonchars(BinarySource *, const char *exclude_set); +ptrlen BinarySource_get_chomped_line(BinarySource *); ptrlen BinarySource_get_pstring(BinarySource *); mp_int *BinarySource_get_mp_ssh1(BinarySource *src); mp_int *BinarySource_get_mp_ssh2(BinarySource *src); +void BinarySource_REWIND_TO__(BinarySource *src, size_t pos); + /* * A couple of useful standard BinarySink implementations, which live * as sensibly here as anywhere else: one that makes a BinarySink diff --git a/memory.c b/memory.c index 43dd866..97ae940 100644 --- a/memory.c +++ b/memory.c @@ -121,9 +121,11 @@ void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize, void *toret; if (secret) { toret = safemalloc(newsize, eltsize, 0); - memcpy(toret, ptr, oldsize * eltsize); - smemclr(ptr, oldsize * eltsize); - sfree(ptr); + if (oldsize) { + memcpy(toret, ptr, oldsize * eltsize); + smemclr(ptr, oldsize * eltsize); + sfree(ptr); + } } else { toret = saferealloc(ptr, newsize, eltsize); } diff --git a/millerrabin.c b/millerrabin.c new file mode 100644 index 0000000..3358bc5 --- /dev/null +++ b/millerrabin.c @@ -0,0 +1,214 @@ +/* + * millerrabin.c: Miller-Rabin probabilistic primality testing, as + * declared in sshkeygen.h. + */ + +#include +#include "ssh.h" +#include "sshkeygen.h" +#include "mpint.h" +#include "mpunsafe.h" + +/* + * The Miller-Rabin primality test is an extension to the Fermat + * test. The Fermat test just checks that a^(p-1) == 1 mod p; this + * is vulnerable to Carmichael numbers. Miller-Rabin considers how + * that 1 is derived as well. + * + * Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1 + * or a == -1 (mod p). + * + * Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence, + * since p is prime, either p divides (a+1) or p divides (a-1). + * But this is the same as saying that either a is congruent to + * -1 mod p or a is congruent to +1 mod p. [] + * + * Comment: This fails when p is not prime. Consider p=mn, so + * that mn divides (a+1)(a-1). Now we could have m dividing (a+1) + * and n dividing (a-1), without the whole of mn dividing either. + * For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides + * 10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p + * without a having to be congruent to either 1 or -1. + * + * So the Miller-Rabin test, as well as considering a^(p-1), + * considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can + * go. In other words. we write p-1 as q * 2^k, with k as large as + * possible (i.e. q must be odd), and we consider the powers + * + * a^(q*2^0) a^(q*2^1) ... a^(q*2^(k-1)) a^(q*2^k) + * i.e. a^((n-1)/2^k) a^((n-1)/2^(k-1)) ... a^((n-1)/2) a^(n-1) + * + * If p is to be prime, the last of these must be 1. Therefore, by + * the above lemma, the one before it must be either 1 or -1. And + * _if_ it's 1, then the one before that must be either 1 or -1, + * and so on ... In other words, we expect to see a trailing chain + * of 1s preceded by a -1. (If we're unlucky, our trailing chain of + * 1s will be as long as the list so we'll never get to see what + * lies before it. This doesn't count as a test failure because it + * hasn't _proved_ that p is not prime.) + * + * For example, consider a=2 and p=1729. 1729 is a Carmichael + * number: although it's not prime, it satisfies a^(p-1) == 1 mod p + * for any a coprime to it. So the Fermat test wouldn't have a + * problem with it at all, unless we happened to stumble on an a + * which had a common factor. + * + * So. 1729 - 1 equals 27 * 2^6. So we look at + * + * 2^27 mod 1729 == 645 + * 2^108 mod 1729 == 1065 + * 2^216 mod 1729 == 1 + * 2^432 mod 1729 == 1 + * 2^864 mod 1729 == 1 + * 2^1728 mod 1729 == 1 + * + * We do have a trailing string of 1s, so the Fermat test would + * have been happy. But this trailing string of 1s is preceded by + * 1065; whereas if 1729 were prime, we'd expect to see it preceded + * by -1 (i.e. 1728.). Guards! Seize this impostor. + * + * (If we were unlucky, we might have tried a=16 instead of a=2; + * now 16^27 mod 1729 == 1, so we would have seen a long string of + * 1s and wouldn't have seen the thing _before_ the 1s. So, just + * like the Fermat test, for a given p there may well exist values + * of a which fail to show up its compositeness. So we try several, + * just like the Fermat test. The difference is that Miller-Rabin + * is not _in general_ fooled by Carmichael numbers.) + * + * Put simply, then, the Miller-Rabin test requires us to: + * + * 1. write p-1 as q * 2^k, with q odd + * 2. compute z = (a^q) mod p. + * 3. report success if z == 1 or z == -1. + * 4. square z at most k-1 times, and report success if it becomes + * -1 at any point. + * 5. report failure otherwise. + * + * (We expect z to become -1 after at most k-1 squarings, because + * if it became -1 after k squarings then a^(p-1) would fail to be + * 1. And we don't need to investigate what happens after we see a + * -1, because we _know_ that -1 squared is 1 modulo anything at + * all, so after we've seen a -1 we can be sure of seeing nothing + * but 1s.) + */ + +struct MillerRabin { + MontyContext *mc; + + size_t k; + mp_int *q; + + mp_int *two, *pm1, *m_pm1; +}; + +MillerRabin *miller_rabin_new(mp_int *p) +{ + MillerRabin *mr = snew(MillerRabin); + + assert(mp_hs_integer(p, 2)); + assert(mp_get_bit(p, 0) == 1); + + mr->k = 1; + while (!mp_get_bit(p, mr->k)) + mr->k++; + mr->q = mp_rshift_safe(p, mr->k); + + mr->two = mp_from_integer(2); + + mr->pm1 = mp_unsafe_copy(p); + mp_sub_integer_into(mr->pm1, mr->pm1, 1); + + mr->mc = monty_new(p); + mr->m_pm1 = monty_import(mr->mc, mr->pm1); + + return mr; +} + +void miller_rabin_free(MillerRabin *mr) +{ + mp_free(mr->q); + mp_free(mr->two); + mp_free(mr->pm1); + mp_free(mr->m_pm1); + monty_free(mr->mc); + smemclr(mr, sizeof(*mr)); + sfree(mr); +} + +struct mr_result { + bool passed; + bool potential_primitive_root; +}; + +static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *w) +{ + /* + * Compute w^q mod p. + */ + mp_int *wqp = monty_pow(mr->mc, w, mr->q); + + /* + * See if this is 1, or if it is -1, or if it becomes -1 + * when squared at most k-1 times. + */ + struct mr_result result; + result.passed = false; + result.potential_primitive_root = false; + + if (mp_cmp_eq(wqp, monty_identity(mr->mc))) { + result.passed = true; + } else { + for (size_t i = 0; i < mr->k; i++) { + if (mp_cmp_eq(wqp, mr->m_pm1)) { + result.passed = true; + result.potential_primitive_root = (i == mr->k - 1); + break; + } + if (i == mr->k - 1) + break; + monty_mul_into(mr->mc, wqp, wqp, wqp); + } + } + + mp_free(wqp); + + return result; +} + +bool miller_rabin_test_random(MillerRabin *mr) +{ + mp_int *mw = mp_random_in_range(mr->two, mr->pm1); + struct mr_result result = miller_rabin_test_inner(mr, mw); + mp_free(mw); + return result.passed; +} + +mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr) +{ + while (true) { + mp_int *mw = mp_unsafe_shrink(mp_random_in_range(mr->two, mr->pm1)); + struct mr_result result = miller_rabin_test_inner(mr, mw); + + if (result.passed && result.potential_primitive_root) { + mp_int *pr = monty_export(mr->mc, mw); + mp_free(mw); + return pr; + } + + mp_free(mw); + + if (!result.passed) { + return NULL; + } + } +} + +unsigned miller_rabin_checks_needed(unsigned bits) +{ + /* Table 4.4 from Handbook of Applied Cryptography */ + return (bits >= 1300 ? 2 : bits >= 850 ? 3 : bits >= 650 ? 4 : + bits >= 550 ? 5 : bits >= 450 ? 6 : bits >= 400 ? 7 : + bits >= 350 ? 8 : bits >= 300 ? 9 : bits >= 250 ? 12 : + bits >= 200 ? 15 : bits >= 150 ? 18 : 27); +} + diff --git a/minibidi.c b/minibidi.c index 39b6661..05d15b3 100644 --- a/minibidi.c +++ b/minibidi.c @@ -102,7 +102,7 @@ typedef struct { #define SHAPE_FIRST 0x621 #define SHAPE_LAST (SHAPE_FIRST + lenof(shapetypes) - 1) -const shape_node shapetypes[] = { +static const shape_node shapetypes[] = { /* index, Typ, Iso, Ligature Index*/ /* 621 */ {SU, 0xFE80}, /* 622 */ {SR, 0xFE81}, @@ -1281,20 +1281,19 @@ int do_bidi(bidi_char *line, int count) bover = true; break; - case PDF: - { - int prevlevel = getPreviousLevel(levels, i); - - if (prevlevel == -1) { - currentEmbedding = paragraphLevel; - currentOverride = ON; - } else { - currentOverride = currentEmbedding & OMASK; - currentEmbedding = currentEmbedding & ~OMASK; - } + case PDF: { + int prevlevel = getPreviousLevel(levels, i); + + if (prevlevel == -1) { + currentEmbedding = paragraphLevel; + currentOverride = ON; + } else { + currentOverride = currentEmbedding & OMASK; + currentEmbedding = currentEmbedding & ~OMASK; } levels[i] = currentEmbedding; break; + } /* Whitespace is treated as neutral for now */ case WS: diff --git a/misc.c b/misc.c index 0e52f7a..56f2ba9 100644 --- a/misc.c +++ b/misc.c @@ -22,6 +22,10 @@ #include "putty.h" #include "misc.h" +#define BASE64_CHARS_NOEQ \ + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" +#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "=" + void seat_connection_fatal(Seat *seat, const char *fmt, ...) { va_list ap; @@ -51,43 +55,29 @@ void add_prompt(prompts_t *p, char *promptstr, bool echo) prompt_t *pr = snew(prompt_t); pr->prompt = promptstr; pr->echo = echo; - pr->result = NULL; - pr->resultsize = 0; + pr->result = strbuf_new_nm(); sgrowarray(p->prompts, p->prompts_size, p->n_prompts); p->prompts[p->n_prompts++] = pr; } -void prompt_ensure_result_size(prompt_t *pr, int newlen) +void prompt_set_result(prompt_t *pr, const char *newstr) { - if ((int)pr->resultsize < newlen) { - char *newbuf; - newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ - - /* - * We don't use sresize / realloc here, because we will be - * storing sensitive stuff like passwords in here, and we want - * to make sure that the data doesn't get copied around in - * memory without the old copy being destroyed. - */ - newbuf = snewn(newlen, char); - memcpy(newbuf, pr->result, pr->resultsize); - smemclr(pr->result, pr->resultsize); - sfree(pr->result); - pr->result = newbuf; - pr->resultsize = newlen; - } + strbuf_clear(pr->result); + put_datapl(pr->result, ptrlen_from_asciz(newstr)); } -void prompt_set_result(prompt_t *pr, const char *newstr) +const char *prompt_get_result_ref(prompt_t *pr) { - prompt_ensure_result_size(pr, strlen(newstr) + 1); - strcpy(pr->result, newstr); + return pr->result->s; +} +char *prompt_get_result(prompt_t *pr) +{ + return dupstr(pr->result->s); } void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; - smemclr(pr->result, pr->resultsize); /* burn the evidence */ - sfree(pr->result); + strbuf_free(pr->result); sfree(pr->prompt); sfree(pr); } @@ -143,22 +133,32 @@ bool validate_manual_hostkey(char *key) * Now q is our word. */ - if (strlen(q) == 16*3 - 1 && - q[strspn(q, "0123456789abcdefABCDEF:")] == 0) { + if (strstartswith(q, "SHA256:")) { + /* Test for a valid SHA256 key fingerprint. */ + r = q + 7; + if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0) + return true; + } + + r = q; + if (strstartswith(r, "MD5:")) + r += 4; + if (strlen(r) == 16*3 - 1 && + r[strspn(r, "0123456789abcdefABCDEF:")] == 0) { /* - * Might be a key fingerprint. Check the colons are in the - * right places, and if so, return the same fingerprint - * canonicalised into lowercase. + * Test for a valid MD5 key fingerprint. Check the colons + * are in the right places, and if so, return the same + * fingerprint canonicalised into lowercase. */ int i; for (i = 0; i < 16; i++) - if (q[3*i] == ':' || q[3*i+1] == ':') + if (r[3*i] == ':' || r[3*i+1] == ':') goto not_fingerprint; /* sorry */ for (i = 0; i < 15; i++) - if (q[3*i+2] != ':') + if (r[3*i+2] != ':') goto not_fingerprint; /* sorry */ for (i = 0; i < 16*3 - 1; i++) - key[i] = tolower(q[i]); + key[i] = tolower(r[i]); key[16*3 - 1] = '\0'; return true; } @@ -175,8 +175,7 @@ bool validate_manual_hostkey(char *key) *s = '\0'; if (strlen(q) % 4 == 0 && strlen(q) > 2*4 && - q[strspn(q, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz+/=")] == 0) { + q[strspn(q, BASE64_CHARS_ALL)] == 0) { /* * Might be a base64-encoded SSH-2 public key blob. Check * that it starts with a sensible algorithm string. No @@ -237,22 +236,47 @@ char *buildinfo(const char *newline) #else strbuf_catf(buf, ", emulating "); #endif - strbuf_catf(buf, "Visual Studio", newline); + strbuf_catf(buf, "Visual Studio"); #if 0 /* * List of _MSC_VER values and their translations taken from * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros - * except for 1920, which is not yet listed on that page as of - * 2019-03-22, and was determined experimentally by Sean Kain. * * The pointless #if 0 branch containing this comment is there so * that every real clause can start with #elif and there's no * anomalous first clause. That way the patch looks nicer when you * add extra ones. */ +#elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500 + /* + * 16.9 and 16.8 have the same _MSC_VER value, and have to be + * distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not + * mentioned on the above page, but see e.g. + * https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120 + * which says that 16.9 builds will have versions starting at + * 19.28.29500.* and going up. Hence, 19 28 29500 is what we + * compare _MSC_FULL_VER against above. + */ + strbuf_catf(buf, " 2019 (16.9)"); +#elif _MSC_VER == 1928 + strbuf_catf(buf, " 2019 (16.8)"); +#elif _MSC_VER == 1927 + strbuf_catf(buf, " 2019 (16.7)"); +#elif _MSC_VER == 1926 + strbuf_catf(buf, " 2019 (16.6)"); +#elif _MSC_VER == 1925 + strbuf_catf(buf, " 2019 (16.5)"); +#elif _MSC_VER == 1924 + strbuf_catf(buf, " 2019 (16.4)"); +#elif _MSC_VER == 1923 + strbuf_catf(buf, " 2019 (16.3)"); +#elif _MSC_VER == 1922 + strbuf_catf(buf, " 2019 (16.2)"); +#elif _MSC_VER == 1921 + strbuf_catf(buf, " 2019 (16.1)"); #elif _MSC_VER == 1920 - strbuf_catf(buf, " 2019 (16.x)"); + strbuf_catf(buf, " 2019 (16.0)"); #elif _MSC_VER == 1916 strbuf_catf(buf, " 2017 version 15.9"); #elif _MSC_VER == 1915 @@ -354,8 +378,8 @@ void nullseat_update_specials_menu(Seat *seat) {} char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; } void nullseat_set_busy_status(Seat *seat, BusyStatus status) {} int nullseat_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *key_fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx) { return 0; } int nullseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, @@ -374,6 +398,14 @@ StripCtrlChars *nullseat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;} bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; } bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; } +bool nullseat_verbose_no(Seat *seat) { return false; } +bool nullseat_verbose_yes(Seat *seat) { return true; } +bool nullseat_interactive_no(Seat *seat) { return false; } +bool nullseat_interactive_yes(Seat *seat) { return true; } +bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; } + +bool null_lp_verbose_no(LogPolicy *lp) { return false; } +bool null_lp_verbose_yes(LogPolicy *lp) { return true; } void sk_free_peer_info(SocketPeerInfo *pi) { diff --git a/misc.h b/misc.h index 6991d8e..7b4012b 100644 --- a/misc.h +++ b/misc.h @@ -24,29 +24,10 @@ char *host_strchr(const char *s, int c); char *host_strrchr(const char *s, int c); char *host_strduptrim(const char *s); -#ifdef __GNUC__ -/* - * On MinGW, the correct compiler format checking for vsnprintf() etc - * can depend on compile-time flags; these control whether you get - * ISO C or Microsoft's non-standard format strings. - * We sometimes use __attribute__ ((format)) for our own printf-like - * functions, which are ultimately interpreted by the toolchain-chosen - * printf, so we need to take that into account to get correct warnings. - */ -#ifdef __MINGW_PRINTF_FORMAT -#define PUTTY_PRINTF_ARCHETYPE __MINGW_PRINTF_FORMAT -#else -#define PUTTY_PRINTF_ARCHETYPE printf -#endif -#endif /* __GNUC__ */ - char *dupstr(const char *s); -char *dupcat(const char *s1, ...); -char *dupprintf(const char *fmt, ...) -#ifdef __GNUC__ - __attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 1, 2))) -#endif - ; +char *dupcat_fn(const char *s1, ...); +#define dupcat(...) dupcat_fn(__VA_ARGS__, (const char *)NULL) +char *dupprintf(const char *fmt, ...) PRINTF_LIKE(1, 2); char *dupvprintf(const char *fmt, va_list ap); void burnstr(char *string); @@ -72,9 +53,13 @@ strbuf *strbuf_new_nm(void); void strbuf_free(strbuf *buf); void *strbuf_append(strbuf *buf, size_t len); +void strbuf_shrink_to(strbuf *buf, size_t new_len); +void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove); char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */ -void strbuf_catf(strbuf *buf, const char *fmt, ...); +void strbuf_catf(strbuf *buf, const char *fmt, ...) PRINTF_LIKE(2, 3); void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap); +static inline void strbuf_clear(strbuf *buf) { strbuf_shrink_to(buf, 0); } +bool strbuf_chomp(strbuf *buf, char char_to_remove); strbuf *strbuf_new_for_agent_query(void); void strbuf_finalise_agent_query(strbuf *buf); @@ -204,6 +189,10 @@ int string_length_for_printf(size_t); * string. */ #define PTRLEN_LITERAL(stringlit) \ TYPECHECK("" stringlit "", make_ptrlen(stringlit, sizeof(stringlit)-1)) +/* Make a ptrlen out of a compile-time string literal in a way that + * allows you to declare the ptrlen itself as a compile-time initialiser. */ +#define PTRLEN_DECL_LITERAL(stringlit) \ + { TYPECHECK("" stringlit "", stringlit), sizeof(stringlit)-1 } /* Make a ptrlen out of a constant byte array. */ #define PTRLEN_FROM_CONST_BYTES(a) make_ptrlen(a, sizeof(a)) @@ -226,6 +215,9 @@ bool smemeq(const void *av, const void *bv, size_t len); * been removed. */ size_t encode_utf8(void *output, unsigned long ch); +/* Write a string out in C string-literal format. */ +void write_c_string_literal(FILE *fp, ptrlen str); + char *buildinfo(const char *newline); /* @@ -258,7 +250,7 @@ static inline NORETURN void unreachable_internal(void) { abort(); } */ #ifdef DEBUG -void debug_printf(const char *fmt, ...); +void debug_printf(const char *fmt, ...) PRINTF_LIKE(1, 2); void debug_memdump(const void *buf, int len, bool L); #define debug(...) (debug_printf(__VA_ARGS__)) #define dmemdump(buf,len) (debug_memdump(buf, len, false)) @@ -417,4 +409,34 @@ static inline char *stripctrl_string(StripCtrlChars *sccpub, const char *str) return stripctrl_string_ptrlen(sccpub, ptrlen_from_asciz(str)); } +/* + * A mechanism for loading a file from disk into a memory buffer where + * it can be picked apart as a BinarySource. + */ +struct LoadedFile { + char *data; + size_t len, max_size; + BinarySource_IMPLEMENTATION; +}; +typedef enum { + LF_OK, /* file loaded successfully */ + LF_TOO_BIG, /* file didn't fit in buffer */ + LF_ERROR, /* error from stdio layer */ +} LoadFileStatus; +LoadedFile *lf_new(size_t max_size); +void lf_free(LoadedFile *lf); +LoadFileStatus lf_load_fp(LoadedFile *lf, FILE *fp); +LoadFileStatus lf_load(LoadedFile *lf, const Filename *filename); +static inline ptrlen ptrlen_from_lf(LoadedFile *lf) +{ return make_ptrlen(lf->data, lf->len); } + +/* Set the memory block of 'size' bytes at 'out' to the bitwise XOR of + * the two blocks of the same size at 'in1' and 'in2'. + * + * 'out' may point to exactly the same address as one of the inputs, + * but if the input and output blocks overlap in any other way, the + * result of this function is not guaranteed. No memmove-style effort + * is made to handle difficult overlap cases. */ +void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size); + #endif diff --git a/missing b/missing index f62bbae..625aeb1 100755 --- a/missing +++ b/missing @@ -1,9 +1,9 @@ #! /bin/sh # Common wrapper for a few potentially missing GNU programs. -scriptversion=2013-10-28.13; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify @@ -17,7 +17,7 @@ scriptversion=2013-10-28.13; # UTC # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -101,9 +101,9 @@ else exit $st fi -perl_URL=http://www.perl.org/ -flex_URL=http://flex.sourceforge.net/ -gnu_software_URL=http://www.gnu.org/software +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software program_details () { @@ -207,9 +207,9 @@ give_advice "$1" | sed -e '1s/^/WARNING: /' \ exit $st # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/mkfiles.pl b/mkfiles.pl index 00112a4..b323e87 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -1425,7 +1425,7 @@ sub manpages { "\n". "unexport CFLAGS # work around a weird issue with krb5-config\n". "\n". - &splitline("CFLAGS = -O2 -Wall -Werror -std=gnu99 -Wvla -g " . + &splitline("CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g " . (join " ", map {"-I$dirpfx$_"} @srcdirs) . " \$(shell \$(GTK_CONFIG) --cflags)"). " -D _FILE_OFFSET_BITS=64\n". @@ -1506,7 +1506,7 @@ sub manpages { "\n". "unexport CFLAGS # work around a weird issue with krb5-config\n". "\n". - &splitline("CFLAGS = -O2 -Wall -Werror -std=gnu99 -Wvla -g " . + &splitline("CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g " . (join " ", map {"-I$dirpfx$_"} @srcdirs)). " -D _FILE_OFFSET_BITS=64\n". "ULDFLAGS = \$(LDFLAGS)\n". @@ -1748,7 +1748,7 @@ sub manpages { print "CC = \$(TOOLPATH)gcc\n". "\n". - &splitline("CFLAGS = -O2 -Wall -Werror -std=gnu99 -Wvla -g " . + &splitline("CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g " . (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". "MLDFLAGS = -framework Cocoa\n". "ULDFLAGS =\n". diff --git a/mpint.c b/mpint.c index 8140faf..39f5506 100644 --- a/mpint.c +++ b/mpint.c @@ -33,7 +33,36 @@ static inline BignumInt mp_word(mp_int *x, size_t i) return i < x->nw ? x->w[i] : 0; } -static mp_int *mp_make_sized(size_t nw) +/* + * Shift an ordinary C integer by BIGNUM_INT_BITS, in a way that + * avoids writing a shift operator whose RHS is greater or equal to + * the size of the type, because that's undefined behaviour in C. + * + * In fact we must avoid even writing it in a definitely-untaken + * branch of an if, because compilers will sometimes warn about + * that. So you can't just write 'shift too big ? 0 : n >> shift', + * because even if 'shift too big' is a constant-expression + * evaluating to false, you can still get complaints about the + * else clause of the ?:. + * + * So we have to re-check _inside_ that clause, so that the shift + * count is reset to something nonsensical but safe in the case + * where the clause wasn't going to be taken anyway. + */ +static uintmax_t shift_right_by_one_word(uintmax_t n) +{ + bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n); + return shift_too_big ? 0 : + n >> (shift_too_big ? 0 : BIGNUM_INT_BITS); +} +static uintmax_t shift_left_by_one_word(uintmax_t n) +{ + bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n); + return shift_too_big ? 0 : + n << (shift_too_big ? 0 : BIGNUM_INT_BITS); +} + +mp_int *mp_make_sized(size_t nw) { mp_int *x = snew_plus(mp_int, nw * sizeof(BignumInt)); assert(nw); /* we outlaw the zero-word mp_int */ @@ -90,6 +119,14 @@ void mp_copy_into(mp_int *dest, mp_int *src) smemclr(dest->w + copy_nw, (dest->nw - copy_nw) * sizeof(BignumInt)); } +void mp_copy_integer_into(mp_int *r, uintmax_t n) +{ + for (size_t i = 0; i < r->nw; i++) { + r->w[i] = n; + n = shift_right_by_one_word(n); + } +} + /* * Conditional selection is done by negating 'which', to give a mask * word which is all 1s if which==1 and all 0s if which==0. Then you @@ -187,7 +224,7 @@ mp_int *mp_from_decimal_pl(ptrlen decimal) mp_int *x = mp_make_sized(words); for (size_t i = 0; i < decimal.len; i++) { - mp_add_integer_into(x, x, ((char *)decimal.ptr)[i] - '0'); + mp_add_integer_into(x, x, ((const char *)decimal.ptr)[i] - '0'); if (i+1 == decimal.len) break; @@ -216,7 +253,7 @@ mp_int *mp_from_hex_pl(ptrlen hex) words = size_t_max(words, 1); mp_int *x = mp_make_sized(words); for (size_t nibble = 0; nibble < hex.len; nibble++) { - BignumInt digit = ((char *)hex.ptr)[hex.len-1 - nibble]; + BignumInt digit = ((const char *)hex.ptr)[hex.len-1 - nibble]; BignumInt lmask = ~-((BignumInt)((digit-'a')|('f'-digit)) >> (BIGNUM_INT_BITS-1)); @@ -260,12 +297,8 @@ unsigned mp_get_bit(mp_int *x, size_t bit) uintmax_t mp_get_integer(mp_int *x) { uintmax_t toret = 0; - for (size_t i = x->nw; i-- > 0 ;) { - /* Shift in two stages to avoid undefined behaviour if the - * shift count equals the integer width */ - toret = (toret << (BIGNUM_INT_BITS/2)) << (BIGNUM_INT_BITS/2); - toret |= x->w[i]; - } + for (size_t i = x->nw; i-- > 0 ;) + toret = shift_left_by_one_word(toret) | x->w[i]; return toret; } @@ -757,8 +790,8 @@ static BignumCarry mp_add_masked_integer_into( { for (size_t i = 0; i < rw; i++) { BignumInt aword = mp_word(a, i); - size_t shift = i * BIGNUM_INT_BITS; - BignumInt bword = shift < BIGNUM_INT_BYTES ? b >> shift : 0; + BignumInt bword = b; + b = shift_right_by_one_word(b); BignumInt out; bword = (bword ^ b_xor) & b_and; BignumADC(out, carry, aword, bword, carry); @@ -799,7 +832,7 @@ static void mp_add_integer_into_shifted_by_words( * shift n down. If it's 0, we add zero bits into r, and * leave n alone. */ BignumInt bword = n & -(BignumInt)indicator; - uintmax_t new_n = (BIGNUM_INT_BITS < 64 ? n >> BIGNUM_INT_BITS : 0); + uintmax_t new_n = shift_right_by_one_word(n); n ^= (n ^ new_n) & -(uintmax_t)indicator; BignumInt aword = mp_word(a, i); @@ -844,11 +877,12 @@ unsigned mp_cmp_hs(mp_int *a, mp_int *b) unsigned mp_hs_integer(mp_int *x, uintmax_t n) { BignumInt carry = 1; - for (size_t i = 0; i < x->nw; i++) { - size_t shift = i * BIGNUM_INT_BITS; - BignumInt nword = shift < CHAR_BIT*sizeof(n) ? n >> shift : 0; + size_t nwords = sizeof(n)/BIGNUM_INT_BYTES; + for (size_t i = 0, e = size_t_max(x->nw, nwords); i < e; i++) { + BignumInt nword = n; + n = shift_right_by_one_word(n); BignumInt dummy_out; - BignumADC(dummy_out, carry, x->w[i], ~nword, carry); + BignumADC(dummy_out, carry, mp_word(x, i), ~nword, carry); (void)dummy_out; } return carry; @@ -870,15 +904,16 @@ unsigned mp_cmp_eq(mp_int *a, mp_int *b) unsigned mp_eq_integer(mp_int *x, uintmax_t n) { BignumInt diff = 0; - for (size_t i = 0; i < x->nw; i++) { - size_t shift = i * BIGNUM_INT_BITS; - BignumInt nword = shift < CHAR_BIT*sizeof(n) ? n >> shift : 0; - diff |= x->w[i] ^ nword; + size_t nwords = sizeof(n)/BIGNUM_INT_BYTES; + for (size_t i = 0, e = size_t_max(x->nw, nwords); i < e; i++) { + BignumInt nword = n; + n = shift_right_by_one_word(n); + diff |= mp_word(x, i) ^ nword; } return 1 ^ normalise_to_1(diff); /* return 1 if diff _is_ zero */ } -void mp_neg_into(mp_int *r, mp_int *a) +static void mp_neg_into(mp_int *r, mp_int *a) { mp_int zero; zero.nw = 0; @@ -899,13 +934,6 @@ mp_int *mp_sub(mp_int *x, mp_int *y) return r; } -mp_int *mp_neg(mp_int *a) -{ - mp_int *r = mp_make_sized(a->nw); - mp_neg_into(r, a); - return r; -} - /* * Internal routine: multiply and accumulate in the trivial O(N^2) * way. Sets r <- r + a*b. @@ -1121,6 +1149,14 @@ void mp_rshift_fixed_into(mp_int *r, mp_int *a, size_t bits) } } +mp_int *mp_lshift_fixed(mp_int *x, size_t bits) +{ + size_t words = (bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; + mp_int *r = mp_make_sized(x->nw + words); + mp_lshift_fixed_into(r, x, bits); + return r; +} + mp_int *mp_rshift_fixed(mp_int *x, size_t bits) { size_t words = bits / BIGNUM_INT_BITS; @@ -1138,18 +1174,16 @@ mp_int *mp_rshift_fixed(mp_int *x, size_t bits) * by a power of 2 words, using the usual bit twiddling to make the * whole shift conditional on the appropriate bit of n. */ -mp_int *mp_rshift_safe(mp_int *x, size_t bits) +static void mp_rshift_safe_in_place(mp_int *r, size_t bits) { size_t wordshift = bits / BIGNUM_INT_BITS; size_t bitshift = bits % BIGNUM_INT_BITS; - mp_int *r = mp_copy(x); - unsigned clear = (r->nw - wordshift) >> (CHAR_BIT * sizeof(size_t) - 1); mp_cond_clear(r, clear); for (unsigned bit = 0; r->nw >> bit; bit++) { - size_t word_offset = 1 << bit; + size_t word_offset = (size_t)1 << bit; BignumInt mask = -(BignumInt)((wordshift >> bit) & 1); for (size_t i = 0; i < r->nw; i++) { BignumInt w = mp_word(r, i + word_offset); @@ -1169,10 +1203,60 @@ mp_int *mp_rshift_safe(mp_int *x, size_t bits) r->w[i] ^= (r->w[i] ^ w) & mask; } } +} +mp_int *mp_rshift_safe(mp_int *x, size_t bits) +{ + mp_int *r = mp_copy(x); + mp_rshift_safe_in_place(r, bits); return r; } +void mp_rshift_safe_into(mp_int *r, mp_int *x, size_t bits) +{ + mp_copy_into(r, x); + mp_rshift_safe_in_place(r, bits); +} + +static void mp_lshift_safe_in_place(mp_int *r, size_t bits) +{ + size_t wordshift = bits / BIGNUM_INT_BITS; + size_t bitshift = bits % BIGNUM_INT_BITS; + + /* + * Same strategy as mp_rshift_safe_in_place, but of course the + * other way up. + */ + + unsigned clear = (r->nw - wordshift) >> (CHAR_BIT * sizeof(size_t) - 1); + mp_cond_clear(r, clear); + + for (unsigned bit = 0; r->nw >> bit; bit++) { + size_t word_offset = (size_t)1 << bit; + BignumInt mask = -(BignumInt)((wordshift >> bit) & 1); + for (size_t i = r->nw; i-- > 0 ;) { + BignumInt w = mp_word(r, i - word_offset); + r->w[i] ^= (r->w[i] ^ w) & mask; + } + } + + size_t downshift = BIGNUM_INT_BITS - bitshift; + size_t no_shift = (downshift >> BIGNUM_INT_BITS_BITS); + downshift &= ~-(size_t)no_shift; + BignumInt downshifted_mask = ~-(BignumInt)no_shift; + + for (size_t i = r->nw; i-- > 0 ;) { + r->w[i] = (r->w[i] << bitshift) | + ((mp_word(r, i-1) >> downshift) & downshifted_mask); + } +} + +void mp_lshift_safe_into(mp_int *r, mp_int *x, size_t bits) +{ + mp_copy_into(r, x); + mp_lshift_safe_in_place(r, bits); +} + void mp_reduce_mod_2to(mp_int *x, size_t p) { size_t word = p / BIGNUM_INT_BITS; @@ -1506,10 +1590,10 @@ mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus) } /* - * Given two coprime nonzero input integers a,b, returns two integers - * A,B such that A*a - B*b = 1. A,B will be the minimal non-negative - * pair satisfying that criterion, which is equivalent to saying that - * 0<=Ab, and gcd(a,b) = * gcd(b,(a-b)/2). * - * For this application, I always expect the actual gcd to be coprime, - * so we can rule out the 'both even' initial case. So this function - * just performs a sequence of reductions in the following form: + * Sometimes this function is used for modular inversion, in which + * case we already know we expect the two inputs to be coprime, so to + * save time the 'both even' initial case is assumed not to arise (or + * to have been handled already by the caller). So this function just + * performs a sequence of reductions in the following form: * * - if a,b are both odd, sort them so that a > b, and replace a with * b-a; otherwise sort them so that a is the even one @@ -1534,14 +1620,14 @@ mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus) * generate those in each case, based on the coefficients from the * reduced pair of numbers: * - * - If a is even, and u,v are such that u*(a/2) + v*b = 1: - * + if u is also even, then this is just (u/2)*a + v*b = 1 - * + otherwise, (u+b)*(a/2) + (v-a/2)*b is also equal to 1, and + * - If a is even, and u,v are such that u*(a/2) + v*b = d: + * + if u is also even, then this is just (u/2)*a + v*b = d + * + otherwise, (u+b)*(a/2) + (v-a/2)*b is also equal to d, and * since u and b are both odd, (u+b)/2 is an integer, so we have - * ((u+b)/2)*a + (v-a/2)*b = 1. + * ((u+b)/2)*a + (v-a/2)*b = d. * - * - If a,b are both odd, and u,v are such that u*b + v*(a-b) = 1, - * then v*a + (u-v)*b = 1. + * - If a,b are both odd, and u,v are such that u*b + v*(a-b) = d, + * then v*a + (u-v)*b = d. * * In the case where we passed from (a,b) to (b,(a-b)/2), we regard it * as having first subtracted b from a and then halved a, so both of @@ -1559,11 +1645,11 @@ mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus) * Also, since these mp_ints are generally treated as unsigned, we * store the coefficients by absolute value, with the semantics that * they always have opposite sign, and in the unwinding loop we keep a - * bit indicating whether Aa-Bb is currently expected to be +1 or -1, - * so that we can do one final conditional adjustment if it's -1. + * bit indicating whether Aa-Bb is currently expected to be +d or -d, + * so that we can do one final conditional adjustment if it's -d. * * Once the reduction rules have managed to reduce the input numbers - * to (0,1), then they are stable (the next reduction will always + * to (0,d), then they are stable (the next reduction will always * divide the even one by 2, which maps 0 to 0). So it doesn't matter * if we do more steps of the algorithm than necessary; hence, for * constant time, we just need to find the maximum number we could @@ -1582,7 +1668,7 @@ mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus) * n further steps each of which subtracts 1 from y and halves it. */ static void mp_bezout_into(mp_int *a_coeff_out, mp_int *b_coeff_out, - mp_int *a_in, mp_int *b_in) + mp_int *gcd_out, mp_int *a_in, mp_int *b_in) { size_t nw = size_t_max(1, size_t_max(a_in->nw, b_in->nw)); @@ -1641,99 +1727,126 @@ static void mp_bezout_into(mp_int *a_coeff_out, mp_int *b_coeff_out, } /* - * Now we expect to have reduced the two numbers to 0 and 1, + * Now we expect to have reduced the two numbers to 0 and d, * although we don't know which way round. (But we avoid checking * this by assertion; sometimes we'll need to do this computation * without giving away that we already know the inputs were bogus. * So we'd prefer to just press on and return nonsense.) */ - /* - * So their Bezout coefficients at this point are simply - * themselves. - */ - mp_copy_into(ac, a); - mp_copy_into(bc, b); + if (gcd_out) { + /* + * At this point we can return the actual gcd. Since one of + * a,b is it and the other is zero, the easiest way to get it + * is to add them together. + */ + mp_add_into(gcd_out, a, b); + } /* - * We'll maintain the invariant as we unwind that ac * a - bc * b - * is either +1 or -1, and we'll remember which. (We _could_ keep - * it at +1 the whole time, but it would cost more work every time - * round the loop, so it's cheaper to fix that up once at the - * end.) - * - * Initially, the result is +1 if a was the nonzero value after - * reduction, and -1 if b was. + * If the caller _only_ wanted the gcd, and neither Bezout + * coefficient is even required, we can skip the entire unwind + * stage. */ - unsigned minus_one = b->w[0]; + if (a_coeff_out || b_coeff_out) { - for (size_t step = steps; step-- > 0 ;) { /* - * Recover the data from the step we're unwinding. + * The Bezout coefficients of a,b at this point are simply 0 + * for whichever of a,b is zero, and 1 for whichever is + * nonzero. The nonzero number equals gcd(a,b), which by + * assumption is odd, so we can do this by just taking the low + * bit of each one. */ - unsigned both_odd = mp_get_bit(record, step*2); - unsigned swap = mp_get_bit(record, step*2+1); + ac->w[0] = mp_get_bit(a, 0); + bc->w[0] = mp_get_bit(b, 0); /* - * Unwind the division: if our coefficient of a is odd, we - * adjust the coefficients by +b and +a respectively. + * Overwrite a,b themselves with those same numbers. This has + * the effect of dividing both of them by d, which will + * arrange that during the unwind stage we generate the + * minimal coefficients instead of a larger pair. */ - unsigned adjust = ac->w[0] & 1; - mp_cond_add_into(ac, ac, b, adjust); - mp_cond_add_into(bc, bc, a, adjust); + mp_copy_into(a, ac); + mp_copy_into(b, bc); /* - * Now ac is definitely even, so we divide it by two. + * We'll maintain the invariant as we unwind that ac * a - bc + * * b is either +d or -d (or rather, +1/-1 after scaling by + * d), and we'll remember which. (We _could_ keep it at +d the + * whole time, but it would cost more work every time round + * the loop, so it's cheaper to fix that up once at the end.) + * + * Initially, the result is +d if a was the nonzero value after + * reduction, and -d if b was. */ - mp_rshift_fixed_into(ac, ac, 1); + unsigned minus_d = b->w[0]; - /* - * Now unwind the subtraction, if there was one, by adding - * ac to bc. - */ - mp_cond_add_into(bc, bc, ac, both_odd); + for (size_t step = steps; step-- > 0 ;) { + /* + * Recover the data from the step we're unwinding. + */ + unsigned both_odd = mp_get_bit(record, step*2); + unsigned swap = mp_get_bit(record, step*2+1); + + /* + * Unwind the division: if our coefficient of a is odd, we + * adjust the coefficients by +b and +a respectively. + */ + unsigned adjust = ac->w[0] & 1; + mp_cond_add_into(ac, ac, b, adjust); + mp_cond_add_into(bc, bc, a, adjust); + + /* + * Now ac is definitely even, so we divide it by two. + */ + mp_rshift_fixed_into(ac, ac, 1); + + /* + * Now unwind the subtraction, if there was one, by adding + * ac to bc. + */ + mp_cond_add_into(bc, bc, ac, both_odd); + + /* + * Undo the transformation of the input numbers, by + * multiplying a by 2 and then adding b to a (the latter + * only if both_odd). + */ + mp_lshift_fixed_into(a, a, 1); + mp_cond_add_into(a, a, b, both_odd); + + /* + * Finally, undo the swap. If we do swap, this also + * reverses the sign of the current result ac*a+bc*b. + */ + mp_cond_swap(a, b, swap); + mp_cond_swap(ac, bc, swap); + minus_d ^= swap; + } /* - * Undo the transformation of the input numbers, by - * multiplying a by 2 and then adding b to a (the latter - * only if both_odd). + * Now we expect to have recovered the input a,b (or rather, + * the versions of them divided by d). But we might find that + * our current result is -d instead of +d, that is, we have + * A',B' such that A'a - B'b = -d. + * + * In that situation, we set A = b-A' and B = a-B', giving us + * Aa-Bb = ab - A'a - ab + B'b = +1. */ - mp_lshift_fixed_into(a, a, 1); - mp_cond_add_into(a, a, b, both_odd); + mp_sub_into(tmp, b, ac); + mp_select_into(ac, ac, tmp, minus_d); + mp_sub_into(tmp, a, bc); + mp_select_into(bc, bc, tmp, minus_d); /* - * Finally, undo the swap. If we do swap, this also - * reverses the sign of the current result ac*a+bc*b. + * Now we really are done. Return the outputs. */ - mp_cond_swap(a, b, swap); - mp_cond_swap(ac, bc, swap); - minus_one ^= swap; - } + if (a_coeff_out) + mp_copy_into(a_coeff_out, ac); + if (b_coeff_out) + mp_copy_into(b_coeff_out, bc); - /* - * Now we expect to have recovered the input a,b. - */ - assert(mp_cmp_eq(a, a_in) & mp_cmp_eq(b, b_in)); - - /* - * But we might find that our current result is -1 instead of +1, - * that is, we have A',B' such that A'a - B'b = -1. - * - * In that situation, we set A = b-A' and B = a-B', giving us - * Aa-Bb = ab - A'a - ab + B'b = +1. - */ - mp_sub_into(tmp, b, ac); - mp_select_into(ac, ac, tmp, minus_one); - mp_sub_into(tmp, a, bc); - mp_select_into(bc, bc, tmp, minus_one); - - /* - * Now we really are done. Return the outputs. - */ - if (a_coeff_out) - mp_copy_into(a_coeff_out, ac); - if (b_coeff_out) - mp_copy_into(b_coeff_out, bc); + } mp_free(a); mp_free(b); @@ -1746,10 +1859,65 @@ static void mp_bezout_into(mp_int *a_coeff_out, mp_int *b_coeff_out, mp_int *mp_invert(mp_int *x, mp_int *m) { mp_int *result = mp_make_sized(m->nw); - mp_bezout_into(result, NULL, x, m); + mp_bezout_into(result, NULL, NULL, x, m); return result; } +void mp_gcd_into(mp_int *a, mp_int *b, mp_int *gcd, mp_int *A, mp_int *B) +{ + /* + * Identify shared factors of 2. To do this we OR the two numbers + * to get something whose lowest set bit is in the right place, + * remove all higher bits by ANDing it with its own negation, and + * use mp_get_nbits to find the location of the single remaining + * set bit. + */ + mp_int *tmp = mp_make_sized(size_t_max(a->nw, b->nw)); + for (size_t i = 0; i < tmp->nw; i++) + tmp->w[i] = mp_word(a, i) | mp_word(b, i); + BignumCarry carry = 1; + for (size_t i = 0; i < tmp->nw; i++) { + BignumInt negw; + BignumADC(negw, carry, 0, ~tmp->w[i], carry); + tmp->w[i] &= negw; + } + size_t shift = mp_get_nbits(tmp) - 1; + mp_free(tmp); + + /* + * Make copies of a,b with those shared factors of 2 divided off, + * so that at least one is odd (which is the precondition for + * mp_bezout_into). Compute the gcd of those. + */ + mp_int *as = mp_rshift_safe(a, shift); + mp_int *bs = mp_rshift_safe(b, shift); + mp_bezout_into(A, B, gcd, as, bs); + mp_free(as); + mp_free(bs); + + /* + * And finally shift the gcd back up (unless the caller didn't + * even ask for it), to put the shared factors of 2 back in. + */ + if (gcd) + mp_lshift_safe_in_place(gcd, shift); +} + +mp_int *mp_gcd(mp_int *a, mp_int *b) +{ + mp_int *gcd = mp_make_sized(size_t_min(a->nw, b->nw)); + mp_gcd_into(a, b, gcd, NULL, NULL); + return gcd; +} + +unsigned mp_coprime(mp_int *a, mp_int *b) +{ + mp_int *gcd = mp_gcd(a, b); + unsigned toret = mp_eq_integer(gcd, 1); + mp_free(gcd); + return toret; +} + static uint32_t recip_approx_32(uint32_t x) { /* @@ -1874,7 +2042,7 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out) */ size_t shift_up = 0; for (size_t i = BIGNUM_INT_BITS_BITS; i-- > 0;) { - size_t sl = 1 << i; /* left shift count */ + size_t sl = (size_t)1 << i; /* left shift count */ size_t sr = 64 - sl; /* complementary right-shift count */ /* Should we shift up? */ @@ -1911,7 +2079,7 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out) * instructions, e.g. by splitting up into cases. */ for (size_t i = BIGNUM_INT_BITS_BITS; i-- > 0;) { - size_t sl = 1 << i; /* left shift count */ + size_t sl = (size_t)1 << i; /* left shift count */ size_t sr = 64 - sl; /* complementary right-shift count */ /* Should we shift up? */ @@ -2091,6 +2259,82 @@ mp_int *mp_mod(mp_int *n, mp_int *d) return r; } +mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder_out) +{ + /* + * Allocate scratch space. + */ + mp_int **alloc, **powers, **newpowers, *scratch; + size_t nalloc = 2*(n+1)+1; + alloc = snewn(nalloc, mp_int *); + for (size_t i = 0; i < nalloc; i++) + alloc[i] = mp_make_sized(y->nw + 1); + powers = alloc; + newpowers = alloc + (n+1); + scratch = alloc[2*n+2]; + + /* + * We're computing the rounded-down nth root of y, i.e. the + * maximal x such that x^n <= y. We try to add 2^i to it for each + * possible value of i, starting from the largest one that might + * fit (i.e. such that 2^{n*i} fits in the size of y) downwards to + * i=0. + * + * We track all the smaller powers of x in the array 'powers'. In + * each iteration, if we update x, we update all of those values + * to match. + */ + mp_copy_integer_into(powers[0], 1); + for (size_t s = mp_max_bits(y) / n + 1; s-- > 0 ;) { + /* + * Let b = 2^s. We need to compute the powers (x+b)^i for each + * i, starting from our recorded values of x^i. + */ + for (size_t i = 0; i < n+1; i++) { + /* + * (x+b)^i = x^i + * + (i choose 1) x^{i-1} b + * + (i choose 2) x^{i-2} b^2 + * + ... + * + b^i + */ + uint16_t binom = 1; /* coefficient of b^i */ + mp_copy_into(newpowers[i], powers[i]); + for (size_t j = 0; j < i; j++) { + /* newpowers[i] += binom * powers[j] * 2^{(i-j)*s} */ + mp_mul_integer_into(scratch, powers[j], binom); + mp_lshift_fixed_into(scratch, scratch, (i-j) * s); + mp_add_into(newpowers[i], newpowers[i], scratch); + + uint32_t binom_mul = binom; + binom_mul *= (i-j); + binom_mul /= (j+1); + assert(binom_mul < 0x10000); + binom = binom_mul; + } + } + + /* + * Now, is the new value of x^n still <= y? If so, update. + */ + unsigned newbit = mp_cmp_hs(y, newpowers[n]); + for (size_t i = 0; i < n+1; i++) + mp_select_into(powers[i], powers[i], newpowers[i], newbit); + } + + if (remainder_out) + mp_sub_into(remainder_out, y, powers[n]); + + mp_int *root = mp_new(mp_max_bits(y) / n); + mp_copy_into(root, powers[1]); + + for (size_t i = 0; i < nalloc; i++) + mp_free(alloc[i]); + sfree(alloc); + + return root; +} + mp_int *mp_modmul(mp_int *x, mp_int *y, mp_int *modulus) { mp_int *product = mp_mul(x, y); @@ -2373,10 +2617,8 @@ mp_int *mp_random_bits_fn(size_t bits, random_read_fn_t random_read) return toret; } -mp_int *mp_random_in_range_fn(mp_int *lo, mp_int *hi, random_read_fn_t rf) +mp_int *mp_random_upto_fn(mp_int *limit, random_read_fn_t rf) { - mp_int *n_outcomes = mp_sub(hi, lo); - /* * It would be nice to generate our random numbers in such a way * as to make every possible outcome literally equiprobable. But @@ -2386,10 +2628,19 @@ mp_int *mp_random_in_range_fn(mp_int *lo, mp_int *hi, random_read_fn_t rf) * is acceptable on the grounds that you'd have to examine so many * outputs to even detect it. */ - mp_int *unreduced = mp_random_bits_fn(mp_max_bits(n_outcomes) + 128, rf); - mp_int *reduced = mp_mod(unreduced, n_outcomes); - mp_add_into(reduced, reduced, lo); + mp_int *unreduced = mp_random_bits_fn(mp_max_bits(limit) + 128, rf); + mp_int *reduced = mp_mod(unreduced, limit); mp_free(unreduced); - mp_free(n_outcomes); return reduced; } + +mp_int *mp_random_in_range_fn(mp_int *lo, mp_int *hi, random_read_fn_t rf) +{ + mp_int *n_outcomes = mp_sub(hi, lo); + mp_int *addend = mp_random_upto_fn(n_outcomes, rf); + mp_int *result = mp_make_sized(hi->nw); + mp_add_into(result, addend, lo); + mp_free(addend); + mp_free(n_outcomes); + return result; +} diff --git a/mpint.h b/mpint.h index e15312b..5611a00 100644 --- a/mpint.h +++ b/mpint.h @@ -176,9 +176,10 @@ mp_int *mp_max(mp_int *x, mp_int *y); void mp_dump(FILE *fp, const char *prefix, mp_int *x, const char *suffix); /* - * Overwrite one mp_int with another. + * Overwrite one mp_int with another, or with a plain integer. */ void mp_copy_into(mp_int *dest, mp_int *src); +void mp_copy_integer_into(mp_int *dest, uintmax_t n); /* * Conditional selection. Overwrites dest with either src0 or src1, @@ -256,6 +257,17 @@ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q, mp_int *r); mp_int *mp_div(mp_int *n, mp_int *d); mp_int *mp_mod(mp_int *x, mp_int *modulus); +/* + * Integer nth root. mp_nthroot returns the largest integer x such + * that x^n <= y, and if 'remainder' is non-NULL then it fills it with + * the residue (y - x^n). + * + * Currently, n has to be small enough that the largest binomial + * coefficient (n choose k) fits in 16 bits, which works out to at + * most 18. + */ +mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder); + /* * Trivially easy special case of mp_mod: reduce a number mod a power * of two. @@ -270,6 +282,25 @@ void mp_reduce_mod_2to(mp_int *x, size_t p); mp_int *mp_invert_mod_2to(mp_int *x, size_t p); mp_int *mp_invert(mp_int *x, mp_int *modulus); +/* + * Greatest common divisor. + * + * mp_gcd_into also returns a pair of Bezout coefficients, namely A,B + * such that a*A - b*B = gcd. (The minus sign is so that both returned + * coefficients can be positive.) + * + * You can pass any of mp_gcd_into's output pointers as NULL if you + * don't need that output value. + * + * mp_gcd is a wrapper with a less cumbersome API, for the case where + * the only output value you need is the gcd itself. mp_coprime is + * even easier, if all you care about is whether or not that gcd is 1. + */ +mp_int *mp_gcd(mp_int *a, mp_int *b); +void mp_gcd_into(mp_int *a, mp_int *b, + mp_int *gcd_out, mp_int *A_out, mp_int *B_out); +unsigned mp_coprime(mp_int *a, mp_int *b); + /* * System for taking square roots modulo an odd prime. * @@ -360,10 +391,17 @@ mp_int *mp_modadd(mp_int *x, mp_int *y, mp_int *modulus); mp_int *mp_modsub(mp_int *x, mp_int *y, mp_int *modulus); /* - * Shift an mp_int right by a given number of bits. The shift count is + * Shift an mp_int by a given number of bits. The shift count is * considered to be secret data, and as a result, the algorithm takes * O(n log n) time instead of the obvious O(n). + * + * There's no mp_lshift_safe, because the size of mp_int to allocate + * would not be able to avoid depending on the shift count. So if you + * need to behave independently of the size of a left shift, you have + * to know a bound on the space you'll need by some other means. */ +void mp_lshift_safe_into(mp_int *r, mp_int *x, size_t shift); +void mp_rshift_safe_into(mp_int *r, mp_int *x, size_t shift); mp_int *mp_rshift_safe(mp_int *x, size_t shift); /* @@ -376,6 +414,7 @@ mp_int *mp_rshift_safe(mp_int *x, size_t shift); */ void mp_lshift_fixed_into(mp_int *r, mp_int *a, size_t shift); void mp_rshift_fixed_into(mp_int *r, mp_int *x, size_t shift); +mp_int *mp_lshift_fixed(mp_int *x, size_t shift); mp_int *mp_rshift_fixed(mp_int *x, size_t shift); /* @@ -391,13 +430,16 @@ mp_int *mp_rshift_fixed(mp_int *x, size_t shift); * then _they_ have link-time dependencies on both modules.) * * mp_random_bits[_fn] returns an integer 0 <= n < 2^bits. + * mp_random_upto[_fn](limit) returns an integer 0 <= n < limit. * mp_random_in_range[_fn](lo,hi) returns an integer lo <= n < hi. */ typedef void (*random_read_fn_t)(void *, size_t); mp_int *mp_random_bits_fn(size_t bits, random_read_fn_t randfn); +mp_int *mp_random_upto_fn(mp_int *limit, random_read_fn_t randfn); mp_int *mp_random_in_range_fn( mp_int *lo_inclusive, mp_int *hi_exclusive, random_read_fn_t randfn); #define mp_random_bits(bits) mp_random_bits_fn(bits, random_read) +#define mp_random_upto(limit) mp_random_upto_fn(limit, random_read) #define mp_random_in_range(lo, hi) mp_random_in_range_fn(lo, hi, random_read) #endif /* PUTTY_MPINT_H */ diff --git a/mpint_i.h b/mpint_i.h index 70fdc6e..d37e75f 100644 --- a/mpint_i.h +++ b/mpint_i.h @@ -59,7 +59,11 @@ /* You can lower the BignumInt size by defining BIGNUM_OVERRIDE on the * command line to be your chosen max value of BIGNUM_INT_BITS_BITS */ -#define BB_OK(b) (!defined BIGNUM_OVERRIDE || BIGNUM_OVERRIDE >= b) +#if defined BIGNUM_OVERRIDE +#define BB_OK(b) ((b) <= BIGNUM_OVERRIDE) +#else +#define BB_OK(b) (1) +#endif #if defined __SIZEOF_INT128__ && BB_OK(6) @@ -315,3 +319,6 @@ struct MontyContext { */ mp_int *scratch; }; + +/* Functions shared between mpint.c and mpunsafe.c */ +mp_int *mp_make_sized(size_t nw); diff --git a/mpunsafe.c b/mpunsafe.c new file mode 100644 index 0000000..beec13f --- /dev/null +++ b/mpunsafe.c @@ -0,0 +1,57 @@ +#include +#include +#include + +#include "defs.h" +#include "misc.h" +#include "puttymem.h" + +#include "mpint.h" +#include "mpint_i.h" + +/* + * This global symbol is also defined in ssh2kex-client.c, to ensure + * that these unsafe non-constant-time mp_int functions can't end up + * accidentally linked in to any PuTTY tool that actually makes an SSH + * client connection. + * + * (Only _client_ connections, however. Uppity, being a test server + * only, is exempt.) + */ +const int deliberate_symbol_clash = 12345; + +static size_t mp_unsafe_words_needed(mp_int *x) +{ + size_t words = x->nw; + while (words > 1 && !x->w[words-1]) + words--; + return words; +} + +mp_int *mp_unsafe_shrink(mp_int *x) +{ + x->nw = mp_unsafe_words_needed(x); + /* This potentially leaves some allocated words between the new + * and old values of x->nw, which won't be wiped by mp_free now + * that x->nw doesn't mention that they exist. But we've just + * checked they're all zero, so we don't need to wipe them now + * either. */ + return x; +} + +mp_int *mp_unsafe_copy(mp_int *x) +{ + mp_int *copy = mp_make_sized(mp_unsafe_words_needed(x)); + mp_copy_into(copy, x); + return copy; +} + +uint32_t mp_unsafe_mod_integer(mp_int *x, uint32_t modulus) +{ + uint64_t accumulator = 0; + for (size_t i = mp_max_bytes(x); i-- > 0 ;) { + accumulator = 0x100 * accumulator + mp_get_byte(x, i); + accumulator %= modulus; + } + return accumulator; +} diff --git a/mpunsafe.h b/mpunsafe.h new file mode 100644 index 0000000..0b6ba3b --- /dev/null +++ b/mpunsafe.h @@ -0,0 +1,46 @@ +/* + * mpunsafe.h: functions that deal with mp_ints in ways that are *not* + * expected to be constant-time. Used during key generation, in which + * constant run time is a lost cause anyway. + * + * These functions are in a separate header, so that you can easily + * check that you're not calling them in the wrong context. They're + * also defined in a separate source file, which is only linked in to + * the key generation tools. Furthermore, that source file also + * defines a global symbol that intentionally conflicts with one + * defined in the SSH client code, so that any attempt to put these + * functions into the same binary as the live SSH client + * implementation will cause a link-time failure. They should only be + * linked into PuTTYgen and auxiliary test programs. + * + * Also, just in case those precautions aren't enough, all the unsafe + * functions have 'unsafe' in the name. + */ + +#ifndef PUTTY_MPINT_UNSAFE_H +#define PUTTY_MPINT_UNSAFE_H + +/* + * The most obvious unsafe thing you want to do with an mp_int is to + * get rid of leading zero words in its representation, so that its + * nominal size is as close as possible to its true size, and you + * don't waste any time processing it. + * + * mp_unsafe_shrink performs this operation in place, mutating the + * size field of the mp_int it's given. It returns the same pointer it + * was given. + * + * mp_unsafe_copy leaves the original mp_int alone and makes a new one + * with the minimal size. + */ +mp_int *mp_unsafe_shrink(mp_int *m); +mp_int *mp_unsafe_copy(mp_int *m); + +/* + * Compute the residue of x mod m. This is implemented in the most + * obvious way using the C % operator, which won't be constant-time on + * many C implementations. + */ +uint32_t mp_unsafe_mod_integer(mp_int *x, uint32_t m); + +#endif /* PUTTY_MPINT_UNSAFE_H */ diff --git a/network.h b/network.h index 355da2b..89419fb 100644 --- a/network.h +++ b/network.h @@ -44,25 +44,37 @@ struct Plug { const struct PlugVtable *vt; }; +typedef enum PlugLogType { + PLUGLOG_CONNECT_TRYING, + PLUGLOG_CONNECT_FAILED, + PLUGLOG_CONNECT_SUCCESS, + PLUGLOG_PROXY_MSG, +} PlugLogType; + struct PlugVtable { - void (*log)(Plug *p, int type, SockAddr *addr, int port, + void (*log)(Plug *p, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code); /* * Passes the client progress reports on the process of setting * up the connection. * - * - type==0 means we are about to try to connect to address - * `addr' (error_msg and error_code are ignored) - * - type==1 means we have failed to connect to address `addr' - * (error_msg and error_code are supplied). This is not a - * fatal error - we may well have other candidate addresses - * to fall back to. When it _is_ fatal, the closing() + * - PLUGLOG_CONNECT_TRYING means we are about to try to connect + * to address `addr' (error_msg and error_code are ignored) + * + * - PLUGLOG_CONNECT_FAILED means we have failed to connect to + * address `addr' (error_msg and error_code are supplied). This + * is not a fatal error - we may well have other candidate + * addresses to fall back to. When it _is_ fatal, the closing() * function will be called. - * - type==2 means that error_msg contains a line of generic - * logging information about setting up the connection. This - * will typically be a wodge of standard-error output from a - * proxy command, so the receiver should probably prefix it to - * indicate this. + * + * - PLUGLOG_CONNECT_SUCCESS means we have succeeded in + * connecting to address `addr'. + * + * - PLUGLOG_PROXY_MSG means that error_msg contains a line of + * logging information from whatever the connection is being + * proxied through. This will typically be a wodge of + * standard-error output from a local proxy command, so the + * receiver should probably prefix it to indicate this. */ void (*closing) (Plug *p, const char *error_msg, int error_code, bool calling_back); @@ -264,8 +276,13 @@ char *get_hostname(void); /* * Trivial socket implementation which just stores an error. Found in * errsock.c. + * + * The consume_string variant takes an already-formatted dynamically + * allocated string, and takes over ownership of that string. */ -Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...); +Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...) + PRINTF_LIKE(2, 3); +Socket *new_error_socket_consume_string(Plug *plug, char *errmsg); /* * Trivial plug that does absolutely nothing. Found in nullplug.c. @@ -282,7 +299,7 @@ extern Plug *const nullplug; * Exports from be_misc.c. */ void backend_socket_log(Seat *seat, LogContext *logctx, - int type, SockAddr *addr, int port, + PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code, Conf *conf, bool session_started); diff --git a/nocmdline.c b/nocmdline.c index 05a79ba..e4c6d08 100644 --- a/nocmdline.c +++ b/nocmdline.c @@ -35,8 +35,3 @@ int cmdline_process_param(const char *p, char *value, { unreachable("cmdline_process_param should never be called"); } - -/* - * This variable will be referred to, so it has to exist. It's ignored. - */ -int cmdline_tooltype = 0; diff --git a/noprint.c b/noprint.c index 2a98908..941da68 100644 --- a/noprint.c +++ b/noprint.c @@ -16,7 +16,7 @@ printer_job *printer_start_job(char *printer) return NULL; } -void printer_job_data(printer_job *pj, void *data, size_t len) +void printer_job_data(printer_job *pj, const void *data, size_t len) { } diff --git a/noproxy.c b/noproxy.c new file mode 100644 index 0000000..1d37293 --- /dev/null +++ b/noproxy.c @@ -0,0 +1,32 @@ +/* + * noproxy.c: an alternative to proxy.c, for use by auxiliary programs + * that need to make network connections but don't want to include all + * the full-on support for endless network proxies (and its + * configuration requirements). Implements the primary APIs of + * proxy.c, but maps them straight to the underlying network layer. + */ + +#include "putty.h" +#include "network.h" +#include "proxy.h" + +SockAddr *name_lookup(const char *host, int port, char **canonicalname, + Conf *conf, int addressfamily, LogContext *logctx, + const char *reason) +{ + return sk_namelookup(host, canonicalname, addressfamily); +} + +Socket *new_connection(SockAddr *addr, const char *hostname, + int port, bool privport, + bool oobinline, bool nodelay, bool keepalive, + Plug *plug, Conf *conf) +{ + return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug); +} + +Socket *new_listener(const char *srcaddr, int port, Plug *plug, + bool local_host_only, Conf *conf, int addressfamily) +{ + return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily); +} diff --git a/norand.c b/norand.c new file mode 100644 index 0000000..2ad9f66 --- /dev/null +++ b/norand.c @@ -0,0 +1,22 @@ +/* + * Stub implementations of RNG functions for applications without an RNG. + */ + +#include "putty.h" + +void random_read(void *out, size_t size) +{ + unreachable("Random numbers are not available in this application"); +} + +void random_save_seed(void) +{ +} + +void random_destroy_seed(void) +{ +} + +void noise_ultralight(NoiseSourceId id, unsigned long data) +{ +} diff --git a/noshare.c b/noshare.c index bc6d0ef..c45634c 100644 --- a/noshare.c +++ b/noshare.c @@ -15,7 +15,7 @@ int platform_ssh_share(const char *name, Conf *conf, Plug *downplug, Plug *upplug, Socket **sock, char **logtext, char **ds_err, char **us_err, - int can_upstream, int can_downstream) + bool can_upstream, bool can_downstream) { return SHARE_NONE; } diff --git a/nullplug.c b/nullplug.c index 81376d3..953f034 100644 --- a/nullplug.c +++ b/nullplug.c @@ -7,8 +7,8 @@ #include "putty.h" -static void nullplug_socket_log(Plug *plug, int type, SockAddr *addr, int port, - const char *error_msg, int error_code) +static void nullplug_socket_log(Plug *plug, PlugLogType type, SockAddr *addr, + int port, const char *err_msg, int err_code) { } @@ -27,11 +27,10 @@ static void nullplug_sent(Plug *plug, size_t bufsize) } static const PlugVtable nullplug_plugvt = { - nullplug_socket_log, - nullplug_closing, - nullplug_receive, - nullplug_sent, - NULL + .log = nullplug_socket_log, + .closing = nullplug_closing, + .receive = nullplug_receive, + .sent = nullplug_sent, }; static Plug nullplug_plug = { &nullplug_plugvt }; diff --git a/pageant.c b/pageant.c index c5cef93..8ca9310 100644 --- a/pageant.c +++ b/pageant.c @@ -29,674 +29,1396 @@ void random_read(void *buf, size_t size) static bool pageant_local = false; -/* - * rsakeys stores SSH-1 RSA keys. ssh2keys stores all SSH-2 keys. - */ -static tree234 *rsakeys, *ssh2keys; +struct PageantClientDialogId { + int dummy; +}; -/* - * Key comparison function for the 2-3-4 tree of RSA keys. - */ -static int cmpkeys_rsa(void *av, void *bv) -{ - RSAKey *a = (RSAKey *) av; - RSAKey *b = (RSAKey *) bv; +typedef struct PageantKeySort PageantKeySort; +typedef struct PageantKey PageantKey; +typedef struct PageantAsyncOp PageantAsyncOp; +typedef struct PageantAsyncOpVtable PageantAsyncOpVtable; +typedef struct PageantClientRequestNode PageantClientRequestNode; +typedef struct PageantKeyRequestNode PageantKeyRequestNode; - return ((int)mp_cmp_hs(a->modulus, b->modulus) - - (int)mp_cmp_hs(b->modulus, a->modulus)); +struct PageantClientRequestNode { + PageantClientRequestNode *prev, *next; +}; +struct PageantKeyRequestNode { + PageantKeyRequestNode *prev, *next; +}; + +struct PageantClientInfo { + PageantClient *pc; /* goes to NULL when client is unregistered */ + PageantClientRequestNode head; +}; + +struct PageantAsyncOp { + const PageantAsyncOpVtable *vt; + PageantClientInfo *info; + PageantClientRequestNode cr; + PageantClientRequestId *reqid; +}; +struct PageantAsyncOpVtable { + void (*coroutine)(PageantAsyncOp *pao); + void (*free)(PageantAsyncOp *pao); +}; +static inline void pageant_async_op_coroutine(PageantAsyncOp *pao) +{ pao->vt->coroutine(pao); } +static inline void pageant_async_op_free(PageantAsyncOp *pao) +{ + delete_callbacks_for_context(pao); + pao->vt->free(pao); +} +static inline void pageant_async_op_unlink(PageantAsyncOp *pao) +{ + pao->cr.prev->next = pao->cr.next; + pao->cr.next->prev = pao->cr.prev; +} +static inline void pageant_async_op_unlink_and_free(PageantAsyncOp *pao) +{ + pageant_async_op_unlink(pao); + pageant_async_op_free(pao); +} +static void pageant_async_op_callback(void *vctx) +{ + pageant_async_op_coroutine((PageantAsyncOp *)vctx); } /* - * Key comparison function for looking up a blob in the 2-3-4 tree - * of SSH-2 keys. + * Master list of all the keys we have stored, in any form at all. */ -static int cmpkeys_ssh2_asymm(void *av, void *bv) -{ - ptrlen *ablob = (ptrlen *) av; - ssh2_userkey *b = (ssh2_userkey *) bv; - strbuf *bblob; - int i, c; +static tree234 *keytree; +struct PageantKeySort { + /* Prefix of the main PageantKey structure which contains all the + * data that the sorting order depends on. Also simple enough that + * you can construct one for lookup purposes. */ + int ssh_version; /* 1 or 2; primary sort key */ + ptrlen public_blob; /* secondary sort key */ +}; +struct PageantKey { + PageantKeySort sort; + strbuf *public_blob; /* the true owner of sort.public_blob */ + char *comment; /* stored separately, whether or not in rkey/skey */ + union { + RSAKey *rkey; /* if ssh_version == 1 */ + ssh2_userkey *skey; /* if ssh_version == 2 */ + }; + strbuf *encrypted_key_file; + bool decryption_prompt_active; + PageantKeyRequestNode blocked_requests; + PageantClientDialogId dlgid; +}; - /* - * Compare purely by public blob. - */ - bblob = strbuf_new(); - ssh_key_public_blob(b->key, BinarySink_UPCAST(bblob)); - - c = 0; - for (i = 0; i < ablob->len && i < bblob->len; i++) { - unsigned char abyte = ((unsigned char *)ablob->ptr)[i]; - if (abyte < bblob->u[i]) { - c = -1; - break; - } else if (abyte > bblob->u[i]) { - c = +1; - break; - } +typedef struct PageantSignOp PageantSignOp; +struct PageantSignOp { + PageantKey *pk; + strbuf *data_to_sign; + unsigned flags; + int crLine; + unsigned char failure_type; + + PageantKeyRequestNode pkr; + PageantAsyncOp pao; +}; + +/* Master lock that indicates whether a GUI request is currently in + * progress */ +static bool gui_request_in_progress = false; + +static void failure(PageantClient *pc, PageantClientRequestId *reqid, + strbuf *sb, unsigned char type, const char *fmt, ...); +static void fail_requests_for_key(PageantKey *pk, const char *reason); +static PageantKey *pageant_nth_key(int ssh_version, int i); + +static void pk_free(PageantKey *pk) +{ + if (pk->public_blob) strbuf_free(pk->public_blob); + sfree(pk->comment); + if (pk->sort.ssh_version == 1 && pk->rkey) { + freersakey(pk->rkey); + sfree(pk->rkey); + } + if (pk->sort.ssh_version == 2 && pk->skey) { + sfree(pk->skey->comment); + ssh_key_free(pk->skey->key); + sfree(pk->skey); } - if (c == 0 && i < ablob->len) - c = +1; /* a is longer */ - if (c == 0 && i < bblob->len) - c = -1; /* a is longer */ + if (pk->encrypted_key_file) strbuf_free(pk->encrypted_key_file); + fail_requests_for_key(pk, "key deleted from Pageant while signing " + "request was pending"); + sfree(pk); +} - strbuf_free(bblob); +static int cmpkeys(void *av, void *bv) +{ + PageantKeySort *a = (PageantKeySort *)av, *b = (PageantKeySort *)bv; - return c; + if (a->ssh_version != b->ssh_version) + return a->ssh_version < b->ssh_version ? -1 : +1; + else + return ptrlen_strcmp(a->public_blob, b->public_blob); } -/* - * Main key comparison function for the 2-3-4 tree of SSH-2 keys. - */ -static int cmpkeys_ssh2(void *av, void *bv) -{ - ssh2_userkey *a = (ssh2_userkey *) av; - strbuf *ablob; - ptrlen apl; - int toret; - - ablob = strbuf_new(); - ssh_key_public_blob(a->key, BinarySink_UPCAST(ablob)); - apl.ptr = ablob->u; - apl.len = ablob->len; - toret = cmpkeys_ssh2_asymm(&apl, bv); - strbuf_free(ablob); +static inline PageantKeySort keysort(int version, ptrlen blob) +{ + PageantKeySort sort; + sort.ssh_version = version; + sort.public_blob = blob; + return sort; +} + +static strbuf *makeblob1(RSAKey *rkey) +{ + strbuf *blob = strbuf_new(); + rsa_ssh1_public_blob(BinarySink_UPCAST(blob), rkey, + RSA_SSH1_EXPONENT_FIRST); + return blob; +} + +static strbuf *makeblob2(ssh2_userkey *skey) +{ + strbuf *blob = strbuf_new(); + ssh_key_public_blob(skey->key, BinarySink_UPCAST(blob)); + return blob; +} + +static PageantKey *findkey1(RSAKey *reqkey) +{ + strbuf *blob = makeblob1(reqkey); + PageantKeySort sort = keysort(1, ptrlen_from_strbuf(blob)); + PageantKey *toret = find234(keytree, &sort, NULL); + strbuf_free(blob); return toret; } -void pageant_make_keylist1(BinarySink *bs) +static PageantKey *findkey2(ptrlen blob) { - int i; - RSAKey *key; + PageantKeySort sort = keysort(2, blob); + return find234(keytree, &sort, NULL); +} + +static int find_first_key_for_version(int ssh_version) +{ + PageantKeySort sort = keysort(ssh_version, PTRLEN_LITERAL("")); + int pos; + if (findrelpos234(keytree, &sort, NULL, REL234_GE, &pos)) + return pos; + return count234(keytree); +} + +static int count_keys(int ssh_version) +{ + return (find_first_key_for_version(ssh_version + 1) - + find_first_key_for_version(ssh_version)); +} +int pageant_count_ssh1_keys(void) { return count_keys(1); } +int pageant_count_ssh2_keys(void) { return count_keys(2); } + +static bool pageant_add_ssh1_key(RSAKey *rkey) +{ + PageantKey *pk = snew(PageantKey); + memset(pk, 0, sizeof(PageantKey)); + pk->sort.ssh_version = 1; + pk->public_blob = makeblob1(rkey); + pk->sort.public_blob = ptrlen_from_strbuf(pk->public_blob); + pk->blocked_requests.next = pk->blocked_requests.prev = + &pk->blocked_requests; + + if (add234(keytree, pk) == pk) { + pk->rkey = rkey; + if (rkey->comment) + pk->comment = dupstr(rkey->comment); + return true; + } else { + pk_free(pk); + return false; + } +} + +static bool pageant_add_ssh2_key(ssh2_userkey *skey) +{ + PageantKey *pk = snew(PageantKey); + memset(pk, 0, sizeof(PageantKey)); + pk->sort.ssh_version = 2; + pk->public_blob = makeblob2(skey); + pk->sort.public_blob = ptrlen_from_strbuf(pk->public_blob); + pk->blocked_requests.next = pk->blocked_requests.prev = + &pk->blocked_requests; + + PageantKey *pk_in_tree = add234(keytree, pk); + if (pk_in_tree == pk) { + /* The key wasn't in the tree at all, and we've just added it. */ + pk->skey = skey; + if (skey->comment) + pk->comment = dupstr(skey->comment); + return true; + } else if (!pk_in_tree->skey) { + /* The key was only stored encrypted, and now we have an + * unencrypted version to add to the existing record. */ + pk_in_tree->skey = skey; + pk_free(pk); + return true; + } else { + /* The key was already in the tree in full. */ + pk_free(pk); + return false; + } +} - put_uint32(bs, count234(rsakeys)); - for (i = 0; NULL != (key = index234(rsakeys, i)); i++) { - rsa_ssh1_public_blob(bs, key, RSA_SSH1_EXPONENT_FIRST); - put_stringz(bs, key->comment); +static void remove_all_keys(int ssh_version) +{ + int start = find_first_key_for_version(ssh_version); + int end = find_first_key_for_version(ssh_version + 1); + while (end > start) { + PageantKey *pk = delpos234(keytree, --end); + assert(pk->sort.ssh_version == ssh_version); + pk_free(pk); } } -void pageant_make_keylist2(BinarySink *bs) +static void list_keys(BinarySink *bs, int ssh_version, bool extended) { int i; - ssh2_userkey *key; + PageantKey *pk; - put_uint32(bs, count234(ssh2keys)); - for (i = 0; NULL != (key = index234(ssh2keys, i)); i++) { - strbuf *blob = strbuf_new(); - ssh_key_public_blob(key->key, BinarySink_UPCAST(blob)); - put_stringsb(bs, blob); - put_stringz(bs, key->comment); + put_uint32(bs, count_keys(ssh_version)); + for (i = find_first_key_for_version(ssh_version); + NULL != (pk = index234(keytree, i)); i++) { + if (pk->sort.ssh_version != ssh_version) + break; + + if (ssh_version > 1) + put_stringpl(bs, pk->sort.public_blob); + else + put_datapl(bs, pk->sort.public_blob); /* no header */ + + put_stringpl(bs, ptrlen_from_asciz(pk->comment)); + + if (extended) { + /* + * Append to each key entry a string containing extension + * data. This string begins with a flags word, and may in + * future contain further data if flag bits are set saying + * that it does. Hence, it's wrapped in a containing + * string, so that clients that only partially understand + * it can still find the parts they do understand. + */ + strbuf *sb = strbuf_new(); + + uint32_t flags = 0; + if (!pk->skey) + flags |= LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY; + if (pk->encrypted_key_file) + flags |= LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE; + put_uint32(sb, flags); + + put_stringsb(bs, sb); + } } } -static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...) -#ifdef __GNUC__ -__attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 3, 4))) -#endif - ; +void pageant_make_keylist1(BinarySink *bs) { list_keys(bs, 1, false); } +void pageant_make_keylist2(BinarySink *bs) { list_keys(bs, 2, false); } +void pageant_make_keylist_extended(BinarySink *bs) { list_keys(bs, 2, true); } -static void plog(void *logctx, pageant_logfn_t logfn, const char *fmt, ...) +void pageant_register_client(PageantClient *pc) { - /* - * This is the wrapper that takes a variadic argument list and - * turns it into the va_list that the log function really expects. - * It's safe to call this with logfn==NULL, because we - * double-check that below; but if you're going to do lots of work - * before getting here (such as looping, or hashing things) then - * you should probably check logfn manually before doing that. - */ - if (logfn) { + pc->info = snew(PageantClientInfo); + pc->info->pc = pc; + pc->info->head.prev = pc->info->head.next = &pc->info->head; +} + +void pageant_unregister_client(PageantClient *pc) +{ + PageantClientInfo *info = pc->info; + assert(info); + assert(info->pc == pc); + + while (pc->info->head.next != &pc->info->head) { + PageantAsyncOp *pao = container_of(pc->info->head.next, + PageantAsyncOp, cr); + pageant_async_op_unlink_and_free(pao); + } + + sfree(pc->info); +} + +static PRINTF_LIKE(5, 6) void failure( + PageantClient *pc, PageantClientRequestId *reqid, strbuf *sb, + unsigned char type, const char *fmt, ...) +{ + strbuf_clear(sb); + put_byte(sb, type); + if (!pc->suppress_logging) { va_list ap; va_start(ap, fmt); - logfn(logctx, fmt, ap); + char *msg = dupvprintf(fmt, ap); va_end(ap); + pageant_client_log(pc, reqid, "reply: SSH_AGENT_FAILURE (%s)", msg); + sfree(msg); + } +} + +static void signop_link(PageantSignOp *so) +{ + assert(!so->pkr.prev); + assert(!so->pkr.next); + + so->pkr.prev = so->pk->blocked_requests.prev; + so->pkr.next = &so->pk->blocked_requests; + so->pkr.prev->next = &so->pkr; + so->pkr.next->prev = &so->pkr; +} + +static void signop_unlink(PageantSignOp *so) +{ + if (so->pkr.next) { + assert(so->pkr.prev); + so->pkr.next->prev = so->pkr.prev; + so->pkr.prev->next = so->pkr.next; + } else { + assert(!so->pkr.prev); + } +} + +static void signop_free(PageantAsyncOp *pao) +{ + PageantSignOp *so = container_of(pao, PageantSignOp, pao); + strbuf_free(so->data_to_sign); + sfree(so); +} + +static bool request_passphrase(PageantClient *pc, PageantKey *pk) +{ + if (!pk->decryption_prompt_active) { + assert(!gui_request_in_progress); + + bool created_dlg = pageant_client_ask_passphrase( + pc, &pk->dlgid, pk->comment); + + if (!created_dlg) + return false; + + gui_request_in_progress = true; + pk->decryption_prompt_active = true; + } + + return true; +} + +static void signop_coroutine(PageantAsyncOp *pao) +{ + PageantSignOp *so = container_of(pao, PageantSignOp, pao); + strbuf *response; + + crBegin(so->crLine); + + while (!so->pk->skey && gui_request_in_progress) + crReturnV; + + if (!so->pk->skey) { + assert(so->pk->encrypted_key_file); + + if (!request_passphrase(so->pao.info->pc, so->pk)) { + response = strbuf_new(); + failure(so->pao.info->pc, so->pao.reqid, response, + so->failure_type, "on-demand decryption could not " + "prompt for a passphrase"); + goto respond; + } + + signop_link(so); + crReturnV; + signop_unlink(so); + } + + uint32_t supported_flags = ssh_key_alg(so->pk->skey->key)->supported_flags; + if (so->flags & ~supported_flags) { + /* + * We MUST reject any message containing flags we don't + * understand. + */ + response = strbuf_new(); + failure(so->pao.info->pc, so->pao.reqid, response, so->failure_type, + "unsupported flag bits 0x%08"PRIx32, + so->flags & ~supported_flags); + goto respond; + } + + char *invalid = ssh_key_invalid(so->pk->skey->key, so->flags); + if (invalid) { + response = strbuf_new(); + failure(so->pao.info->pc, so->pao.reqid, response, so->failure_type, + "key invalid: %s", invalid); + sfree(invalid); + goto respond; + } + + strbuf *signature = strbuf_new(); + ssh_key_sign(so->pk->skey->key, ptrlen_from_strbuf(so->data_to_sign), + so->flags, BinarySink_UPCAST(signature)); + + response = strbuf_new(); + put_byte(response, SSH2_AGENT_SIGN_RESPONSE); + put_stringsb(response, signature); + + respond: + pageant_client_got_response(so->pao.info->pc, so->pao.reqid, + ptrlen_from_strbuf(response)); + strbuf_free(response); + + pageant_async_op_unlink_and_free(&so->pao); + crFinishFreedV; +} + +static const PageantAsyncOpVtable signop_vtable = { + .coroutine = signop_coroutine, + .free = signop_free, +}; + +static void fail_requests_for_key(PageantKey *pk, const char *reason) +{ + while (pk->blocked_requests.next != &pk->blocked_requests) { + PageantSignOp *so = container_of(pk->blocked_requests.next, + PageantSignOp, pkr); + signop_unlink(so); + strbuf *sb = strbuf_new(); + failure(so->pao.info->pc, so->pao.reqid, sb, so->failure_type, + "%s", reason); + pageant_client_got_response(so->pao.info->pc, so->pao.reqid, + ptrlen_from_strbuf(sb)); + strbuf_free(sb); + pageant_async_op_unlink_and_free(&so->pao); + } +} + +static void unblock_requests_for_key(PageantKey *pk) +{ + for (PageantKeyRequestNode *pkr = pk->blocked_requests.next; + pkr != &pk->blocked_requests; pkr = pkr->next) { + PageantSignOp *so = container_of(pk->blocked_requests.next, + PageantSignOp, pkr); + queue_toplevel_callback(pageant_async_op_callback, &so->pao); + } +} + +void pageant_passphrase_request_success(PageantClientDialogId *dlgid, + ptrlen passphrase) +{ + PageantKey *pk = container_of(dlgid, PageantKey, dlgid); + + assert(gui_request_in_progress); + gui_request_in_progress = false; + pk->decryption_prompt_active = false; + + if (!pk->skey) { + const char *error; + + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf( + pk->encrypted_key_file)); + + strbuf *ppsb = strbuf_new_nm(); + put_datapl(ppsb, passphrase); + + pk->skey = ppk_load_s(src, ppsb->s, &error); + + strbuf_free(ppsb); + + if (!pk->skey) { + fail_requests_for_key(pk, "unable to decrypt key"); + return; + } else if (pk->skey == SSH2_WRONG_PASSPHRASE) { + pk->skey = NULL; + + /* + * Find a PageantClient to use for another attempt at + * request_passphrase. + */ + PageantKeyRequestNode *pkr = pk->blocked_requests.next; + if (pkr == &pk->blocked_requests) { + /* + * Special case: if all the requests have gone away at + * this point, we need not bother putting up a request + * at all any more. + */ + return; + } + + PageantSignOp *so = container_of(pk->blocked_requests.next, + PageantSignOp, pkr); + + pk->decryption_prompt_active = false; + if (!request_passphrase(so->pao.info->pc, pk)) { + fail_requests_for_key(pk, "unable to continue creating " + "passphrase prompts"); + } + return; + } else { + keylist_update(); + } + } + + unblock_requests_for_key(pk); +} + +void pageant_passphrase_request_refused(PageantClientDialogId *dlgid) +{ + PageantKey *pk = container_of(dlgid, PageantKey, dlgid); + + assert(gui_request_in_progress); + gui_request_in_progress = false; + pk->decryption_prompt_active = false; + + fail_requests_for_key(pk, "user refused to supply passphrase"); +} + +typedef struct PageantImmOp PageantImmOp; +struct PageantImmOp { + int crLine; + strbuf *response; + + PageantAsyncOp pao; +}; + +static void immop_free(PageantAsyncOp *pao) +{ + PageantImmOp *io = container_of(pao, PageantImmOp, pao); + if (io->response) + strbuf_free(io->response); + sfree(io); +} + +static void immop_coroutine(PageantAsyncOp *pao) +{ + PageantImmOp *io = container_of(pao, PageantImmOp, pao); + + crBegin(io->crLine); + + if (0) crReturnV; + + pageant_client_got_response(io->pao.info->pc, io->pao.reqid, + ptrlen_from_strbuf(io->response)); + pageant_async_op_unlink_and_free(&io->pao); + crFinishFreedV; +} + +static const PageantAsyncOpVtable immop_vtable = { + .coroutine = immop_coroutine, + .free = immop_free, +}; + +static bool reencrypt_key(PageantKey *pk) +{ + if (pk->sort.ssh_version != 2) { + /* + * We don't support storing SSH-1 keys in encrypted form at + * all. + */ + return false; + } + + if (!pk->encrypted_key_file) { + /* + * We can't re-encrypt a key if it doesn't have an encrypted + * form. (We could make one up, of course - but with what + * passphrase that we could expect the user to know later?) + */ + return false; + } + + /* Only actually free pk->skey if it exists. But we return success + * regardless, so that 'please ensure this key isn't stored + * decrypted' is idempotent. */ + if (pk->skey) { + sfree(pk->skey->comment); + ssh_key_free(pk->skey->key); + sfree(pk->skey); + pk->skey = NULL; } + + return true; } -void pageant_handle_msg(BinarySink *bs, - const void *msgdata, int msglen, - void *logctx, pageant_logfn_t logfn) +#define DECL_EXT_ENUM(id, name) id, +enum Extension { KNOWN_EXTENSIONS(DECL_EXT_ENUM) EXT_UNKNOWN }; +#define DEF_EXT_NAMES(id, name) PTRLEN_DECL_LITERAL(name), +static const ptrlen extension_names[] = { KNOWN_EXTENSIONS(DEF_EXT_NAMES) }; + +static PageantAsyncOp *pageant_make_op( + PageantClient *pc, PageantClientRequestId *reqid, ptrlen msgpl) { BinarySource msg[1]; + strbuf *sb = strbuf_new_nm(); + unsigned char failure_type = SSH_AGENT_FAILURE; int type; - BinarySource_BARE_INIT(msg, msgdata, msglen); +#define fail(...) failure(pc, reqid, sb, failure_type, __VA_ARGS__) + + BinarySource_BARE_INIT_PL(msg, msgpl); type = get_byte(msg); if (get_err(msg)) { - pageant_failure_msg(bs, "message contained no type code", - logctx, logfn); - return; + fail("message contained no type code"); + goto responded; } switch (type) { - case SSH1_AGENTC_REQUEST_RSA_IDENTITIES: + case SSH1_AGENTC_REQUEST_RSA_IDENTITIES: { /* * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER. */ - { - plog(logctx, logfn, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES"); + pageant_client_log(pc, reqid, + "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES"); - put_byte(bs, SSH1_AGENT_RSA_IDENTITIES_ANSWER); - pageant_make_keylist1(bs); + put_byte(sb, SSH1_AGENT_RSA_IDENTITIES_ANSWER); + pageant_make_keylist1(BinarySink_UPCAST(sb)); - plog(logctx, logfn, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER"); - if (logfn) { /* skip this loop if not logging */ - int i; - RSAKey *rkey; - for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) { - char *fingerprint = rsa_ssh1_fingerprint(rkey); - plog(logctx, logfn, "returned key: %s", fingerprint); - sfree(fingerprint); - } + pageant_client_log(pc, reqid, + "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER"); + if (!pc->suppress_logging) { + int i; + PageantKey *pk; + for (i = 0; NULL != (pk = pageant_nth_key(1, i)); i++) { + char *fingerprint = rsa_ssh1_fingerprint(pk->rkey); + pageant_client_log(pc, reqid, "returned key: %s", + fingerprint); + sfree(fingerprint); } } break; - case SSH2_AGENTC_REQUEST_IDENTITIES: + } + case SSH2_AGENTC_REQUEST_IDENTITIES: { /* * Reply with SSH2_AGENT_IDENTITIES_ANSWER. */ - { - plog(logctx, logfn, "request: SSH2_AGENTC_REQUEST_IDENTITIES"); + pageant_client_log(pc, reqid, + "request: SSH2_AGENTC_REQUEST_IDENTITIES"); - put_byte(bs, SSH2_AGENT_IDENTITIES_ANSWER); - pageant_make_keylist2(bs); + put_byte(sb, SSH2_AGENT_IDENTITIES_ANSWER); + pageant_make_keylist2(BinarySink_UPCAST(sb)); - plog(logctx, logfn, "reply: SSH2_AGENT_IDENTITIES_ANSWER"); - if (logfn) { /* skip this loop if not logging */ - int i; - ssh2_userkey *skey; - for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) { - char *fingerprint = ssh2_fingerprint(skey->key); - plog(logctx, logfn, "returned key: %s %s", - fingerprint, skey->comment); - sfree(fingerprint); - } + pageant_client_log(pc, reqid, "reply: SSH2_AGENT_IDENTITIES_ANSWER"); + if (!pc->suppress_logging) { + int i; + PageantKey *pk; + for (i = 0; NULL != (pk = pageant_nth_key(2, i)); i++) { + char *fingerprint = ssh2_fingerprint_blob( + ptrlen_from_strbuf(pk->public_blob), SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "returned key: %s %s", + fingerprint, pk->comment); + sfree(fingerprint); } } break; - case SSH1_AGENTC_RSA_CHALLENGE: + } + case SSH1_AGENTC_RSA_CHALLENGE: { /* * Reply with either SSH1_AGENT_RSA_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ - { - RSAKey reqkey, *key; - mp_int *challenge, *response; - ptrlen session_id; - unsigned response_type; - unsigned char response_md5[16]; - int i; - - plog(logctx, logfn, "request: SSH1_AGENTC_RSA_CHALLENGE"); - - response = NULL; - memset(&reqkey, 0, sizeof(reqkey)); - - get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST); - challenge = get_mp_ssh1(msg); - session_id = get_data(msg, 16); - response_type = get_uint32(msg); + RSAKey reqkey; + PageantKey *pk; + mp_int *challenge, *response; + ptrlen session_id; + unsigned response_type; + unsigned char response_md5[16]; + int i; + + pageant_client_log(pc, reqid, "request: SSH1_AGENTC_RSA_CHALLENGE"); + + response = NULL; + memset(&reqkey, 0, sizeof(reqkey)); + + get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST); + challenge = get_mp_ssh1(msg); + session_id = get_data(msg, 16); + response_type = get_uint32(msg); + + if (get_err(msg)) { + fail("unable to decode request"); + goto challenge1_cleanup; + } + if (response_type != 1) { + fail("response type other than 1 not supported"); + goto challenge1_cleanup; + } - if (get_err(msg)) { - pageant_failure_msg(bs, "unable to decode request", - logctx, logfn); - goto challenge1_cleanup; - } - if (response_type != 1) { - pageant_failure_msg( - bs, "response type other than 1 not supported", - logctx, logfn); - goto challenge1_cleanup; - } + if (!pc->suppress_logging) { + char *fingerprint; + reqkey.comment = NULL; + fingerprint = rsa_ssh1_fingerprint(&reqkey); + pageant_client_log(pc, reqid, "requested key: %s", fingerprint); + sfree(fingerprint); + } - if (logfn) { - char *fingerprint; - reqkey.comment = NULL; - fingerprint = rsa_ssh1_fingerprint(&reqkey); - plog(logctx, logfn, "requested key: %s", fingerprint); - sfree(fingerprint); - } - if ((key = find234(rsakeys, &reqkey, NULL)) == NULL) { - pageant_failure_msg(bs, "key not found", logctx, logfn); - goto challenge1_cleanup; - } - response = rsa_ssh1_decrypt(challenge, key); + if ((pk = findkey1(&reqkey)) == NULL) { + fail("key not found"); + goto challenge1_cleanup; + } + response = rsa_ssh1_decrypt(challenge, pk->rkey); - { - ssh_hash *h = ssh_hash_new(&ssh_md5); - for (i = 0; i < 32; i++) - put_byte(h, mp_get_byte(response, 31 - i)); - put_datapl(h, session_id); - ssh_hash_final(h, response_md5); - } + { + ssh_hash *h = ssh_hash_new(&ssh_md5); + for (i = 0; i < 32; i++) + put_byte(h, mp_get_byte(response, 31 - i)); + put_datapl(h, session_id); + ssh_hash_final(h, response_md5); + } - put_byte(bs, SSH1_AGENT_RSA_RESPONSE); - put_data(bs, response_md5, 16); + put_byte(sb, SSH1_AGENT_RSA_RESPONSE); + put_data(sb, response_md5, 16); - plog(logctx, logfn, "reply: SSH1_AGENT_RSA_RESPONSE"); + pageant_client_log(pc, reqid, "reply: SSH1_AGENT_RSA_RESPONSE"); challenge1_cleanup: - if (response) - mp_free(response); - mp_free(challenge); - freersakey(&reqkey); - } + if (response) + mp_free(response); + mp_free(challenge); + freersakey(&reqkey); break; - case SSH2_AGENTC_SIGN_REQUEST: + } + case SSH2_AGENTC_SIGN_REQUEST: { /* * Reply with either SSH2_AGENT_SIGN_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ - { - ssh2_userkey *key; - ptrlen keyblob, sigdata; - strbuf *signature; - uint32_t flags, supported_flags; + PageantKey *pk; + ptrlen keyblob, sigdata; + uint32_t flags; - plog(logctx, logfn, "request: SSH2_AGENTC_SIGN_REQUEST"); + pageant_client_log(pc, reqid, "request: SSH2_AGENTC_SIGN_REQUEST"); - keyblob = get_string(msg); - sigdata = get_string(msg); + keyblob = get_string(msg); + sigdata = get_string(msg); - if (get_err(msg)) { - pageant_failure_msg(bs, "unable to decode request", - logctx, logfn); - return; - } + if (get_err(msg)) { + fail("unable to decode request"); + goto responded; + } - /* - * Later versions of the agent protocol added a flags word - * on the end of the sign request. That hasn't always been - * there, so we don't complain if we don't find it. - * - * get_uint32 will default to returning zero if no data is - * available. - */ - bool have_flags = false; - flags = get_uint32(msg); - if (!get_err(msg)) - have_flags = true; - - if (logfn) { - char *fingerprint = ssh2_fingerprint_blob(keyblob); - plog(logctx, logfn, "requested key: %s", fingerprint); - sfree(fingerprint); - } - key = find234(ssh2keys, &keyblob, cmpkeys_ssh2_asymm); - if (!key) { - pageant_failure_msg(bs, "key not found", logctx, logfn); - return; - } + /* + * Later versions of the agent protocol added a flags word + * on the end of the sign request. That hasn't always been + * there, so we don't complain if we don't find it. + * + * get_uint32 will default to returning zero if no data is + * available. + */ + bool have_flags = false; + flags = get_uint32(msg); + if (!get_err(msg)) + have_flags = true; + + if (!pc->suppress_logging) { + char *fingerprint = ssh2_fingerprint_blob( + keyblob, SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "requested key: %s", fingerprint); + sfree(fingerprint); + } + if ((pk = findkey2(keyblob)) == NULL) { + fail("key not found"); + goto responded; + } - if (have_flags) - plog(logctx, logfn, "signature flags = 0x%08"PRIx32, flags); - else - plog(logctx, logfn, "no signature flags"); + if (have_flags) + pageant_client_log(pc, reqid, "signature flags = 0x%08"PRIx32, + flags); + else + pageant_client_log(pc, reqid, "no signature flags"); + + strbuf_free(sb); /* no immediate response */ + + PageantSignOp *so = snew(PageantSignOp); + so->pao.vt = &signop_vtable; + so->pao.info = pc->info; + so->pao.cr.prev = pc->info->head.prev; + so->pao.cr.next = &pc->info->head; + so->pao.reqid = reqid; + so->pk = pk; + so->pkr.prev = so->pkr.next = NULL; + so->data_to_sign = strbuf_new(); + put_datapl(so->data_to_sign, sigdata); + so->flags = flags; + so->failure_type = failure_type; + so->crLine = 0; + return &so->pao; + break; + } + case SSH1_AGENTC_ADD_RSA_IDENTITY: { + /* + * Add to the list and return SSH_AGENT_SUCCESS, or + * SSH_AGENT_FAILURE if the key was malformed. + */ + RSAKey *key; - supported_flags = ssh_key_alg(key->key)->supported_flags; - if (flags & ~supported_flags) { - /* - * We MUST reject any message containing flags we - * don't understand. - */ - char *msg = dupprintf( - "unsupported flag bits 0x%08"PRIx32, - flags & ~supported_flags); - pageant_failure_msg(bs, msg, logctx, logfn); - sfree(msg); - return; - } + pageant_client_log(pc, reqid, "request: SSH1_AGENTC_ADD_RSA_IDENTITY"); - char *invalid = ssh_key_invalid(key->key, flags); - if (invalid) { - char *msg = dupprintf("key invalid: %s", invalid); - pageant_failure_msg(bs, msg, logctx, logfn); - sfree(msg); - sfree(invalid); - return; - } + key = get_rsa_ssh1_priv_agent(msg); + key->comment = mkstr(get_string(msg)); + + if (get_err(msg)) { + fail("unable to decode request"); + goto add1_cleanup; + } + + if (!rsa_verify(key)) { + fail("key is invalid"); + goto add1_cleanup; + } - signature = strbuf_new(); - ssh_key_sign(key->key, sigdata, flags, - BinarySink_UPCAST(signature)); + if (!pc->suppress_logging) { + char *fingerprint = rsa_ssh1_fingerprint(key); + pageant_client_log(pc, reqid, + "submitted key: %s", fingerprint); + sfree(fingerprint); + } - put_byte(bs, SSH2_AGENT_SIGN_RESPONSE); - put_stringsb(bs, signature); + if (pageant_add_ssh1_key(key)) { + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); + key = NULL; /* don't free it in cleanup */ + } else { + fail("key already present"); + } - plog(logctx, logfn, "reply: SSH2_AGENT_SIGN_RESPONSE"); + add1_cleanup: + if (key) { + freersakey(key); + sfree(key); } break; - case SSH1_AGENTC_ADD_RSA_IDENTITY: + } + case SSH2_AGENTC_ADD_IDENTITY: { /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ - { - RSAKey *key; + ssh2_userkey *key = NULL; + ptrlen algpl; + const ssh_keyalg *alg; - plog(logctx, logfn, "request: SSH1_AGENTC_ADD_RSA_IDENTITY"); + pageant_client_log(pc, reqid, "request: SSH2_AGENTC_ADD_IDENTITY"); - key = snew(RSAKey); - memset(key, 0, sizeof(RSAKey)); + algpl = get_string(msg); - get_rsa_ssh1_pub(msg, key, RSA_SSH1_MODULUS_FIRST); - get_rsa_ssh1_priv(msg, key); + key = snew(ssh2_userkey); + key->key = NULL; + key->comment = NULL; + alg = find_pubkey_alg_len(algpl); + if (!alg) { + fail("algorithm unknown"); + goto add2_cleanup; + } - /* SSH-1 names p and q the other way round, i.e. we have - * the inverse of p mod q and not of q mod p. We swap the - * names, because our internal RSA wants iqmp. */ - key->iqmp = get_mp_ssh1(msg); - key->q = get_mp_ssh1(msg); - key->p = get_mp_ssh1(msg); + key->key = ssh_key_new_priv_openssh(alg, msg); - key->comment = mkstr(get_string(msg)); + if (!key->key) { + fail("key setup failed"); + goto add2_cleanup; + } - if (get_err(msg)) { - pageant_failure_msg(bs, "unable to decode request", - logctx, logfn); - goto add1_cleanup; - } + key->comment = mkstr(get_string(msg)); - if (!rsa_verify(key)) { - pageant_failure_msg(bs, "key is invalid", logctx, logfn); - goto add1_cleanup; - } + if (get_err(msg)) { + fail("unable to decode request"); + goto add2_cleanup; + } - if (logfn) { - char *fingerprint = rsa_ssh1_fingerprint(key); - plog(logctx, logfn, "submitted key: %s", fingerprint); - sfree(fingerprint); - } + if (!pc->suppress_logging) { + char *fingerprint = ssh2_fingerprint(key->key, SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "submitted key: %s %s", + fingerprint, key->comment); + sfree(fingerprint); + } - if (add234(rsakeys, key) == key) { - keylist_update(); - put_byte(bs, SSH_AGENT_SUCCESS); - plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); - key = NULL; /* don't free it in cleanup */ - } else { - pageant_failure_msg(bs, "key already present", - logctx, logfn); - } + if (pageant_add_ssh2_key(key)) { + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); - add1_cleanup: - if (key) { - freersakey(key); - sfree(key); - } + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); + + key = NULL; /* don't clean it up */ + } else { + fail("key already present"); + } + + add2_cleanup: + if (key) { + if (key->key) + ssh_key_free(key->key); + if (key->comment) + sfree(key->comment); + sfree(key); } break; - case SSH2_AGENTC_ADD_IDENTITY: + } + case SSH1_AGENTC_REMOVE_RSA_IDENTITY: { /* - * Add to the list and return SSH_AGENT_SUCCESS, or - * SSH_AGENT_FAILURE if the key was malformed. + * Remove from the list and return SSH_AGENT_SUCCESS, or + * perhaps SSH_AGENT_FAILURE if it wasn't in the list to + * start with. */ - { - ssh2_userkey *key = NULL; - ptrlen algpl; - const ssh_keyalg *alg; + RSAKey reqkey; + PageantKey *pk; - plog(logctx, logfn, "request: SSH2_AGENTC_ADD_IDENTITY"); + pageant_client_log(pc, reqid, + "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY"); - algpl = get_string(msg); + memset(&reqkey, 0, sizeof(reqkey)); + get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST); - key = snew(ssh2_userkey); - key->key = NULL; - key->comment = NULL; - alg = find_pubkey_alg_len(algpl); - if (!alg) { - pageant_failure_msg(bs, "algorithm unknown", logctx, logfn); - goto add2_cleanup; - } + if (get_err(msg)) { + fail("unable to decode request"); + freersakey(&reqkey); + goto responded; + } - key->key = ssh_key_new_priv_openssh(alg, msg); + if (!pc->suppress_logging) { + char *fingerprint; + reqkey.comment = NULL; + fingerprint = rsa_ssh1_fingerprint(&reqkey); + pageant_client_log(pc, reqid, "unwanted key: %s", fingerprint); + sfree(fingerprint); + } - if (!key->key) { - pageant_failure_msg(bs, "key setup failed", logctx, logfn); - goto add2_cleanup; - } + pk = findkey1(&reqkey); + freersakey(&reqkey); + if (pk) { + pageant_client_log(pc, reqid, "found with comment: %s", + pk->rkey->comment); - key->comment = mkstr(get_string(msg)); + del234(keytree, pk); + keylist_update(); + pk_free(pk); + put_byte(sb, SSH_AGENT_SUCCESS); - if (get_err(msg)) { - pageant_failure_msg(bs, "unable to decode request", - logctx, logfn); - goto add2_cleanup; - } + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); + } else { + fail("key not found"); + } + break; + } + case SSH2_AGENTC_REMOVE_IDENTITY: { + /* + * Remove from the list and return SSH_AGENT_SUCCESS, or + * perhaps SSH_AGENT_FAILURE if it wasn't in the list to + * start with. + */ + PageantKey *pk; + ptrlen blob; - if (logfn) { - char *fingerprint = ssh2_fingerprint(key->key); - plog(logctx, logfn, "submitted key: %s %s", - fingerprint, key->comment); - sfree(fingerprint); - } + pageant_client_log(pc, reqid, "request: SSH2_AGENTC_REMOVE_IDENTITY"); - if (add234(ssh2keys, key) == key) { - keylist_update(); - put_byte(bs, SSH_AGENT_SUCCESS); + blob = get_string(msg); - plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); + if (get_err(msg)) { + fail("unable to decode request"); + goto responded; + } - key = NULL; /* don't clean it up */ - } else { - pageant_failure_msg(bs, "key already present", - logctx, logfn); - } + if (!pc->suppress_logging) { + char *fingerprint = ssh2_fingerprint_blob( + blob, SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "unwanted key: %s", fingerprint); + sfree(fingerprint); + } - add2_cleanup: - if (key) { - if (key->key) - ssh_key_free(key->key); - if (key->comment) - sfree(key->comment); - sfree(key); - } + pk = findkey2(blob); + if (!pk) { + fail("key not found"); + goto responded; } + + pageant_client_log(pc, reqid, "found with comment: %s", pk->comment); + + del234(keytree, pk); + keylist_update(); + pk_free(pk); + put_byte(sb, SSH_AGENT_SUCCESS); + + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); break; - case SSH1_AGENTC_REMOVE_RSA_IDENTITY: + } + case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES: { /* - * Remove from the list and return SSH_AGENT_SUCCESS, or - * perhaps SSH_AGENT_FAILURE if it wasn't in the list to - * start with. + * Remove all SSH-1 keys. Always returns success. */ - { - RSAKey reqkey, *key; + pageant_client_log(pc, reqid, + "request: SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES"); + + remove_all_keys(1); + keylist_update(); + + put_byte(sb, SSH_AGENT_SUCCESS); + + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); + break; + } + case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: { + /* + * Remove all SSH-2 keys. Always returns success. + */ + pageant_client_log(pc, reqid, + "request: SSH2_AGENTC_REMOVE_ALL_IDENTITIES"); + + remove_all_keys(2); + keylist_update(); + + put_byte(sb, SSH_AGENT_SUCCESS); + + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); + break; + } + case SSH2_AGENTC_EXTENSION: { + enum Extension exttype = EXT_UNKNOWN; + ptrlen extname = get_string(msg); + pageant_client_log(pc, reqid, + "request: SSH2_AGENTC_EXTENSION \"%.*s\"", + PTRLEN_PRINTF(extname)); + + for (size_t i = 0; i < lenof(extension_names); i++) + if (ptrlen_eq_ptrlen(extname, extension_names[i])) { + exttype = i; + + /* + * For SSH_AGENTC_EXTENSION requests, the message + * code SSH_AGENT_FAILURE is reserved for "I don't + * recognise this extension name at all". For any + * other kind of failure while processing an + * extension we _do_ recognise, we must switch to + * returning a different failure code, with + * semantics "I understood the extension name, but + * something else went wrong". + */ + failure_type = SSH_AGENT_EXTENSION_FAILURE; + break; + } + + switch (exttype) { + case EXT_UNKNOWN: + fail("unrecognised extension name '%.*s'", + PTRLEN_PRINTF(extname)); + break; - plog(logctx, logfn, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY"); + case EXT_QUERY: + /* Standard request to list the supported extensions. */ + put_byte(sb, SSH_AGENT_SUCCESS); + for (size_t i = 0; i < lenof(extension_names); i++) + put_stringpl(sb, extension_names[i]); + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS + names"); + break; - memset(&reqkey, 0, sizeof(reqkey)); - get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST); + case EXT_ADD_PPK: { + ptrlen keyfile = get_string(msg); if (get_err(msg)) { - pageant_failure_msg(bs, "unable to decode request", - logctx, logfn); - freersakey(&reqkey); - return; + fail("unable to decode request"); + goto responded; } - if (logfn) { - char *fingerprint; - reqkey.comment = NULL; - fingerprint = rsa_ssh1_fingerprint(&reqkey); - plog(logctx, logfn, "unwanted key: %s", fingerprint); + BinarySource src[1]; + const char *error; + + strbuf *public_blob = strbuf_new(); + char *comment; + + BinarySource_BARE_INIT_PL(src, keyfile); + if (!ppk_loadpub_s(src, NULL, BinarySink_UPCAST(public_blob), + &comment, &error)) { + fail("failed to extract public key blob: %s", error); + goto add_ppk_cleanup; + } + + if (!pc->suppress_logging) { + char *fingerprint = ssh2_fingerprint_blob( + ptrlen_from_strbuf(public_blob), SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "add-ppk: %s %s", + fingerprint, comment); sfree(fingerprint); } - key = find234(rsakeys, &reqkey, NULL); - freersakey(&reqkey); - if (key) { - plog(logctx, logfn, "found with comment: %s", key->comment); + BinarySource_BARE_INIT_PL(src, keyfile); + bool encrypted = ppk_encrypted_s(src, NULL); + + if (!encrypted) { + /* If the key isn't encrypted, then we should just + * load and add it in the obvious way. */ + BinarySource_BARE_INIT_PL(src, keyfile); + ssh2_userkey *skey = ppk_load_s(src, NULL, &error); + if (!skey) { + fail("failed to decode private key: %s", error); + } else if (pageant_add_ssh2_key(skey)) { + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); + + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS" + " (loaded unencrypted PPK)"); + } else { + fail("key already present"); + if (skey->key) + ssh_key_free(skey->key); + if (skey->comment) + sfree(skey->comment); + sfree(skey); + } + goto add_ppk_cleanup; + } - del234(rsakeys, key); - keylist_update(); - freersakey(key); - sfree(key); - put_byte(bs, SSH_AGENT_SUCCESS); + PageantKeySort sort = + keysort(2, ptrlen_from_strbuf(public_blob)); - plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); + PageantKey *pk = find234(keytree, &sort, NULL); + if (pk) { + /* + * This public key blob already exists in the + * keytree. Add the encrypted key file to the + * existing record, if it doesn't have one already. + */ + if (!pk->encrypted_key_file) { + pk->encrypted_key_file = strbuf_new_nm(); + put_datapl(pk->encrypted_key_file, keyfile); + + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); + pageant_client_log( + pc, reqid, "reply: SSH_AGENT_SUCCESS (added encrypted" + " PPK to existing key record)"); + } else { + fail("key already present"); + } } else { - pageant_failure_msg(bs, "key not found", logctx, logfn); + /* + * We're adding a new key record containing only + * an encrypted key file. + */ + PageantKey *pk = snew(PageantKey); + memset(pk, 0, sizeof(PageantKey)); + pk->blocked_requests.next = pk->blocked_requests.prev = + &pk->blocked_requests; + pk->sort.ssh_version = 2; + pk->public_blob = public_blob; + public_blob = NULL; + pk->sort.public_blob = ptrlen_from_strbuf(pk->public_blob); + pk->comment = dupstr(comment); + pk->encrypted_key_file = strbuf_new_nm(); + put_datapl(pk->encrypted_key_file, keyfile); + + PageantKey *added = add234(keytree, pk); + assert(added == pk); (void)added; + + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS (made" + " new encrypted-only key record)"); } - } - break; - case SSH2_AGENTC_REMOVE_IDENTITY: - /* - * Remove from the list and return SSH_AGENT_SUCCESS, or - * perhaps SSH_AGENT_FAILURE if it wasn't in the list to - * start with. - */ - { - ssh2_userkey *key; - ptrlen blob; - plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_IDENTITY"); + add_ppk_cleanup: + if (public_blob) + strbuf_free(public_blob); + sfree(comment); + break; + } - blob = get_string(msg); + case EXT_REENCRYPT: { + /* + * Re-encrypt a single key, in the sense of deleting + * its unencrypted copy, returning it to the state of + * only having the encrypted PPK form stored, so that + * the next attempt to use it will have to re-prompt + * for the passphrase. + */ + ptrlen blob = get_string(msg); if (get_err(msg)) { - pageant_failure_msg(bs, "unable to decode request", - logctx, logfn); - return; + fail("unable to decode request"); + goto responded; } - if (logfn) { - char *fingerprint = ssh2_fingerprint_blob(blob); - plog(logctx, logfn, "unwanted key: %s", fingerprint); + if (!pc->suppress_logging) { + char *fingerprint = ssh2_fingerprint_blob( + blob, SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "key to re-encrypt: %s", + fingerprint); sfree(fingerprint); } - key = find234(ssh2keys, &blob, cmpkeys_ssh2_asymm); - if (!key) { - pageant_failure_msg(bs, "key not found", logctx, logfn); - return; + PageantKey *pk = findkey2(blob); + if (!pk) { + fail("key not found"); + goto responded; } - plog(logctx, logfn, "found with comment: %s", key->comment); - - del234(ssh2keys, key); - keylist_update(); - ssh_key_free(key->key); - sfree(key->comment); - sfree(key); - put_byte(bs, SSH_AGENT_SUCCESS); - - plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); - } - break; - case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES: - /* - * Remove all SSH-1 keys. Always returns success. - */ - { - RSAKey *rkey; + pageant_client_log(pc, reqid, + "found with comment: %s", pk->comment); - plog(logctx, logfn, "request:" - " SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES"); - - while ((rkey = index234(rsakeys, 0)) != NULL) { - del234(rsakeys, rkey); - freersakey(rkey); - sfree(rkey); + if (!reencrypt_key(pk)) { + fail("this key couldn't be re-encrypted"); + goto responded; } - keylist_update(); - - put_byte(bs, SSH_AGENT_SUCCESS); - plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); - } - break; - case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: - /* - * Remove all SSH-2 keys. Always returns success. - */ - { - ssh2_userkey *skey; + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); + break; + } - plog(logctx, logfn, "request: SSH2_AGENTC_REMOVE_ALL_IDENTITIES"); + case EXT_REENCRYPT_ALL: { + /* + * Re-encrypt all keys that have an encrypted form + * stored. Usually, returns success, but with a uint32 + * appended indicating how many keys remain + * unencrypted. The exception is if there is at least + * one key in the agent and _no_ key was successfully + * re-encrypted; in that situation we've done nothing, + * and the client didn't _want_ us to do nothing, so + * we return failure. + * + * (Rationale: the 'failure' message ought to be + * atomic, that is, you shouldn't return failure + * having made a state change.) + */ + unsigned nfailures = 0, nsuccesses = 0; + PageantKey *pk; + + for (int i = 0; (pk = index234(keytree, i)) != NULL; i++) { + if (reencrypt_key(pk)) + nsuccesses++; + else + nfailures++; + } - while ((skey = index234(ssh2keys, 0)) != NULL) { - del234(ssh2keys, skey); - ssh_key_free(skey->key); - sfree(skey->comment); - sfree(skey); + if (nsuccesses == 0 && nfailures > 0) { + fail("no key could be re-encrypted"); + } else { + keylist_update(); + put_byte(sb, SSH_AGENT_SUCCESS); + put_uint32(sb, nfailures); + pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS " + "(%u keys re-encrypted, %u failures)", + nsuccesses, nfailures); } - keylist_update(); + break; + } - put_byte(bs, SSH_AGENT_SUCCESS); + case EXT_LIST_EXTENDED: { + /* + * Return a key list like SSH2_AGENTC_REQUEST_IDENTITIES, + * except that each key is annotated with extra + * information such as whether it's currently encrypted. + * + * The return message type is AGENT_SUCCESS with auxiliary + * data, which is more like other extension messages. I + * think it would be confusing to reuse IDENTITIES_ANSWER + * for a reply message with an incompatible format. + */ + put_byte(sb, SSH_AGENT_SUCCESS); + pageant_make_keylist_extended(BinarySink_UPCAST(sb)); - plog(logctx, logfn, "reply: SSH_AGENT_SUCCESS"); + pageant_client_log(pc, reqid, + "reply: SSH2_AGENT_SUCCESS + key list"); + if (!pc->suppress_logging) { + int i; + PageantKey *pk; + for (i = 0; NULL != (pk = pageant_nth_key(2, i)); i++) { + char *fingerprint = ssh2_fingerprint_blob( + ptrlen_from_strbuf(pk->public_blob), + SSH_FPTYPE_DEFAULT); + pageant_client_log(pc, reqid, "returned key: %s %s", + fingerprint, pk->comment); + sfree(fingerprint); + } + } + break; + } } break; + } default: - plog(logctx, logfn, "request: unknown message type %d", type); - pageant_failure_msg(bs, "unrecognised message", logctx, logfn); + pageant_client_log(pc, reqid, "request: unknown message type %d", + type); + fail("unrecognised message"); break; } -} -void pageant_failure_msg(BinarySink *bs, - const char *log_reason, - void *logctx, pageant_logfn_t logfn) -{ - put_byte(bs, SSH_AGENT_FAILURE); - plog(logctx, logfn, "reply: SSH_AGENT_FAILURE (%s)", log_reason); -} +#undef fail -void pageant_init(void) -{ - pageant_local = true; - rsakeys = newtree234(cmpkeys_rsa); - ssh2keys = newtree234(cmpkeys_ssh2); + responded:; + + PageantImmOp *io = snew(PageantImmOp); + io->pao.vt = &immop_vtable; + io->pao.info = pc->info; + io->pao.cr.prev = pc->info->head.prev; + io->pao.cr.next = &pc->info->head; + io->pao.reqid = reqid; + io->response = sb; + io->crLine = 0; + return &io->pao; } -RSAKey *pageant_nth_ssh1_key(int i) +void pageant_handle_msg(PageantClient *pc, PageantClientRequestId *reqid, + ptrlen msgpl) { - return index234(rsakeys, i); + PageantAsyncOp *pao = pageant_make_op(pc, reqid, msgpl); + queue_toplevel_callback(pageant_async_op_callback, pao); } -ssh2_userkey *pageant_nth_ssh2_key(int i) +void pageant_init(void) { - return index234(ssh2keys, i); + pageant_local = true; + keytree = newtree234(cmpkeys); } -int pageant_count_ssh1_keys(void) +static PageantKey *pageant_nth_key(int ssh_version, int i) { - return count234(rsakeys); + PageantKey *pk = index234( + keytree, find_first_key_for_version(ssh_version) + i); + if (pk && pk->sort.ssh_version == ssh_version) + return pk; + else + return NULL; } -int pageant_count_ssh2_keys(void) +bool pageant_delete_nth_ssh1_key(int i) { - return count234(ssh2keys); + PageantKey *pk = delpos234(keytree, find_first_key_for_version(1) + i); + if (!pk) + return false; + pk_free(pk); + return true; } -bool pageant_add_ssh1_key(RSAKey *rkey) +bool pageant_delete_nth_ssh2_key(int i) { - return add234(rsakeys, rkey) == rkey; + PageantKey *pk = delpos234(keytree, find_first_key_for_version(2) + i); + if (!pk) + return false; + pk_free(pk); + return true; } -bool pageant_add_ssh2_key(ssh2_userkey *skey) +bool pageant_reencrypt_nth_ssh2_key(int i) { - return add234(ssh2keys, skey) == skey; + PageantKey *pk = index234(keytree, find_first_key_for_version(2) + i); + if (!pk) + return false; + return reencrypt_key(pk); } -bool pageant_delete_ssh1_key(RSAKey *rkey) +void pageant_delete_all(void) { - RSAKey *deleted = del234(rsakeys, rkey); - if (!deleted) - return false; - assert(deleted == rkey); - return true; + remove_all_keys(1); + remove_all_keys(2); } -bool pageant_delete_ssh2_key(ssh2_userkey *skey) +void pageant_reencrypt_all(void) { - ssh2_userkey *deleted = del234(ssh2keys, skey); - if (!deleted) - return false; - assert(deleted == skey); - return true; + PageantKey *pk; + for (int i = 0; (pk = index234(keytree, i)) != NULL; i++) + reencrypt_key(pk); } /* ---------------------------------------------------------------------- @@ -716,15 +1438,26 @@ bool pageant_delete_ssh2_key(ssh2_userkey *skey) (c) = (unsigned char)*data++; \ } while (0) +struct pageant_conn_queued_response { + struct pageant_conn_queued_response *next, *prev; + size_t req_index; /* for indexing requests in log messages */ + strbuf *sb; + PageantClientRequestId reqid; +}; + struct pageant_conn_state { Socket *connsock; - void *logctx; - pageant_logfn_t logfn; + PageantListenerClient *plc; unsigned char lenbuf[4], pktbuf[AGENT_MAX_MSGLEN]; unsigned len, got; bool real_packet; + size_t conn_index; /* for indexing connections in log messages */ + size_t req_index; /* for indexing requests in log messages */ int crLine; /* for coroutine in pageant_conn_receive */ + struct pageant_conn_queued_response response_queue; + + PageantClient pc; Plug plug; }; @@ -734,10 +1467,13 @@ static void pageant_conn_closing(Plug *plug, const char *error_msg, struct pageant_conn_state *pc = container_of( plug, struct pageant_conn_state, plug); if (error_msg) - plog(pc->logctx, pc->logfn, "%p: error: %s", pc, error_msg); + pageant_listener_client_log(pc->plc, "c#%"SIZEu": error: %s", + pc->conn_index, error_msg); else - plog(pc->logctx, pc->logfn, "%p: connection closed", pc); + pageant_listener_client_log(pc->plc, "c#%"SIZEu": connection closed", + pc->conn_index); sk_close(pc->connsock); + pageant_unregister_client(&pc->pc); sfree(pc); } @@ -754,15 +1490,56 @@ static void pageant_conn_sent(Plug *plug, size_t bufsize) */ } -static void pageant_conn_log(void *logctx, const char *fmt, va_list ap) +static void pageant_conn_log(PageantClient *pc, PageantClientRequestId *reqid, + const char *fmt, va_list ap) { - /* Wrapper on pc->logfn that prefixes the connection identifier */ - struct pageant_conn_state *pc = (struct pageant_conn_state *)logctx; + struct pageant_conn_state *pcs = + container_of(pc, struct pageant_conn_state, pc); + struct pageant_conn_queued_response *qr = + container_of(reqid, struct pageant_conn_queued_response, reqid); + char *formatted = dupvprintf(fmt, ap); - plog(pc->logctx, pc->logfn, "%p: %s", pc, formatted); + pageant_listener_client_log(pcs->plc, "c#%"SIZEu",r#%"SIZEu": %s", + pcs->conn_index, qr->req_index, formatted); sfree(formatted); } +static void pageant_conn_got_response( + PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) +{ + struct pageant_conn_state *pcs = + container_of(pc, struct pageant_conn_state, pc); + struct pageant_conn_queued_response *qr = + container_of(reqid, struct pageant_conn_queued_response, reqid); + + qr->sb = strbuf_new_nm(); + put_stringpl(qr->sb, response); + + while (pcs->response_queue.next != &pcs->response_queue && + pcs->response_queue.next->sb) { + qr = pcs->response_queue.next; + sk_write(pcs->connsock, qr->sb->u, qr->sb->len); + qr->next->prev = qr->prev; + qr->prev->next = qr->next; + strbuf_free(qr->sb); + sfree(qr); + } +} + +static bool pageant_conn_ask_passphrase( + PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) +{ + struct pageant_conn_state *pcs = + container_of(pc, struct pageant_conn_state, pc); + return pageant_listener_client_ask_passphrase(pcs->plc, dlgid, comment); +} + +static const PageantClientVtable pageant_connection_clientvt = { + .log = pageant_conn_log, + .got_response = pageant_conn_got_response, + .ask_passphrase = pageant_conn_ask_passphrase, +}; + static void pageant_conn_receive( Plug *plug, int urgent, const char *data, size_t len) { @@ -783,6 +1560,31 @@ static void pageant_conn_receive( pc->got = 0; pc->real_packet = (pc->len < AGENT_MAX_MSGLEN-4); + { + struct pageant_conn_queued_response *qr = + snew(struct pageant_conn_queued_response); + qr->prev = pc->response_queue.prev; + qr->next = &pc->response_queue; + qr->prev->next = qr->next->prev = qr; + qr->sb = NULL; + qr->req_index = pc->req_index++; + } + + if (!pc->real_packet) { + /* + * Send failure immediately, before consuming the packet + * data. That way we notify the client reasonably early + * even if the data channel has just started spewing + * nonsense. + */ + pageant_client_log(&pc->pc, &pc->response_queue.prev->reqid, + "early reply: SSH_AGENT_FAILURE " + "(overlong message, length %u)", pc->len); + static const unsigned char failure[] = { SSH_AGENT_FAILURE }; + pageant_conn_got_response(&pc->pc, &pc->response_queue.prev->reqid, + make_ptrlen(failure, lenof(failure))); + } + while (pc->got < pc->len) { crGetChar(c); if (pc->real_packet) @@ -790,26 +1592,9 @@ static void pageant_conn_receive( pc->got++; } - { - strbuf *reply = strbuf_new(); - - put_uint32(reply, 0); /* length field to fill in later */ - - if (pc->real_packet) { - pageant_handle_msg(BinarySink_UPCAST(reply), pc->pktbuf, pc->len, pc, - pc->logfn ? pageant_conn_log : NULL); - } else { - plog(pc->logctx, pc->logfn, "%p: overlong message (%u)", - pc, pc->len); - pageant_failure_msg(BinarySink_UPCAST(reply), "message too long", pc, - pc->logfn ? pageant_conn_log : NULL); - } - - PUT_32BIT_MSB_FIRST(reply->s, reply->len - 4); - sk_write(pc->connsock, reply->s, reply->len); - - strbuf_free(reply); - } + if (pc->real_packet) + pageant_handle_msg(&pc->pc, &pc->response_queue.prev->reqid, + make_ptrlen(pc->pktbuf, pc->len)); } crFinishV; @@ -817,8 +1602,8 @@ static void pageant_conn_receive( struct pageant_listen_state { Socket *listensock; - void *logctx; - pageant_logfn_t logfn; + PageantListenerClient *plc; + size_t conn_index; /* for indexing connections in log messages */ Plug plug; }; @@ -829,17 +1614,16 @@ static void pageant_listen_closing(Plug *plug, const char *error_msg, struct pageant_listen_state *pl = container_of( plug, struct pageant_listen_state, plug); if (error_msg) - plog(pl->logctx, pl->logfn, "listening socket: error: %s", error_msg); + pageant_listener_client_log(pl->plc, "listening socket: error: %s", + error_msg); sk_close(pl->listensock); pl->listensock = NULL; } static const PlugVtable pageant_connection_plugvt = { - NULL, /* no log function, because that's for outgoing connections */ - pageant_conn_closing, - pageant_conn_receive, - pageant_conn_sent, - NULL /* no accepting function, because we've already done it */ + .closing = pageant_conn_closing, + .receive = pageant_conn_receive, + .sent = pageant_conn_sent, }; static int pageant_listen_accepting(Plug *plug, @@ -853,8 +1637,11 @@ static int pageant_listen_accepting(Plug *plug, pc = snew(struct pageant_conn_state); pc->plug.vt = &pageant_connection_plugvt; - pc->logfn = pl->logfn; - pc->logctx = pl->logctx; + pc->pc.vt = &pageant_connection_clientvt; + pc->plc = pl->plc; + pc->response_queue.next = pc->response_queue.prev = &pc->response_queue; + pc->conn_index = pl->conn_index++; + pc->req_index = 0; pc->crLine = 0; pc->connsock = constructor(ctx, &pc->plug); @@ -864,35 +1651,37 @@ static int pageant_listen_accepting(Plug *plug, return 1; } - sk_set_frozen(pc->connsock, 0); + sk_set_frozen(pc->connsock, false); peerinfo = sk_peer_info(pc->connsock); if (peerinfo && peerinfo->log_text) { - plog(pl->logctx, pl->logfn, "%p: new connection from %s", - pc, peerinfo->log_text); + pageant_listener_client_log(pl->plc, + "c#%"SIZEu": new connection from %s", + pc->conn_index, peerinfo->log_text); } else { - plog(pl->logctx, pl->logfn, "%p: new connection", pc); + pageant_listener_client_log(pl->plc, "c#%"SIZEu": new connection", + pc->conn_index); } sk_free_peer_info(peerinfo); + pageant_register_client(&pc->pc); + return 0; } static const PlugVtable pageant_listener_plugvt = { - NULL, /* no log function, because that's for outgoing connections */ - pageant_listen_closing, - NULL, /* no receive function on a listening socket */ - NULL, /* no sent function on a listening socket */ - pageant_listen_accepting + .closing = pageant_listen_closing, + .accepting = pageant_listen_accepting, }; -struct pageant_listen_state *pageant_listener_new(Plug **plug) +struct pageant_listen_state *pageant_listener_new( + Plug **plug, PageantListenerClient *plc) { struct pageant_listen_state *pl = snew(struct pageant_listen_state); pl->plug.vt = &pageant_listener_plugvt; - pl->logctx = NULL; - pl->logfn = NULL; + pl->plc = plc; pl->listensock = NULL; + pl->conn_index = 0; *plug = &pl->plug; return pl; } @@ -902,13 +1691,6 @@ void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket *sock) pl->listensock = sock; } -void pageant_listener_set_logfn(struct pageant_listen_state *pl, - void *logctx, pageant_logfn_t logfn) -{ - pl->logctx = logctx; - pl->logfn = logfn; -} - void pageant_listener_free(struct pageant_listen_state *pl) { if (pl->listensock) @@ -923,6 +1705,109 @@ void pageant_listener_free(struct pageant_listen_state *pl) static tree234 *passphrases = NULL; +typedef struct PageantInternalClient { + strbuf *response; + bool got_response; + PageantClient pc; +} PageantInternalClient; + +static void internal_client_got_response( + PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) +{ + PageantInternalClient *pic = container_of(pc, PageantInternalClient, pc); + strbuf_clear(pic->response); + put_datapl(pic->response, response); + pic->got_response = true; +} + +static bool internal_client_ask_passphrase( + PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) +{ + /* No delaying operations are permitted in this mode */ + return false; +} + +static const PageantClientVtable internal_clientvt = { + .log = NULL, + .got_response = internal_client_got_response, + .ask_passphrase = internal_client_ask_passphrase, +}; + +typedef struct PageantClientOp { + strbuf *buf; + bool request_made; + BinarySink_DELEGATE_IMPLEMENTATION; + BinarySource_IMPLEMENTATION; +} PageantClientOp; + +static PageantClientOp *pageant_client_op_new(void) +{ + PageantClientOp *pco = snew(PageantClientOp); + pco->buf = strbuf_new_for_agent_query(); + pco->request_made = false; + BinarySink_DELEGATE_INIT(pco, pco->buf); + BinarySource_INIT(pco, "", 0); + return pco; +} + +static void pageant_client_op_free(PageantClientOp *pco) +{ + if (pco->buf) + strbuf_free(pco->buf); + sfree(pco); +} + +static unsigned pageant_client_op_query(PageantClientOp *pco) +{ + /* Since we use the same strbuf for the request and the response, + * check by assertion that we aren't embarrassingly sending a + * previous response back to the agent */ + assert(!pco->request_made); + pco->request_made = true; + + if (!pageant_local) { + void *response_raw; + int resplen_raw; + agent_query_synchronous(pco->buf, &response_raw, &resplen_raw); + strbuf_clear(pco->buf); + put_data(pco->buf, response_raw, resplen_raw); + sfree(response_raw); + + /* The data coming back from agent_query_synchronous will have + * its length field prepended. So we start by parsing it as an + * SSH-formatted string, and then reinitialise our + * BinarySource with the interior of that string. */ + BinarySource_INIT_PL(pco, ptrlen_from_strbuf(pco->buf)); + BinarySource_INIT_PL(pco, get_string(pco)); + } else { + PageantInternalClient pic; + PageantClientRequestId reqid; + + pic.pc.vt = &internal_clientvt; + pic.pc.suppress_logging = true; + pic.response = pco->buf; + pic.got_response = false; + pageant_register_client(&pic.pc); + + assert(pco->buf->len > 4); + PageantAsyncOp *pao = pageant_make_op( + &pic.pc, &reqid, make_ptrlen(pco->buf->s + 4, pco->buf->len - 4)); + while (!pic.got_response) + pageant_async_op_coroutine(pao); + + pageant_unregister_client(&pic.pc); + + BinarySource_INIT_PL(pco, ptrlen_from_strbuf(pco->buf)); + } + + /* Strip off and directly return the type byte, which every client + * will need, to save a boilerplate get_byte at each call site */ + unsigned reply_type = get_byte(pco); + if (get_err(pco)) + reply_type = 256; /* out-of-range code */ + return reply_type; +} + /* * After processing a list of filenames, we want to forget the * passphrases. @@ -940,80 +1825,112 @@ void pageant_forget_passphrases(void) } } -void *pageant_get_keylist1(int *length) +typedef struct KeyListEntry { + ptrlen blob, comment; + uint32_t flags; +} KeyListEntry; +typedef struct KeyList { + strbuf *raw_data; + KeyListEntry *keys; + size_t nkeys; + bool broken; +} KeyList; + +static void keylist_free(KeyList *kl) { - void *ret; + sfree(kl->keys); + strbuf_free(kl->raw_data); + sfree(kl); +} - if (!pageant_local) { - strbuf *request; - unsigned char *response; - void *vresponse; - int resplen; - - request = strbuf_new_for_agent_query(); - put_byte(request, SSH1_AGENTC_REQUEST_RSA_IDENTITIES); - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); - - response = vresponse; - if (resplen < 5 || response[4] != SSH1_AGENT_RSA_IDENTITIES_ANSWER) { - sfree(response); - return NULL; - } +static PageantClientOp *pageant_request_keylist_1(void) +{ + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH1_AGENTC_REQUEST_RSA_IDENTITIES); + if (pageant_client_op_query(pco) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) + return pco; + pageant_client_op_free(pco); + return NULL; +} - ret = snewn(resplen-5, unsigned char); - memcpy(ret, response+5, resplen-5); - sfree(response); +static PageantClientOp *pageant_request_keylist_2(void) +{ + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_REQUEST_IDENTITIES); + if (pageant_client_op_query(pco) == SSH2_AGENT_IDENTITIES_ANSWER) + return pco; + pageant_client_op_free(pco); + return NULL; +} - if (length) - *length = resplen-5; - } else { - strbuf *buf = strbuf_new(); - pageant_make_keylist1(BinarySink_UPCAST(buf)); - *length = buf->len; - ret = strbuf_to_str(buf); - } - return ret; +static PageantClientOp *pageant_request_keylist_extended(void) +{ + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_EXTENSION); + put_stringpl(pco, extension_names[EXT_LIST_EXTENDED]); + if (pageant_client_op_query(pco) == SSH_AGENT_SUCCESS) + return pco; + pageant_client_op_free(pco); + return NULL; } -void *pageant_get_keylist2(int *length) +static KeyList *pageant_get_keylist(unsigned ssh_version) { - void *ret; + PageantClientOp *pco; + bool list_is_extended = false; - if (!pageant_local) { - strbuf *request; - unsigned char *response; - void *vresponse; - int resplen; - - request = strbuf_new_for_agent_query(); - put_byte(request, SSH2_AGENTC_REQUEST_IDENTITIES); - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); - - response = vresponse; - if (resplen < 5 || response[4] != SSH2_AGENT_IDENTITIES_ANSWER) { - sfree(response); - return NULL; + if (ssh_version == 1) { + pco = pageant_request_keylist_1(); + } else { + if ((pco = pageant_request_keylist_extended()) != NULL) + list_is_extended = true; + else + pco = pageant_request_keylist_2(); + } + + if (!pco) + return NULL; + + KeyList *kl = snew(KeyList); + kl->nkeys = get_uint32(pco); + kl->keys = snewn(kl->nkeys, struct KeyListEntry); + kl->broken = false; + + for (size_t i = 0; i < kl->nkeys && !get_err(pco); i++) { + if (ssh_version == 1) { + int bloblen = rsa_ssh1_public_blob_len( + make_ptrlen(get_ptr(pco), get_avail(pco))); + if (bloblen < 0) { + kl->broken = true; + bloblen = 0; + } + kl->keys[i].blob = get_data(pco, bloblen); + } else { + kl->keys[i].blob = get_string(pco); } + kl->keys[i].comment = get_string(pco); - ret = snewn(resplen-5, unsigned char); - memcpy(ret, response+5, resplen-5); - sfree(response); + if (list_is_extended) { + ptrlen key_ext_info = get_string(pco); + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, key_ext_info); - if (length) - *length = resplen-5; - } else { - strbuf *buf = strbuf_new(); - pageant_make_keylist2(BinarySink_UPCAST(buf)); - *length = buf->len; - ret = strbuf_to_str(buf); + kl->keys[i].flags = get_uint32(src); + } else { + kl->keys[i].flags = 0; + } } - return ret; + + if (get_err(pco)) + kl->broken = true; + kl->raw_data = pco->buf; + pco->buf = NULL; + pageant_client_op_free(pco); + return kl; } int pageant_add_keyfile(Filename *filename, const char *passphrase, - char **retstr) + char **retstr, bool add_encrypted) { RSAKey *rkey = NULL; ssh2_userkey *skey = NULL; @@ -1038,128 +1955,131 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, return PAGEANT_ACTION_FAILURE; } + if (add_encrypted && type == SSH_KEYTYPE_SSH1) { + *retstr = dupprintf("Can't add SSH-1 keys in encrypted form"); + return PAGEANT_ACTION_FAILURE; + } + /* * See if the key is already loaded (in the primary Pageant, * which may or may not be us). */ { strbuf *blob = strbuf_new(); - unsigned char *keylist, *p; - int i, nkeys, keylistlen; + KeyList *kl; if (type == SSH_KEYTYPE_SSH1) { - if (!rsa_ssh1_loadpub(filename, BinarySink_UPCAST(blob), NULL, &error)) { + if (!rsa1_loadpub_f(filename, BinarySink_UPCAST(blob), + NULL, &error)) { *retstr = dupprintf("Couldn't load private key (%s)", error); strbuf_free(blob); return PAGEANT_ACTION_FAILURE; } - keylist = pageant_get_keylist1(&keylistlen); + kl = pageant_get_keylist(1); } else { - /* For our purposes we want the blob prefixed with its - * length, so add a placeholder here to fill in - * afterwards */ - put_uint32(blob, 0); - if (!ssh2_userkey_loadpub(filename, NULL, BinarySink_UPCAST(blob), - NULL, &error)) { + if (!ppk_loadpub_f(filename, NULL, BinarySink_UPCAST(blob), + NULL, &error)) { *retstr = dupprintf("Couldn't load private key (%s)", error); strbuf_free(blob); return PAGEANT_ACTION_FAILURE; } - PUT_32BIT_MSB_FIRST(blob->s, blob->len - 4); - keylist = pageant_get_keylist2(&keylistlen); + kl = pageant_get_keylist(2); } - if (keylist) { - if (keylistlen < 4) { - *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); - strbuf_free(blob); - return PAGEANT_ACTION_FAILURE; - } - nkeys = toint(GET_32BIT_MSB_FIRST(keylist)); - if (nkeys < 0) { + + if (kl) { + if (kl->broken) { *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); + keylist_free(kl); strbuf_free(blob); return PAGEANT_ACTION_FAILURE; } - p = keylist + 4; - keylistlen -= 4; - - for (i = 0; i < nkeys; i++) { - if (!memcmp(blob->s, p, blob->len)) { - /* Key is already present; we can now leave. */ - sfree(keylist); - strbuf_free(blob); - return PAGEANT_ACTION_OK; - } - /* Now skip over public blob */ - if (type == SSH_KEYTYPE_SSH1) { - int n = rsa_ssh1_public_blob_len( - make_ptrlen(p, keylistlen)); - if (n < 0) { - *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); - strbuf_free(blob); - return PAGEANT_ACTION_FAILURE; - } - p += n; - keylistlen -= n; - } else { - int n; - if (keylistlen < 4) { - *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); - strbuf_free(blob); - return PAGEANT_ACTION_FAILURE; - } - n = GET_32BIT_MSB_FIRST(p); - p += 4; - keylistlen -= 4; - - if (n < 0 || n > keylistlen) { - *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); - strbuf_free(blob); - return PAGEANT_ACTION_FAILURE; - } - p += n; - keylistlen -= n; - } - /* Now skip over comment field */ - { - int n; - if (keylistlen < 4) { - *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); - strbuf_free(blob); - return PAGEANT_ACTION_FAILURE; - } - n = GET_32BIT_MSB_FIRST(p); - p += 4; - keylistlen -= 4; - if (n < 0 || n > keylistlen) { - *retstr = dupstr("Received broken key list from agent"); - sfree(keylist); + for (size_t i = 0; i < kl->nkeys; i++) { + /* + * If the key already exists in the agent, we're done, + * except in the following special cases: + * + * It's encrypted in the agent, and we're being asked + * to add it unencrypted, in which case we still want + * to upload the unencrypted version to cause the key + * to become decrypted. + * (Rationale: if you know in advance you're going to + * want it, and don't want to be interrupted at an + * unpredictable moment to be asked for the + * passphrase.) + * + * The agent only has cleartext, and we're being asked + * to add it encrypted, in which case we'll add the + * encrypted form. + * (Rationale: if you might want to re-encrypt the key + * at some future point, but it happened to have been + * initially added in cleartext, perhaps by something + * other than Pageant.) + */ + if (ptrlen_eq_ptrlen(ptrlen_from_strbuf(blob), + kl->keys[i].blob)) { + bool have_unencrypted = + !(kl->keys[i].flags & + LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY); + bool have_encrypted = + (kl->keys[i].flags & + LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE); + if ((have_unencrypted && !add_encrypted) + || (have_encrypted && add_encrypted)) { + /* Key is already present in the desired form; + * we can now leave. */ + keylist_free(kl); strbuf_free(blob); - return PAGEANT_ACTION_FAILURE; + return PAGEANT_ACTION_OK; } - p += n; - keylistlen -= n; } } - sfree(keylist); + keylist_free(kl); } strbuf_free(blob); } + if (add_encrypted) { + const char *load_error; + LoadedFile *lf = lf_load_keyfile(filename, &load_error); + if (!lf) { + *retstr = dupstr(load_error); + return PAGEANT_ACTION_FAILURE; + } + + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_EXTENSION); + put_stringpl(pco, extension_names[EXT_ADD_PPK]); + put_string(pco, lf->data, lf->len); + + lf_free(lf); + + unsigned reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); + + if (reply != SSH_AGENT_SUCCESS) { + if (reply == SSH_AGENT_FAILURE) { + /* The agent didn't understand the protocol extension + * at all. */ + *retstr = dupstr("Agent doesn't support adding " + "encrypted keys"); + } else { + *retstr = dupstr("The already running agent " + "refused to add the key."); + } + return PAGEANT_ACTION_FAILURE; + } + + return PAGEANT_ACTION_OK; + } + error = NULL; if (type == SSH_KEYTYPE_SSH1) - needs_pass = rsa_ssh1_encrypted(filename, &comment); + needs_pass = rsa1_encrypted_f(filename, &comment); else - needs_pass = ssh2_userkey_encrypted(filename, &comment); + needs_pass = ppk_encrypted_f(filename, &comment); attempts = 0; if (type == SSH_KEYTYPE_SSH1) rkey = snew(RSAKey); @@ -1195,9 +2115,9 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, this_passphrase = ""; if (type == SSH_KEYTYPE_SSH1) - ret = rsa_ssh1_loadkey(filename, rkey, this_passphrase, &error); + ret = rsa1_load_f(filename, rkey, this_passphrase, &error); else { - skey = ssh2_load_userkey(filename, this_passphrase, &error); + skey = ppk_load_f(filename, this_passphrase, &error); if (skey == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!skey) @@ -1251,74 +2171,38 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, sfree(comment); if (type == SSH_KEYTYPE_SSH1) { - if (!pageant_local) { - strbuf *request; - unsigned char *response; - void *vresponse; - int resplen; - - request = strbuf_new_for_agent_query(); - put_byte(request, SSH1_AGENTC_ADD_RSA_IDENTITY); - put_uint32(request, mp_get_nbits(rkey->modulus)); - put_mp_ssh1(request, rkey->modulus); - put_mp_ssh1(request, rkey->exponent); - put_mp_ssh1(request, rkey->private_exponent); - put_mp_ssh1(request, rkey->iqmp); - put_mp_ssh1(request, rkey->q); - put_mp_ssh1(request, rkey->p); - put_stringz(request, rkey->comment); - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); - - response = vresponse; - if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { - *retstr = dupstr("The already running Pageant " - "refused to add the key."); - freersakey(rkey); - sfree(rkey); - sfree(response); - return PAGEANT_ACTION_FAILURE; - } - freersakey(rkey); - sfree(rkey); - sfree(response); - } else { - if (!pageant_add_ssh1_key(rkey)) { - freersakey(rkey); - sfree(rkey); /* already present, don't waste RAM */ - } + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH1_AGENTC_ADD_RSA_IDENTITY); + rsa_ssh1_private_blob_agent(BinarySink_UPCAST(pco), rkey); + put_stringz(pco, rkey->comment); + unsigned reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); + + freersakey(rkey); + sfree(rkey); + + if (reply != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running agent " + "refused to add the key."); + return PAGEANT_ACTION_FAILURE; } } else { - if (!pageant_local) { - strbuf *request; - unsigned char *response; - void *vresponse; - int resplen; - - request = strbuf_new_for_agent_query(); - put_byte(request, SSH2_AGENTC_ADD_IDENTITY); - put_stringz(request, ssh_key_ssh_id(skey->key)); - ssh_key_openssh_blob(skey->key, BinarySink_UPCAST(request)); - put_stringz(request, skey->comment); - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); - - response = vresponse; - if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { - *retstr = dupstr("The already running Pageant " - "refused to add the key."); - sfree(response); - return PAGEANT_ACTION_FAILURE; - } - - ssh_key_free(skey->key); - sfree(skey); - sfree(response); - } else { - if (!pageant_add_ssh2_key(skey)) { - ssh_key_free(skey->key); - sfree(skey); /* already present, don't waste RAM */ - } + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_ADD_IDENTITY); + put_stringz(pco, ssh_key_ssh_id(skey->key)); + ssh_key_openssh_blob(skey->key, BinarySink_UPCAST(pco)); + put_stringz(pco, skey->comment); + unsigned reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); + + sfree(skey->comment); + ssh_key_free(skey->key); + sfree(skey); + + if (reply != SSH_AGENT_SUCCESS) { + *retstr = dupstr("The already running agent " + "refused to add the key."); + return PAGEANT_ACTION_FAILURE; } } return PAGEANT_ACTION_OK; @@ -1327,159 +2211,126 @@ int pageant_add_keyfile(Filename *filename, const char *passphrase, int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, char **retstr) { - unsigned char *keylist; - int i, nkeys, keylistlen; - ptrlen comment; + KeyList *kl1 = NULL, *kl2 = NULL; struct pageant_pubkey cbkey; - BinarySource src[1]; + int toret = PAGEANT_ACTION_FAILURE; - keylist = pageant_get_keylist1(&keylistlen); - if (!keylist) { - *retstr = dupstr("Did not receive an SSH-1 key list from agent"); - return PAGEANT_ACTION_FAILURE; + kl1 = pageant_get_keylist(1); + if (kl1 && kl1->broken) { + *retstr = dupstr("Received broken SSH-1 key list from agent"); + goto out; } - BinarySource_BARE_INIT(src, keylist, keylistlen); - nkeys = toint(get_uint32(src)); - for (i = 0; i < nkeys; i++) { - RSAKey rkey; - char *fingerprint; + kl2 = pageant_get_keylist(2); + if (kl2 && kl2->broken) { + *retstr = dupstr("Received broken SSH-2 key list from agent"); + goto out; + } - /* public blob and fingerprint */ - memset(&rkey, 0, sizeof(rkey)); - get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST); - comment = get_string(src); + if (kl1) { + for (size_t i = 0; i < kl1->nkeys; i++) { + cbkey.blob = strbuf_new(); + put_datapl(cbkey.blob, kl1->keys[i].blob); + cbkey.comment = mkstr(kl1->keys[i].comment); + cbkey.ssh_version = 1; - if (get_err(src)) { - *retstr = dupstr("Received broken SSH-1 key list from agent"); + /* Decode public blob into a key in order to fingerprint it */ + RSAKey rkey; + memset(&rkey, 0, sizeof(rkey)); + { + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, kl1->keys[i].blob); + get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST); + if (get_err(src)) { + *retstr = dupstr( + "Received an invalid SSH-1 key from agent"); + goto out; + } + } + char **fingerprints = rsa_ssh1_fake_all_fingerprints(&rkey); freersakey(&rkey); - sfree(keylist); - return PAGEANT_ACTION_FAILURE; - } - - fingerprint = rsa_ssh1_fingerprint(&rkey); - - cbkey.blob = strbuf_new(); - rsa_ssh1_public_blob(BinarySink_UPCAST(cbkey.blob), &rkey, - RSA_SSH1_EXPONENT_FIRST); - cbkey.comment = mkstr(comment); - cbkey.ssh_version = 1; - callback(callback_ctx, fingerprint, cbkey.comment, &cbkey); - strbuf_free(cbkey.blob); - freersakey(&rkey); - sfree(cbkey.comment); - sfree(fingerprint); - } - sfree(keylist); + callback(callback_ctx, fingerprints, cbkey.comment, + kl1->keys[i].flags, &cbkey); - if (get_err(src) || get_avail(src) != 0) { - *retstr = dupstr("Received broken SSH-1 key list from agent"); - return PAGEANT_ACTION_FAILURE; + strbuf_free(cbkey.blob); + sfree(cbkey.comment); + ssh2_free_all_fingerprints(fingerprints); + } } - keylist = pageant_get_keylist2(&keylistlen); - if (!keylist) { - *retstr = dupstr("Did not receive an SSH-2 key list from agent"); - return PAGEANT_ACTION_FAILURE; - } - BinarySource_BARE_INIT(src, keylist, keylistlen); + if (kl2) { + for (size_t i = 0; i < kl2->nkeys; i++) { + cbkey.blob = strbuf_new(); + put_datapl(cbkey.blob, kl2->keys[i].blob); + cbkey.comment = mkstr(kl2->keys[i].comment); + cbkey.ssh_version = 2; - nkeys = toint(get_uint32(src)); - for (i = 0; i < nkeys; i++) { - ptrlen pubblob; - char *fingerprint; + char **fingerprints = + ssh2_all_fingerprints_for_blob(kl2->keys[i].blob); - pubblob = get_string(src); - comment = get_string(src); + callback(callback_ctx, fingerprints, cbkey.comment, + kl2->keys[i].flags, &cbkey); - if (get_err(src)) { - *retstr = dupstr("Received broken SSH-2 key list from agent"); - sfree(keylist); - return PAGEANT_ACTION_FAILURE; + ssh2_free_all_fingerprints(fingerprints); + sfree(cbkey.comment); + strbuf_free(cbkey.blob); } - - fingerprint = ssh2_fingerprint_blob(pubblob); - cbkey.blob = strbuf_new(); - put_datapl(cbkey.blob, pubblob); - - cbkey.ssh_version = 2; - cbkey.comment = mkstr(comment); - callback(callback_ctx, fingerprint, cbkey.comment, &cbkey); - sfree(fingerprint); - sfree(cbkey.comment); - } - - sfree(keylist); - - if (get_err(src) || get_avail(src) != 0) { - *retstr = dupstr("Received broken SSH-2 key list from agent"); - return PAGEANT_ACTION_FAILURE; } - return PAGEANT_ACTION_OK; + *retstr = NULL; + toret = PAGEANT_ACTION_OK; + out: + if (kl1) + keylist_free(kl1); + if (kl2) + keylist_free(kl2); + return toret; } int pageant_delete_key(struct pageant_pubkey *key, char **retstr) { - strbuf *request; - unsigned char *response; - int resplen, ret; - void *vresponse; - - request = strbuf_new_for_agent_query(); + PageantClientOp *pco = pageant_client_op_new(); if (key->ssh_version == 1) { - put_byte(request, SSH1_AGENTC_REMOVE_RSA_IDENTITY); - put_data(request, key->blob->s, key->blob->len); + put_byte(pco, SSH1_AGENTC_REMOVE_RSA_IDENTITY); + put_data(pco, key->blob->s, key->blob->len); } else { - put_byte(request, SSH2_AGENTC_REMOVE_IDENTITY); - put_string(request, key->blob->s, key->blob->len); + put_byte(pco, SSH2_AGENTC_REMOVE_IDENTITY); + put_string(pco, key->blob->s, key->blob->len); } - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); + unsigned reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); - response = vresponse; - if (resplen < 5 || response[4] != SSH_AGENT_SUCCESS) { + if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete key"); - ret = PAGEANT_ACTION_FAILURE; + return PAGEANT_ACTION_FAILURE; } else { *retstr = NULL; - ret = PAGEANT_ACTION_OK; + return PAGEANT_ACTION_OK; } - sfree(response); - return ret; } int pageant_delete_all_keys(char **retstr) { - strbuf *request; - unsigned char *response; - int resplen; - bool success; - void *vresponse; - - request = strbuf_new_for_agent_query(); - put_byte(request, SSH2_AGENTC_REMOVE_ALL_IDENTITIES); - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); - response = vresponse; - success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS); - sfree(response); - if (!success) { + PageantClientOp *pco; + unsigned reply; + + pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_REMOVE_ALL_IDENTITIES); + reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); + if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete SSH-2 keys"); return PAGEANT_ACTION_FAILURE; } - request = strbuf_new_for_agent_query(); - put_byte(request, SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES); - agent_query_synchronous(request, &vresponse, &resplen); - strbuf_free(request); - response = vresponse; - success = (resplen >= 4 && response[4] == SSH_AGENT_SUCCESS); - sfree(response); - if (!success) { + pco = pageant_client_op_new(); + put_byte(pco, SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES); + reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); + if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete SSH-1 keys"); return PAGEANT_ACTION_FAILURE; } @@ -1488,6 +2339,89 @@ int pageant_delete_all_keys(char **retstr) return PAGEANT_ACTION_OK; } +int pageant_reencrypt_key(struct pageant_pubkey *key, char **retstr) +{ + PageantClientOp *pco = pageant_client_op_new(); + + if (key->ssh_version == 1) { + *retstr = dupstr("Can't re-encrypt an SSH-1 key"); + pageant_client_op_free(pco); + return PAGEANT_ACTION_FAILURE; + } else { + put_byte(pco, SSH2_AGENTC_EXTENSION); + put_stringpl(pco, extension_names[EXT_REENCRYPT]); + put_string(pco, key->blob->s, key->blob->len); + } + + unsigned reply = pageant_client_op_query(pco); + pageant_client_op_free(pco); + + if (reply != SSH_AGENT_SUCCESS) { + if (reply == SSH_AGENT_FAILURE) { + /* The agent didn't understand the protocol extension at all. */ + *retstr = dupstr("Agent doesn't support encrypted keys"); + } else { + *retstr = dupstr("Agent failed to re-encrypt key"); + } + return PAGEANT_ACTION_FAILURE; + } else { + *retstr = NULL; + return PAGEANT_ACTION_OK; + } +} + +int pageant_reencrypt_all_keys(char **retstr) +{ + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_EXTENSION); + put_stringpl(pco, extension_names[EXT_REENCRYPT_ALL]); + unsigned reply = pageant_client_op_query(pco); + uint32_t failures = get_uint32(pco); + pageant_client_op_free(pco); + if (reply != SSH_AGENT_SUCCESS) { + if (reply == SSH_AGENT_FAILURE) { + /* The agent didn't understand the protocol extension at all. */ + *retstr = dupstr("Agent doesn't support encrypted keys"); + } else { + *retstr = dupstr("Agent failed to re-encrypt any keys"); + } + return PAGEANT_ACTION_FAILURE; + } else if (failures == 1) { + /* special case for English grammar */ + *retstr = dupstr("1 key remains unencrypted"); + return PAGEANT_ACTION_WARNING; + } else if (failures > 0) { + *retstr = dupprintf("%"PRIu32" keys remain unencrypted", failures); + return PAGEANT_ACTION_WARNING; + } else { + *retstr = NULL; + return PAGEANT_ACTION_OK; + } +} + +int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out, + uint32_t flags, char **retstr) +{ + PageantClientOp *pco = pageant_client_op_new(); + put_byte(pco, SSH2_AGENTC_SIGN_REQUEST); + put_string(pco, key->blob->s, key->blob->len); + put_stringpl(pco, message); + put_uint32(pco, flags); + unsigned reply = pageant_client_op_query(pco); + ptrlen signature = get_string(pco); + + if (reply == SSH2_AGENT_SIGN_RESPONSE && !get_err(pco)) { + *retstr = NULL; + put_datapl(out, signature); + pageant_client_op_free(pco); + return PAGEANT_ACTION_OK; + } else { + *retstr = dupstr("Agent failed to create signature"); + pageant_client_op_free(pco); + return PAGEANT_ACTION_FAILURE; + } +} + struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key) { struct pageant_pubkey *ret = snew(struct pageant_pubkey); diff --git a/pageant.h b/pageant.h index 1ccadbb..fe6f2ff 100644 --- a/pageant.h +++ b/pageant.h @@ -11,35 +11,98 @@ */ #define AGENT_MAX_MSGLEN 262144 -typedef void (*pageant_logfn_t)(void *logctx, const char *fmt, va_list ap); +typedef struct PageantClientVtable PageantClientVtable; +typedef struct PageantClient PageantClient; +typedef struct PageantClientInfo PageantClientInfo; +typedef struct PageantClientRequestId PageantClientRequestId; +typedef struct PageantClientDialogId PageantClientDialogId; +struct PageantClient { + const struct PageantClientVtable *vt; + PageantClientInfo *info; /* used by the central Pageant code */ + + /* Setting this flag prevents the 'log' vtable entry from ever + * being called, so that it's safe to make it NULL. This also + * allows optimisations in the core code (it can avoid entire + * loops that are only used for logging purposes). So you can also + * set it dynamically if you find out at run time that you're not + * doing logging. */ + bool suppress_logging; +}; +struct PageantClientVtable { + void (*log)(PageantClient *pc, PageantClientRequestId *reqid, + const char *fmt, va_list ap); + void (*got_response)(PageantClient *pc, PageantClientRequestId *reqid, + ptrlen response); + bool (*ask_passphrase)(PageantClient *pc, PageantClientDialogId *dlgid, + const char *key_comment); +}; + +static inline void pageant_client_log_v( + PageantClient *pc, PageantClientRequestId *reqid, + const char *fmt, va_list ap) +{ + if (!pc->suppress_logging) + pc->vt->log(pc, reqid, fmt, ap); +} +static inline PRINTF_LIKE(3, 4) void pageant_client_log( + PageantClient *pc, PageantClientRequestId *reqid, const char *fmt, ...) +{ + if (!pc->suppress_logging) { + va_list ap; + va_start(ap, fmt); + pc->vt->log(pc, reqid, fmt, ap); + va_end(ap); + } +} +static inline void pageant_client_got_response( + PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) +{ pc->vt->got_response(pc, reqid, response); } +static inline bool pageant_client_ask_passphrase( + PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) +{ return pc->vt->ask_passphrase(pc, dlgid, comment); } + +/* PageantClientRequestId is used to match up responses to the agent + * requests they refer to. A client may allocate one of these for each + * call to pageant_handle_request, (probably as a subfield of some + * larger struct on the client side) and expect the same pointer to be + * passed back in pageant_client_got_response. */ +struct PageantClientRequestId { int unused_; }; /* * Initial setup. */ void pageant_init(void); +/* + * Register and unregister PageantClients. This is necessary so that + * when a PageantClient goes away, any unfinished asynchronous + * requests can be cleaned up. + * + * pageant_register_client will fill in pc->id. The client itself + * should not touch that field. + */ +void pageant_register_client(PageantClient *pc); +void pageant_unregister_client(PageantClient *pc); + /* * The main agent function that answers messages. * * Expects a message/length pair as input, minus its initial length * field but still with its type code on the front. * - * Returns a fully formatted message as output, *with* its initial - * length field, and sets *outlen to the full size of that message. + * When a response is ready, the got_response method in the + * PageantClient vtable will be passed it in the form of a ptrlen, + * again minus its length field. */ -void pageant_handle_msg(BinarySink *bs, - const void *msg, int msglen, - void *logctx, pageant_logfn_t logfn); +void pageant_handle_msg(PageantClient *pc, PageantClientRequestId *reqid, + ptrlen msg); /* - * Construct a failure response. Useful for agent front ends which - * suffer a problem before they even get to pageant_handle_msg. - * - * 'log_reason' is only used if logfn is not NULL. + * Send the core Pageant code a response to a passphrase request. */ -void pageant_failure_msg(BinarySink *bs, - const char *log_reason, - void *logctx, pageant_logfn_t logfn); +void pageant_passphrase_request_success(PageantClientDialogId *dlgid, + ptrlen passphrase); +void pageant_passphrase_request_refused(PageantClientDialogId *dlgid); /* * Construct a list of public keys, just as the two LIST_IDENTITIES @@ -49,21 +112,17 @@ void pageant_make_keylist1(BinarySink *); void pageant_make_keylist2(BinarySink *); /* - * Accessor functions for Pageant's internal key lists. Fetch the nth - * key; count the keys; attempt to add a key (returning true on - * success, in which case the ownership of the key structure has been - * taken over by pageant.c); attempt to delete a key (returning true - * on success, in which case the ownership of the key structure is - * passed back to the client). + * Accessor functions for Pageant's internal key lists, used by GUI + * Pageant, to count the keys, to delete a key, or to re-encrypt a + * decrypted-on-demand key (SSH-2 only). */ -RSAKey *pageant_nth_ssh1_key(int i); -ssh2_userkey *pageant_nth_ssh2_key(int i); int pageant_count_ssh1_keys(void); int pageant_count_ssh2_keys(void); -bool pageant_add_ssh1_key(RSAKey *rkey); -bool pageant_add_ssh2_key(ssh2_userkey *skey); -bool pageant_delete_ssh1_key(RSAKey *rkey); -bool pageant_delete_ssh2_key(ssh2_userkey *skey); +bool pageant_delete_nth_ssh1_key(int i); +bool pageant_delete_nth_ssh2_key(int i); +bool pageant_reencrypt_nth_ssh2_key(int i); +void pageant_delete_all(void); +void pageant_reencrypt_all(void); /* * This callback must be provided by the Pageant front end code. @@ -83,11 +142,48 @@ void keylist_update(void); * socket pointer. Also, provide a logging function later if you want * to. */ +typedef struct PageantListenerClientVtable PageantListenerClientVtable; +typedef struct PageantListenerClient PageantListenerClient; +struct PageantListenerClient { + const PageantListenerClientVtable *vt; + /* suppress_logging flag works similarly to the one in + * PageantClient, but it is only read when a new connection comes + * in. So if you do need to change it in mid-run, expect existing + * agent connections to still use the old value. */ + bool suppress_logging; +}; +struct PageantListenerClientVtable { + void (*log)(PageantListenerClient *, const char *fmt, va_list ap); + bool (*ask_passphrase)(PageantListenerClient *pc, + PageantClientDialogId *dlgid, + const char *key_comment); +}; + +static inline void pageant_listener_client_log_v( + PageantListenerClient *plc, const char *fmt, va_list ap) +{ + if (!plc->suppress_logging) + plc->vt->log(plc, fmt, ap); +} +static inline PRINTF_LIKE(2, 3) void pageant_listener_client_log( + PageantListenerClient *plc, const char *fmt, ...) +{ + if (!plc->suppress_logging) { + va_list ap; + va_start(ap, fmt); + plc->vt->log(plc, fmt, ap); + va_end(ap); + } +} +static inline bool pageant_listener_client_ask_passphrase( + PageantListenerClient *plc, PageantClientDialogId *dlgid, + const char *comment) +{ return plc->vt->ask_passphrase(plc, dlgid, comment); } + struct pageant_listen_state; -struct pageant_listen_state *pageant_listener_new(Plug **plug); +struct pageant_listen_state *pageant_listener_new( + Plug **plug, PageantListenerClient *plc); void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket *); -void pageant_listener_set_logfn(struct pageant_listen_state *pl, - void *logctx, pageant_logfn_t logfn); void pageant_listener_free(struct pageant_listen_state *pl); /* @@ -96,10 +192,6 @@ void pageant_listener_free(struct pageant_listen_state *pl); * process. (On at least one platform we want to do this in an * agnostic way between the two situations.) * - * pageant_get_keylist{1,2} work just like pageant_make_keylist{1,2} - * above, except that they can also cope if they have to contact an - * external agent. - * * pageant_add_keyfile() is used to load a private key from a file and * add it to the agent. Initially, you should call it with passphrase * NULL, and it will check if the key is already in the agent, and @@ -114,15 +206,15 @@ void pageant_listener_free(struct pageant_listen_state *pl); * for keys that have the same trust properties). Call * pageant_forget_passphrases() to get rid of them all. */ -void *pageant_get_keylist1(int *length); -void *pageant_get_keylist2(int *length); enum { PAGEANT_ACTION_OK, /* success; no further action needed */ PAGEANT_ACTION_FAILURE, /* failure; *retstr is error message */ - PAGEANT_ACTION_NEED_PP /* need passphrase: *retstr is key comment */ + PAGEANT_ACTION_NEED_PP, /* need passphrase: *retstr is key comment */ + PAGEANT_ACTION_WARNING, /* success but with a warning message; + * *retstr is warning message */ }; int pageant_add_keyfile(Filename *filename, const char *passphrase, - char **retstr); + char **retstr, bool add_encrypted); void pageant_forget_passphrases(void); struct pageant_pubkey { @@ -136,11 +228,30 @@ struct pageant_pubkey { struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key); void pageant_pubkey_free(struct pageant_pubkey *key); -typedef void (*pageant_key_enum_fn_t)(void *ctx, - const char *fingerprint, - const char *comment, +typedef void (*pageant_key_enum_fn_t)(void *ctx, char **fingerprints, + const char *comment, uint32_t ext_flags, struct pageant_pubkey *key); int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, char **retstr); int pageant_delete_key(struct pageant_pubkey *key, char **retstr); int pageant_delete_all_keys(char **retstr); +int pageant_reencrypt_key(struct pageant_pubkey *key, char **retstr); +int pageant_reencrypt_all_keys(char **retstr); +int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out, + uint32_t flags, char **retstr); + +/* + * Definitions for agent protocol extensions. + */ +#define PUTTYEXT(base) base "@putty.projects.tartarus.org" + +#define KNOWN_EXTENSIONS(X) \ + X(EXT_QUERY, "query") \ + X(EXT_ADD_PPK, PUTTYEXT("add-ppk")) \ + X(EXT_REENCRYPT, PUTTYEXT("reencrypt")) \ + X(EXT_REENCRYPT_ALL, PUTTYEXT("reencrypt-all")) \ + X(EXT_LIST_EXTENDED, PUTTYEXT("list-extended")) \ + /* end of list */ + +#define LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE 1 +#define LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY 2 diff --git a/pockle.c b/pockle.c new file mode 100644 index 0000000..60017e3 --- /dev/null +++ b/pockle.c @@ -0,0 +1,450 @@ +#include +#include "ssh.h" +#include "sshkeygen.h" +#include "mpint.h" +#include "mpunsafe.h" +#include "tree234.h" + +typedef struct PocklePrimeRecord PocklePrimeRecord; + +struct Pockle { + tree234 *tree; + + PocklePrimeRecord **list; + size_t nlist, listsize; +}; + +struct PocklePrimeRecord { + mp_int *prime; + PocklePrimeRecord **factors; + size_t nfactors; + mp_int *witness; + + size_t index; /* index in pockle->list */ +}; + +static int ppr_cmp(void *av, void *bv) +{ + PocklePrimeRecord *a = (PocklePrimeRecord *)av; + PocklePrimeRecord *b = (PocklePrimeRecord *)bv; + return mp_cmp_hs(a->prime, b->prime) - mp_cmp_hs(b->prime, a->prime); +} + +static int ppr_find(void *av, void *bv) +{ + mp_int *a = (mp_int *)av; + PocklePrimeRecord *b = (PocklePrimeRecord *)bv; + return mp_cmp_hs(a, b->prime) - mp_cmp_hs(b->prime, a); +} + +Pockle *pockle_new(void) +{ + Pockle *pockle = snew(Pockle); + pockle->tree = newtree234(ppr_cmp); + pockle->list = NULL; + pockle->nlist = pockle->listsize = 0; + return pockle; +} + +void pockle_free(Pockle *pockle) +{ + pockle_release(pockle, 0); + assert(count234(pockle->tree) == 0); + freetree234(pockle->tree); + sfree(pockle->list); + sfree(pockle); +} + +static PockleStatus pockle_insert(Pockle *pockle, mp_int *p, mp_int **factors, + size_t nfactors, mp_int *w) +{ + PocklePrimeRecord *pr = snew(PocklePrimeRecord); + pr->prime = mp_copy(p); + + PocklePrimeRecord *found = add234(pockle->tree, pr); + if (pr != found) { + /* it was already in there */ + mp_free(pr->prime); + sfree(pr); + return POCKLE_OK; + } + + if (w) { + pr->factors = snewn(nfactors, PocklePrimeRecord *); + for (size_t i = 0; i < nfactors; i++) { + pr->factors[i] = find234(pockle->tree, factors[i], ppr_find); + assert(pr->factors[i]); + } + pr->nfactors = nfactors; + pr->witness = mp_copy(w); + } else { + pr->factors = NULL; + pr->nfactors = 0; + pr->witness = NULL; + } + pr->index = pockle->nlist; + + sgrowarray(pockle->list, pockle->listsize, pockle->nlist); + pockle->list[pockle->nlist++] = pr; + return POCKLE_OK; +} + +size_t pockle_mark(Pockle *pockle) +{ + return pockle->nlist; +} + +void pockle_release(Pockle *pockle, size_t mark) +{ + while (pockle->nlist > mark) { + PocklePrimeRecord *pr = pockle->list[--pockle->nlist]; + del234(pockle->tree, pr); + mp_free(pr->prime); + if (pr->witness) + mp_free(pr->witness); + sfree(pr->factors); + sfree(pr); + } +} + +PockleStatus pockle_add_small_prime(Pockle *pockle, mp_int *p) +{ + if (mp_hs_integer(p, (1ULL << 32))) + return POCKLE_SMALL_PRIME_NOT_SMALL; + + uint32_t val = mp_get_integer(p); + + if (val < 2) + return POCKLE_PRIME_SMALLER_THAN_2; + + init_smallprimes(); + for (size_t i = 0; i < NSMALLPRIMES; i++) { + if (val == smallprimes[i]) + break; /* success */ + if (val % smallprimes[i] == 0) + return POCKLE_SMALL_PRIME_NOT_PRIME; + } + + return pockle_insert(pockle, p, NULL, 0, NULL); +} + +PockleStatus pockle_add_prime(Pockle *pockle, mp_int *p, + mp_int **factors, size_t nfactors, + mp_int *witness) +{ + MontyContext *mc = NULL; + mp_int *x = NULL, *f = NULL, *w = NULL; + PockleStatus status; + + /* + * We're going to try to verify that p is prime by using + * Pocklington's theorem. The idea is that we're given w such that + * w^{p-1} == 1 (mod p) (1) + * and for a collection of primes q | p-1, + * w^{(p-1)/q} - 1 is coprime to p. (2) + * + * Suppose r is a prime factor of p itself. Consider the + * multiplicative order of w mod r. By (1), r | w^{p-1}-1. But by + * (2), r does not divide w^{(p-1)/q}-1. So the order of w mod r + * is a factor of p-1, but not a factor of (p-1)/q. Hence, the + * largest power of q that divides p-1 must also divide ord w. + * + * Repeating this reasoning for all q, we find that the product of + * all the q (which we'll denote f) must divide ord w, which in + * turn divides r-1. So f | r-1 for any r | p. + * + * In particular, this means f < r. That is, all primes r | p are + * bigger than f. So if f > sqrt(p), then we've shown p is prime, + * because otherwise it would have to be the product of at least + * two factors bigger than its own square root. + * + * With an extra check, we can also show p to be prime even if + * we're only given enough factors to make f > cbrt(p). See below + * for that part, when we come to it. + */ + + /* + * Start by checking p > 1. It certainly can't be prime otherwise! + * (And since we're going to prove it prime by showing all its + * prime factors are large, we do also have to know it _has_ at + * least one prime factor for that to tell us anything.) + */ + if (!mp_hs_integer(p, 2)) + return POCKLE_PRIME_SMALLER_THAN_2; + + /* + * Check that all the factors we've been given really are primes + * (in the sense that we already had them in our index). Make the + * product f, and check it really does divide p-1. + */ + x = mp_copy(p); + mp_sub_integer_into(x, x, 1); + f = mp_from_integer(1); + for (size_t i = 0; i < nfactors; i++) { + mp_int *q = factors[i]; + + if (!find234(pockle->tree, q, ppr_find)) { + status = POCKLE_FACTOR_NOT_KNOWN_PRIME; + goto out; + } + + mp_int *quotient = mp_new(mp_max_bits(x)); + mp_int *residue = mp_new(mp_max_bits(q)); + mp_divmod_into(x, q, quotient, residue); + + unsigned exact = mp_eq_integer(residue, 0); + mp_free(residue); + + mp_free(x); + x = quotient; + + if (!exact) { + status = POCKLE_FACTOR_NOT_A_FACTOR; + goto out; + } + + mp_int *tmp = f; + f = mp_unsafe_shrink(mp_mul(tmp, q)); + mp_free(tmp); + } + + /* + * Check that f > cbrt(p). + */ + mp_int *f2 = mp_mul(f, f); + mp_int *f3 = mp_mul(f2, f); + bool too_big = mp_cmp_hs(p, f3); + mp_free(f3); + mp_free(f2); + if (too_big) { + status = POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL; + goto out; + } + + /* + * Now do the extra check that allows us to get away with only + * having f > cbrt(p) instead of f > sqrt(p). + * + * If we can show that f | r-1 for any r | p, then we've ruled out + * p being a product of _more_ than two primes (because then it + * would be the product of at least three things bigger than its + * own cube root). But we still have to rule out it being a + * product of exactly two. + * + * Suppose for the sake of contradiction that p is the product of + * two prime factors. We know both of those factors would have to + * be congruent to 1 mod f. So we'd have to have + * + * p = (uf+1)(vf+1) = (uv)f^2 + (u+v)f + 1 (3) + * + * We can't have uv >= f, or else that expression would come to at + * least f^3, i.e. it would exceed p. So uv < f. Hence, u,v < f as + * well. + * + * Can we have u+v >= f? If we did, then we could write v >= f-u, + * and hence f > uv >= u(f-u). That can be rearranged to show that + * u^2 > (u-1)f; decrementing the LHS makes the inequality no + * longer necessarily strict, so we have u^2-1 >= (u-1)f, and + * dividing off u-1 gives u+1 >= f. But we know u < f, so the only + * way this could happen would be if u=f-1, which makes v=1. But + * _then_ (3) gives us p = (f-1)f^2 + f^2 + 1 = f^3+1. But that + * can't be true if f^3 > p. So we can't have u+v >= f either, by + * contradiction. + * + * After all that, what have we shown? We've shown that we can + * write p = (uv)f^2 + (u+v)f + 1, with both uv and u+v strictly + * less than f. In other words, if you write down p in base f, it + * has exactly three digits, and they are uv, u+v and 1. + * + * But that means we can _find_ u and v: we know p and f, so we + * can just extract those digits of p's base-f representation. + * Once we've done so, they give the sum and product of the + * potential u,v. And given the sum and product of two numbers, + * you can make a quadratic which has those numbers as roots. + * + * We don't actually have to _solve_ the quadratic: all we have to + * do is check if its discriminant is a perfect square. If not, + * we'll know that no integers u,v can match this description. + */ + { + /* We already have x = (p-1)/f. So we just need to write x in + * the form aF + b, and then we have a=uv and b=u+v. */ + mp_int *a = mp_new(mp_max_bits(x)); + mp_int *b = mp_new(mp_max_bits(f)); + mp_divmod_into(x, f, a, b); + assert(!mp_cmp_hs(a, f)); + assert(!mp_cmp_hs(b, f)); + + /* If a=0, then that means p < f^2, so we don't need to do + * this check at all: the straightforward Pocklington theorem + * is all we need. */ + if (!mp_eq_integer(a, 0)) { + unsigned perfect_square = 0; + + mp_int *bsq = mp_mul(b, b); + mp_lshift_fixed_into(a, a, 2); + + if (mp_cmp_hs(bsq, a)) { + /* b^2-4a is non-negative, so it might be a square. + * Check it. */ + mp_int *discriminant = mp_sub(bsq, a); + mp_int *remainder = mp_new(mp_max_bits(discriminant)); + mp_int *root = mp_nthroot(discriminant, 2, remainder); + perfect_square = mp_eq_integer(remainder, 0); + mp_free(discriminant); + mp_free(root); + mp_free(remainder); + } + + mp_free(bsq); + + if (perfect_square) { + mp_free(b); + mp_free(a); + status = POCKLE_DISCRIMINANT_IS_SQUARE; + goto out; + } + } + mp_free(b); + mp_free(a); + } + + /* + * Now we've done all the checks that are cheaper than a modpow, + * so we've ruled out as many things as possible before having to + * do any hard work. But there's nothing for it now: make a + * MontyContext. + */ + mc = monty_new(p); + w = monty_import(mc, witness); + + /* + * The initial Fermat check: is w^{p-1} itself congruent to 1 mod + * p? + */ + { + mp_int *pm1 = mp_copy(p); + mp_sub_integer_into(pm1, pm1, 1); + mp_int *power = monty_pow(mc, w, pm1); + unsigned fermat_pass = mp_cmp_eq(power, monty_identity(mc)); + mp_free(power); + mp_free(pm1); + + if (!fermat_pass) { + status = POCKLE_FERMAT_TEST_FAILED; + goto out; + } + } + + /* + * And now, for each factor q, is w^{(p-1)/q}-1 coprime to p? + */ + for (size_t i = 0; i < nfactors; i++) { + mp_int *q = factors[i]; + mp_int *exponent = mp_unsafe_shrink(mp_div(p, q)); + mp_int *power = monty_pow(mc, w, exponent); + mp_int *power_extracted = monty_export(mc, power); + mp_sub_integer_into(power_extracted, power_extracted, 1); + + unsigned coprime = mp_coprime(power_extracted, p); + if (!coprime) { + /* + * If w^{(p-1)/q}-1 is not coprime to p, the test has + * failed. But it makes a difference why. If the power of + * w turned out to be 1, so that we took gcd(1-1,p) = + * gcd(0,p) = p, that's like an inconclusive Fermat or M-R + * test: it might just mean you picked a witness integer + * that wasn't a primitive root. But if the power is any + * _other_ value mod p that is not coprime to p, it means + * we've detected that the number is *actually not prime*! + */ + if (mp_eq_integer(power_extracted, 0)) + status = POCKLE_WITNESS_POWER_IS_1; + else + status = POCKLE_WITNESS_POWER_NOT_COPRIME; + } + + mp_free(exponent); + mp_free(power); + mp_free(power_extracted); + + if (!coprime) + goto out; /* with the status we set up above */ + } + + /* + * Success! p is prime. Insert it into our tree234 of known + * primes, so that future calls to this function can cite it in + * evidence of larger numbers' primality. + */ + status = pockle_insert(pockle, p, factors, nfactors, witness); + + out: + if (x) + mp_free(x); + if (f) + mp_free(f); + if (w) + mp_free(w); + if (mc) + monty_free(mc); + return status; +} + +static void mp_write_decimal(strbuf *sb, mp_int *x) +{ + char *s = mp_get_decimal(x); + ptrlen pl = ptrlen_from_asciz(s); + put_datapl(sb, pl); + smemclr(s, pl.len); + sfree(s); +} + +strbuf *pockle_mpu(Pockle *pockle, mp_int *p) +{ + strbuf *sb = strbuf_new_nm(); + PocklePrimeRecord *pr = find234(pockle->tree, p, ppr_find); + assert(pr); + + bool *needed = snewn(pockle->nlist, bool); + memset(needed, 0, pockle->nlist * sizeof(bool)); + needed[pr->index] = true; + + strbuf_catf(sb, "[MPU - Primality Certificate]\nVersion 1.0\nBase 10\n\n" + "Proof for:\nN "); + mp_write_decimal(sb, p); + strbuf_catf(sb, "\n"); + + for (size_t index = pockle->nlist; index-- > 0 ;) { + if (!needed[index]) + continue; + pr = pockle->list[index]; + + if (mp_get_nbits(pr->prime) <= 64) { + strbuf_catf(sb, "\nType Small\nN "); + mp_write_decimal(sb, pr->prime); + strbuf_catf(sb, "\n"); + } else { + assert(pr->witness); + strbuf_catf(sb, "\nType BLS5\nN "); + mp_write_decimal(sb, pr->prime); + strbuf_catf(sb, "\n"); + for (size_t i = 0; i < pr->nfactors; i++) { + strbuf_catf(sb, "Q[%"SIZEu"] ", i+1); + mp_write_decimal(sb, pr->factors[i]->prime); + assert(pr->factors[i]->index < index); + needed[pr->factors[i]->index] = true; + strbuf_catf(sb, "\n"); + } + for (size_t i = 0; i < pr->nfactors + 1; i++) { + strbuf_catf(sb, "A[%"SIZEu"] ", i); + mp_write_decimal(sb, pr->witness); + strbuf_catf(sb, "\n"); + } + strbuf_catf(sb, "----\n"); + } + } + sfree(needed); + + return sb; +} diff --git a/portfwd.c b/portfwd.c index 2fbaa29..f3349b8 100644 --- a/portfwd.c +++ b/portfwd.c @@ -95,13 +95,13 @@ static void free_portlistener_state(struct PortListener *pl) sfree(pl); } -static void pfd_log(Plug *plug, int type, SockAddr *addr, int port, +static void pfd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { /* we have to dump these since we have no interface to logging.c */ } -static void pfl_log(Plug *plug, int type, SockAddr *addr, int port, +static void pfl_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { /* we have to dump these since we have no interface to logging.c */ @@ -408,7 +408,7 @@ static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len) * Freeze the socket until the SSH server confirms the * connection. */ - sk_set_frozen(pf->s, 1); + sk_set_frozen(pf->s, true); pf->c = wrap_lportfwd_open(pf->cl, pf->hostname, pf->port, pf->s, &pf->chan); @@ -427,11 +427,10 @@ static void pfd_sent(Plug *plug, size_t bufsize) } static const PlugVtable PortForwarding_plugvt = { - pfd_log, - pfd_closing, - pfd_receive, - pfd_sent, - NULL + .log = pfd_log, + .closing = pfd_closing, + .receive = pfd_receive, + .sent = pfd_sent, }; static void pfd_chan_free(Channel *chan); @@ -443,32 +442,32 @@ static void pfd_send_eof(Channel *chan); static void pfd_set_input_wanted(Channel *chan, bool wanted); static char *pfd_log_close_msg(Channel *chan); -static const struct ChannelVtable PortForwarding_channelvt = { - pfd_chan_free, - pfd_open_confirmation, - pfd_open_failure, - pfd_send, - pfd_send_eof, - pfd_set_input_wanted, - pfd_log_close_msg, - chan_default_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - chan_no_request_response, +static const ChannelVtable PortForwarding_channelvt = { + .free = pfd_chan_free, + .open_confirmation = pfd_open_confirmation, + .open_failed = pfd_open_failure, + .send = pfd_send, + .send_eof = pfd_send_eof, + .set_input_wanted = pfd_set_input_wanted, + .log_close_msg = pfd_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, }; -Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug) +Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug, bool start_ready) { struct PortForwarding *pf; @@ -482,7 +481,7 @@ Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug) pf->cl = cl; pf->input_wanted = true; - pf->ready = false; + pf->ready = start_ready; pf->socks_state = SOCKS_NONE; pf->hostname = NULL; @@ -523,7 +522,7 @@ static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) Socket *s; const char *err; - chan = portfwd_raw_new(pl->cl, &plug); + chan = portfwd_raw_new(pl->cl, &plug, false); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) { portfwd_raw_free(chan); @@ -538,7 +537,7 @@ static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) pf->socksbuf = strbuf_new(); pf->socksbuf_consumed = 0; pf->port = 0; /* "hostname" buffer is so far empty */ - sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */ + sk_set_frozen(s, false); /* we want to receive SOCKS _now_! */ } else { pf->hostname = dupstr(pl->hostname); pf->port = pl->port; @@ -551,11 +550,9 @@ static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) } static const PlugVtable PortListener_plugvt = { - pfl_log, - pfl_closing, - NULL, /* recv */ - NULL, /* send */ - pfl_accepting + .log = pfl_log, + .closing = pfl_closing, + .accepting = pfl_accepting, }; /* @@ -666,7 +663,7 @@ static void pfd_open_confirmation(Channel *chan) PortForwarding *pf = container_of(chan, PortForwarding, chan); pf->ready = true; - sk_set_frozen(pf->s, 0); + sk_set_frozen(pf->s, false); sk_write(pf->s, NULL, 0); if (pf->socksbuf) { sshfwd_write(pf->c, pf->socksbuf->u + pf->socksbuf_consumed, @@ -732,7 +729,7 @@ static int pfr_cmp(void *av, void *bv) return 0; } -void pfr_free(PortFwdRecord *pfr) +static void pfr_free(PortFwdRecord *pfr) { /* Dispose of any listening socket. */ if (pfr->local) diff --git a/primecandidate.c b/primecandidate.c new file mode 100644 index 0000000..cf55919 --- /dev/null +++ b/primecandidate.c @@ -0,0 +1,445 @@ +/* + * primecandidate.c: implementation of the PrimeCandidateSource + * abstraction declared in sshkeygen.h. + */ + +#include +#include "ssh.h" +#include "mpint.h" +#include "mpunsafe.h" +#include "sshkeygen.h" + +struct avoid { + unsigned mod, res; +}; + +struct PrimeCandidateSource { + unsigned bits; + bool ready, try_sophie_germain; + bool one_shot, thrown_away_my_shot; + + /* We'll start by making up a random number strictly less than this ... */ + mp_int *limit; + + /* ... then we'll multiply by 'factor', and add 'addend'. */ + mp_int *factor, *addend; + + /* Then we'll try to add a small multiple of 'factor' to it to + * avoid it being a multiple of any small prime. Also, for RSA, we + * may need to avoid it being _this_ multiple of _this_: */ + unsigned avoid_residue, avoid_modulus; + + /* Once we're actually running, this will be the complete list of + * (modulus, residue) pairs we want to avoid. */ + struct avoid *avoids; + size_t navoids, avoidsize; + + /* List of known primes that our number will be congruent to 1 modulo */ + mp_int **kps; + size_t nkps, kpsize; +}; + +PrimeCandidateSource *pcs_new_with_firstbits(unsigned bits, + unsigned first, unsigned nfirst) +{ + PrimeCandidateSource *s = snew(PrimeCandidateSource); + + assert(first >> (nfirst-1) == 1); + + s->bits = bits; + s->ready = false; + s->try_sophie_germain = false; + s->one_shot = false; + s->thrown_away_my_shot = false; + + s->kps = NULL; + s->nkps = s->kpsize = 0; + + s->avoids = NULL; + s->navoids = s->avoidsize = 0; + + /* Make the number that's the lower limit of our range */ + mp_int *firstmp = mp_from_integer(first); + mp_int *base = mp_lshift_fixed(firstmp, bits - nfirst); + mp_free(firstmp); + + /* Set the low bit of that, because all (nontrivial) primes are odd */ + mp_set_bit(base, 0, 1); + + /* That's our addend. Now initialise factor to 2, to ensure we + * only generate odd numbers */ + s->factor = mp_from_integer(2); + s->addend = base; + + /* And that means the limit of our random numbers must be one + * factor of two _less_ than the position of the low bit of + * 'first', because we'll be multiplying the random number by + * 2 immediately afterwards. */ + s->limit = mp_power_2(bits - nfirst - 1); + + /* avoid_modulus == 0 signals that there's no extra residue to avoid */ + s->avoid_residue = 1; + s->avoid_modulus = 0; + + return s; +} + +PrimeCandidateSource *pcs_new(unsigned bits) +{ + return pcs_new_with_firstbits(bits, 1, 1); +} + +void pcs_free(PrimeCandidateSource *s) +{ + mp_free(s->limit); + mp_free(s->factor); + mp_free(s->addend); + for (size_t i = 0; i < s->nkps; i++) + mp_free(s->kps[i]); + sfree(s->avoids); + sfree(s->kps); + sfree(s); +} + +void pcs_try_sophie_germain(PrimeCandidateSource *s) +{ + s->try_sophie_germain = true; +} + +void pcs_set_oneshot(PrimeCandidateSource *s) +{ + s->one_shot = true; +} + +static void pcs_require_residue_inner(PrimeCandidateSource *s, + mp_int *mod, mp_int *res) +{ + /* + * We already have a factor and addend. Ensure this one doesn't + * contradict it. + */ + mp_int *gcd = mp_gcd(mod, s->factor); + mp_int *test1 = mp_mod(s->addend, gcd); + mp_int *test2 = mp_mod(res, gcd); + assert(mp_cmp_eq(test1, test2)); + mp_free(test1); + mp_free(test2); + + /* + * Reduce our input factor and addend, which are constraints on + * the ultimate output number, so that they're constraints on the + * initial cofactor we're going to make up. + * + * If we're generating x and we want to ensure ax+b == r (mod m), + * how does that work? We've already checked that b == r modulo g + * = gcd(a,m), i.e. r-b is a multiple of g, and so are a and m. So + * let's write a=gA, m=gM, (r-b)=gR, and then we can start by + * dividing that off: + * + * ax == r-b (mod m ) + * => gAx == gR (mod gM) + * => Ax == R (mod M) + * + * Now the moduli A,M are coprime, which makes things easier. + * + * We're going to need to generate the x in this equation by + * generating a new smaller value y, multiplying it by M, and + * adding some constant K. So we have x = My + K, and we need to + * work out what K will satisfy the above equation. In other + * words, we need A(My+K) == R (mod M), and the AMy term vanishes, + * so we just need AK == R (mod M). So our congruence is solved by + * setting K to be R * A^{-1} mod M. + */ + mp_int *A = mp_div(s->factor, gcd); + mp_int *M = mp_div(mod, gcd); + mp_int *Rpre = mp_modsub(res, s->addend, mod); + mp_int *R = mp_div(Rpre, gcd); + mp_int *Ainv = mp_invert(A, M); + mp_int *K = mp_modmul(R, Ainv, M); + + mp_free(gcd); + mp_free(Rpre); + mp_free(Ainv); + mp_free(A); + mp_free(R); + + /* + * So we know we have to transform our existing (factor, addend) + * pair into (factor * M, addend * factor * K). Now we just need + * to work out what the limit should be on the random value we're + * generating. + * + * If we need My+K < old_limit, then y < (old_limit-K)/M. But the + * RHS is a fraction, so in integers, we need y < ceil of it. + */ + assert(!mp_cmp_hs(K, s->limit)); + mp_int *dividend = mp_add(s->limit, M); + mp_sub_integer_into(dividend, dividend, 1); + mp_sub_into(dividend, dividend, K); + mp_free(s->limit); + s->limit = mp_div(dividend, M); + mp_free(dividend); + + /* + * Now just update the real factor and addend, and we're done. + */ + + mp_int *addend_old = s->addend; + mp_int *tmp = mp_mul(s->factor, K); /* use the _old_ value of factor */ + s->addend = mp_add(s->addend, tmp); + mp_free(tmp); + mp_free(addend_old); + + mp_int *factor_old = s->factor; + s->factor = mp_mul(s->factor, M); + mp_free(factor_old); + + mp_free(M); + mp_free(K); + s->factor = mp_unsafe_shrink(s->factor); + s->addend = mp_unsafe_shrink(s->addend); + s->limit = mp_unsafe_shrink(s->limit); +} + +void pcs_require_residue(PrimeCandidateSource *s, + mp_int *mod, mp_int *res_orig) +{ + /* + * Reduce the input residue to its least non-negative value, in + * case it was given as a larger equivalent value. + */ + mp_int *res_reduced = mp_mod(res_orig, mod); + pcs_require_residue_inner(s, mod, res_reduced); + mp_free(res_reduced); +} + +void pcs_require_residue_1(PrimeCandidateSource *s, mp_int *mod) +{ + mp_int *res = mp_from_integer(1); + pcs_require_residue(s, mod, res); + mp_free(res); +} + +void pcs_require_residue_1_mod_prime(PrimeCandidateSource *s, mp_int *mod) +{ + pcs_require_residue_1(s, mod); + + sgrowarray(s->kps, s->kpsize, s->nkps); + s->kps[s->nkps++] = mp_copy(mod); +} + +void pcs_avoid_residue_small(PrimeCandidateSource *s, + unsigned mod, unsigned res) +{ + assert(!s->avoid_modulus); /* can't cope with more than one */ + s->avoid_modulus = mod; + s->avoid_residue = res % mod; /* reduce, just in case */ +} + +static int avoid_cmp(const void *av, const void *bv) +{ + const struct avoid *a = (const struct avoid *)av; + const struct avoid *b = (const struct avoid *)bv; + return a->mod < b->mod ? -1 : a->mod > b->mod ? +1 : 0; +} + +static uint64_t invert(uint64_t a, uint64_t m) +{ + int64_t v0 = a, i0 = 1; + int64_t v1 = m, i1 = 0; + while (v0) { + int64_t tmp, q = v1 / v0; + tmp = v0; v0 = v1 - q*v0; v1 = tmp; + tmp = i0; i0 = i1 - q*i0; i1 = tmp; + } + assert(v1 == 1 || v1 == -1); + return i1 * v1; +} + +void pcs_ready(PrimeCandidateSource *s) +{ + /* + * List all the small (modulus, residue) pairs we want to avoid. + */ + + init_smallprimes(); + +#define ADD_AVOID(newmod, newres) do { \ + sgrowarray(s->avoids, s->avoidsize, s->navoids); \ + s->avoids[s->navoids].mod = (newmod); \ + s->avoids[s->navoids].res = (newres); \ + s->navoids++; \ + } while (0) + + unsigned limit = (mp_hs_integer(s->addend, 65536) ? 65536 : + mp_get_integer(s->addend)); + + /* + * Don't be divisible by any small prime, or at least, any prime + * smaller than our output number might actually manage to be. (If + * asked to generate a really small prime, it would be + * embarrassing to rule out legitimate answers on the grounds that + * they were divisible by themselves.) + */ + for (size_t i = 0; i < NSMALLPRIMES && smallprimes[i] < limit; i++) + ADD_AVOID(smallprimes[i], 0); + + if (s->try_sophie_germain) { + /* + * If we're aiming to generate a Sophie Germain prime (i.e. p + * such that 2p+1 is also prime), then we also want to ensure + * 2p+1 is not congruent to 0 mod any small prime, because if + * it is, we'll waste a lot of time generating a p for which + * 2p+1 can't possibly work. So we have to avoid an extra + * residue mod each odd q. + * + * We can simplify: 2p+1 == 0 (mod q) + * => 2p == -1 (mod q) + * => p == -2^{-1} (mod q) + * + * There's no need to do Euclid's algorithm to compute those + * inverses, because for any odd q, the modular inverse of -2 + * mod q is just (q-1)/2. (Proof: multiplying it by -2 gives + * 1-q, which is congruent to 1 mod q.) + */ + for (size_t i = 0; i < NSMALLPRIMES && smallprimes[i] < limit; i++) + if (smallprimes[i] != 2) + ADD_AVOID(smallprimes[i], (smallprimes[i] - 1) / 2); + } + + /* + * Finally, if there's a particular modulus and residue we've been + * told to avoid, put it on the list. + */ + if (s->avoid_modulus) + ADD_AVOID(s->avoid_modulus, s->avoid_residue); + +#undef ADD_AVOID + + /* + * Sort our to-avoid list by modulus. Partly this is so that we'll + * check the smaller moduli first during the live runs, which lets + * us spot most failing cases earlier rather than later. Also, it + * brings equal moduli together, so that we can reuse the residue + * we computed from a previous one. + */ + qsort(s->avoids, s->navoids, sizeof(*s->avoids), avoid_cmp); + + /* + * Next, adjust each of these moduli to take account of our factor + * and addend. If we want factor*x+addend to avoid being congruent + * to 'res' modulo 'mod', then x itself must avoid being congruent + * to (res - addend) * factor^{-1}. + * + * If factor == 0 modulo mod, then the answer will have a fixed + * residue anyway, so we can discard it from our list to test. + */ + int64_t factor_m = 0, addend_m = 0, last_mod = 0; + + size_t out = 0; + for (size_t i = 0; i < s->navoids; i++) { + int64_t mod = s->avoids[i].mod, res = s->avoids[i].res; + if (mod != last_mod) { + last_mod = mod; + addend_m = mp_unsafe_mod_integer(s->addend, mod); + factor_m = mp_unsafe_mod_integer(s->factor, mod); + } + + if (factor_m == 0) { + assert(res != addend_m); + continue; + } + + res = (res - addend_m) * invert(factor_m, mod); + res %= mod; + if (res < 0) + res += mod; + + s->avoids[out].mod = mod; + s->avoids[out].res = res; + out++; + } + + s->navoids = out; + + s->ready = true; +} + +mp_int *pcs_generate(PrimeCandidateSource *s) +{ + assert(s->ready); + if (s->one_shot) { + if (s->thrown_away_my_shot) + return NULL; + s->thrown_away_my_shot = true; + } + + while (true) { + mp_int *x = mp_random_upto(s->limit); + + int64_t x_res = 0, last_mod = 0; + bool ok = true; + + for (size_t i = 0; i < s->navoids; i++) { + int64_t mod = s->avoids[i].mod, avoid_res = s->avoids[i].res; + + if (mod != last_mod) { + last_mod = mod; + x_res = mp_unsafe_mod_integer(x, mod); + } + + if (x_res == avoid_res) { + ok = false; + break; + } + } + + if (!ok) { + mp_free(x); + continue; /* try a new x */ + } + + /* + * We've found a viable x. Make the final output value. + */ + mp_int *toret = mp_new(s->bits); + mp_mul_into(toret, x, s->factor); + mp_add_into(toret, toret, s->addend); + mp_free(x); + return toret; + } +} + +void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out, + mp_int **factor_out, mp_int **addend_out) +{ + *limit_out = mp_copy(pcs->limit); + *factor_out = mp_copy(pcs->factor); + *addend_out = mp_copy(pcs->addend); +} + +unsigned pcs_get_bits(PrimeCandidateSource *pcs) +{ + return pcs->bits; +} + +unsigned pcs_get_bits_remaining(PrimeCandidateSource *pcs) +{ + return mp_get_nbits(pcs->limit); +} + +mp_int *pcs_get_upper_bound(PrimeCandidateSource *pcs) +{ + /* Compute (limit-1) * factor + addend */ + mp_int *tmp = mp_mul(pcs->limit, pcs->factor); + mp_int *bound = mp_add(tmp, pcs->addend); + mp_free(tmp); + mp_sub_into(bound, bound, pcs->factor); + return bound; +} + +mp_int **pcs_get_known_prime_factors(PrimeCandidateSource *pcs, size_t *nout) +{ + *nout = pcs->nkps; + return pcs->kps; +} diff --git a/proxy.c b/proxy.c index ebe7be8..d7069cb 100644 --- a/proxy.c +++ b/proxy.c @@ -65,7 +65,7 @@ void proxy_activate (ProxySocket *p) * unfreezing the actual underlying socket. */ if (!p->freeze) - sk_set_frozen(&p->sock, 0); + sk_set_frozen(&p->sock, false); } /* basic proxy socket functions */ @@ -171,8 +171,8 @@ static const char * sk_proxy_socket_error (Socket *s) /* basic proxy plug functions */ -static void plug_proxy_log(Plug *plug, int type, SockAddr *addr, int port, - const char *error_msg, int error_code) +static void plug_proxy_log(Plug *plug, PlugLogType type, SockAddr *addr, + int port, const char *error_msg, int error_code) { ProxySocket *ps = container_of(plug, ProxySocket, plugimpl); @@ -371,23 +371,23 @@ SockAddr *name_lookup(const char *host, int port, char **canonicalname, } } -static const struct SocketVtable ProxySocket_sockvt = { - sk_proxy_plug, - sk_proxy_close, - sk_proxy_write, - sk_proxy_write_oob, - sk_proxy_write_eof, - sk_proxy_set_frozen, - sk_proxy_socket_error, - NULL, /* peer_info */ +static const SocketVtable ProxySocket_sockvt = { + .plug = sk_proxy_plug, + .close = sk_proxy_close, + .write = sk_proxy_write, + .write_oob = sk_proxy_write_oob, + .write_eof = sk_proxy_write_eof, + .set_frozen = sk_proxy_set_frozen, + .socket_error = sk_proxy_socket_error, + .peer_info = NULL, }; -static const struct PlugVtable ProxySocket_plugvt = { - plug_proxy_log, - plug_proxy_closing, - plug_proxy_receive, - plug_proxy_sent, - plug_proxy_accepting +static const PlugVtable ProxySocket_plugvt = { + .log = plug_proxy_log, + .closing = plug_proxy_closing, + .receive = plug_proxy_receive, + .sent = plug_proxy_sent, + .accepting = plug_proxy_accepting }; Socket *new_connection(SockAddr *addr, const char *hostname, @@ -407,8 +407,7 @@ Socket *new_connection(SockAddr *addr, const char *hostname, if ((sret = platform_new_connection(addr, hostname, port, privport, oobinline, nodelay, keepalive, - plug, conf)) != - NULL) + plug, conf)) != NULL) return sret; ret = snew(ProxySocket); @@ -455,7 +454,7 @@ Socket *new_connection(SockAddr *addr, const char *hostname, conf_get_str(conf, CONF_proxy_host), conf_get_int(conf, CONF_proxy_port), hostname, port); - plug_log(plug, 2, NULL, 0, logmsg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } @@ -463,7 +462,7 @@ Socket *new_connection(SockAddr *addr, const char *hostname, char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host), conf_get_int(conf, CONF_addressfamily), "proxy"); - plug_log(plug, 2, NULL, 0, logmsg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } @@ -484,7 +483,7 @@ Socket *new_connection(SockAddr *addr, const char *hostname, logmsg = dupprintf("Connecting to %s proxy at %s port %d", proxy_type, addrbuf, conf_get_int(conf, CONF_proxy_port)); - plug_log(plug, 2, NULL, 0, logmsg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } @@ -499,7 +498,7 @@ Socket *new_connection(SockAddr *addr, const char *hostname, return &ret->sock; /* start the proxy negotiation process... */ - sk_set_frozen(ret->sub_socket, 0); + sk_set_frozen(ret->sub_socket, false); ret->negotiate(ret, PROXY_CHANGE_NEW); return &ret->sock; @@ -766,13 +765,12 @@ int proxy_socks4_negotiate (ProxySocket *p, int change) put_uint16(command, p->remote_port); switch (sk_addrtype(p->remote_addr)) { - case ADDRTYPE_IPV4: - { - char addr[4]; - sk_addrcopy(p->remote_addr, addr); - put_data(command, addr, 4); - break; - } + case ADDRTYPE_IPV4: { + char addr[4]; + sk_addrcopy(p->remote_addr, addr); + put_data(command, addr, 4); + break; + } case ADDRTYPE_NAME: sk_getaddr(p->remote_addr, hostname, lenof(hostname)); put_uint32(command, 1); @@ -1084,19 +1082,18 @@ int proxy_socks5_negotiate (ProxySocket *p, int change) put_byte(command, 4); /* IPv6 */ sk_addrcopy(p->remote_addr, strbuf_append(command, 16)); break; - case ADDRTYPE_NAME: - { - char hostname[512]; - put_byte(command, 3); /* domain name */ - sk_getaddr(p->remote_addr, hostname, lenof(hostname)); - if (!put_pstring(command, hostname)) { - p->error = "Proxy error: SOCKS 5 cannot " - "support host names longer than 255 chars"; - strbuf_free(command); - return 1; - } + case ADDRTYPE_NAME: { + char hostname[512]; + put_byte(command, 3); /* domain name */ + sk_getaddr(p->remote_addr, hostname, lenof(hostname)); + if (!put_pstring(command, hostname)) { + p->error = "Proxy error: SOCKS 5 cannot " + "support host names longer than 255 chars"; + strbuf_free(command); + return 1; } break; + } } put_uint16(command, p->remote_port); @@ -1178,7 +1175,7 @@ int proxy_socks5_negotiate (ProxySocket *p, int change) switch (data[3]) { case 1: len += 4; break; /* IPv4 address */ case 4: len += 16; break;/* IPv6 address */ - case 3: len += (unsigned char)data[4]; break; /* domain name */ + case 3: len += 1+(unsigned char)data[4]; break; /* domain name */ default: plug_closing(p->plug, "Proxy error: SOCKS proxy returned " "unrecognised address format", @@ -1317,41 +1314,40 @@ char *format_telnet_command(SockAddr *addr, int port, Conf *conf) break; case 'x': - case 'X': - { - /* escaped hexadecimal value (ie. \xff) */ - unsigned char v = 0; - int i = 0; - - for (;;) { - eo++; - if (fmt[eo] >= '0' && fmt[eo] <= '9') - v += fmt[eo] - '0'; - else if (fmt[eo] >= 'a' && fmt[eo] <= 'f') - v += fmt[eo] - 'a' + 10; - else if (fmt[eo] >= 'A' && fmt[eo] <= 'F') - v += fmt[eo] - 'A' + 10; - else { - /* non hex character, so we abort and just - * send the whole thing unescaped (including \x) - */ - put_byte(buf, '\\'); - eo = so + 1; - break; - } - - /* we only extract two hex characters */ - if (i == 1) { - put_byte(buf, v); - eo++; - break; - } - - i++; - v <<= 4; - } + case 'X': { + /* escaped hexadecimal value (ie. \xff) */ + unsigned char v = 0; + int i = 0; + + for (;;) { + eo++; + if (fmt[eo] >= '0' && fmt[eo] <= '9') + v += fmt[eo] - '0'; + else if (fmt[eo] >= 'a' && fmt[eo] <= 'f') + v += fmt[eo] - 'a' + 10; + else if (fmt[eo] >= 'A' && fmt[eo] <= 'F') + v += fmt[eo] - 'A' + 10; + else { + /* non hex character, so we abort and just + * send the whole thing unescaped (including \x) + */ + put_byte(buf, '\\'); + eo = so + 1; + break; + } + + /* we only extract two hex characters */ + if (i == 1) { + put_byte(buf, v); + eo++; + break; + } + + i++; + v <<= 4; } break; + } default: put_data(buf, fmt + so, 2); @@ -1456,7 +1452,7 @@ int proxy_telnet_negotiate (ProxySocket *p, int change) *out = '\0'; logmsg = dupprintf("Sending Telnet proxy command: %s", reescaped); - plug_log(p->plug, 2, NULL, 0, logmsg, 0); + plug_log(p->plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); sfree(reescaped); } diff --git a/pscp.c b/pscp.c index bcf18dc..a11d1e1 100644 --- a/pscp.c +++ b/pscp.c @@ -19,7 +19,6 @@ #include #include -#define PUTTY_DO_GLOBALS #include "putty.h" #include "psftp.h" #include "ssh.h" @@ -43,8 +42,8 @@ static bool using_sftp = false; static bool uploading = false; static Backend *backend; -Conf *conf; -bool sent_eof = false; +static Conf *conf; +static bool sent_eof = false; static void source(const char *src); static void rsource(const char *src); @@ -64,24 +63,27 @@ static size_t pscp_output(Seat *, bool is_stderr, const void *, size_t); static bool pscp_eof(Seat *); static const SeatVtable pscp_seat_vt = { - pscp_output, - pscp_eof, - filexfer_get_userpass_input, - nullseat_notify_remote_exit, - console_connection_fatal, - nullseat_update_specials_menu, - nullseat_get_ttymode, - nullseat_set_busy_status, - console_verify_ssh_host_key, - console_confirm_weak_crypto_primitive, - console_confirm_weak_cached_hostkey, - nullseat_is_never_utf8, - nullseat_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - nullseat_get_window_pixel_size, - console_stripctrl_new, - nullseat_set_trust_status_vacuously, + .output = pscp_output, + .eof = pscp_eof, + .get_userpass_input = filexfer_get_userpass_input, + .notify_remote_exit = nullseat_notify_remote_exit, + .connection_fatal = console_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = nullseat_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .verify_ssh_host_key = console_verify_ssh_host_key, + .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = nullseat_get_window_pixel_size, + .stripctrl_new = console_stripctrl_new, + .set_trust_status = nullseat_set_trust_status_vacuously, + .verbose = cmdline_seat_verbose, + .interactive = nullseat_interactive_no, + .get_cursor_position = nullseat_get_cursor_position, }; static Seat pscp_seat[1] = {{ &pscp_seat_vt }}; @@ -113,26 +115,20 @@ static void abandon_stats(void) } } -static void tell_user(FILE *stream, const char *fmt, ...) +static PRINTF_LIKE(2, 3) void tell_user(FILE *stream, const char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); va_end(ap); - str2 = dupcat(str, "\n", NULL); + str2 = dupcat(str, "\n"); sfree(str); abandon_stats(); tell_str(stream, str2); sfree(str2); } -void agent_schedule_callback(void (*callback)(void *, void *, int), - void *callback_ctx, void *data, int len) -{ - unreachable("all PSCP agent requests should be synchronous"); -} - /* * Receive a block of data from the SSH link. Block until all data * is available. @@ -224,14 +220,14 @@ static void ssh_scp_init(void) /* * Print an error message and exit after closing the SSH link. */ -static NORETURN void bump(const char *fmt, ...) +static NORETURN PRINTF_LIKE(1, 2) void bump(const char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); va_end(ap); - str2 = dupcat(str, "\n", NULL); + str2 = dupcat(str, "\n"); sfree(str); abandon_stats(); tell_str(stderr, str2); @@ -306,7 +302,7 @@ static void do_cmd(char *host, char *user, char *cmd) * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ - if (!loaded_session) { + if (!cmdline_loaded_session()) { /* Try to load settings for `host' into a temporary config */ Conf *conf2 = conf_new(); conf_set_str(conf2, CONF_host, ""); @@ -327,10 +323,12 @@ static void do_cmd(char *host, char *user, char *cmd) } /* - * Force use of SSH. (If they got the protocol wrong we assume the - * port is useless too.) + * Force protocol to SSH if the user has somehow contrived to + * select one we don't support (e.g. by loading an inappropriate + * saved session). In that situation we assume the port number is + * useless too.) */ - if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) { conf_set_int(conf, CONF_protocol, PROT_SSH); conf_set_int(conf, CONF_port, 22); } @@ -397,6 +395,17 @@ static void do_cmd(char *host, char *user, char *cmd) } } + /* + * Force protocol to SSH if the user has somehow contrived to + * select one we don't support (e.g. by loading an inappropriate + * saved session). In that situation we assume the port number is + * useless too.) + */ + if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) { + conf_set_int(conf, CONF_protocol, PROT_SSH); + conf_set_int(conf, CONF_port, 22); + } + /* * Disable scary things which shouldn't be enabled for simple * things like SCP and SFTP: agent forwarding, port forwarding, @@ -450,11 +459,13 @@ static void do_cmd(char *host, char *user, char *cmd) } conf_set_bool(conf, CONF_nopty, true); - logctx = log_init(default_logpolicy, conf); + logctx = log_init(console_cli_logpolicy, conf); - platform_psftp_pre_conn_setup(); + platform_psftp_pre_conn_setup(console_cli_logpolicy); - err = backend_init(&ssh_backend, pscp_seat, &backend, logctx, conf, + err = backend_init(backend_vt_from_proto( + conf_get_int(conf, CONF_protocol)), + pscp_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, @@ -762,7 +773,7 @@ int scp_send_filename(const char *name, uint64_t size, int permissions) struct fxp_attrs attrs; if (scp_sftp_targetisdir) { - fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); + fullname = dupcat(scp_sftp_remotepath, "/", name); } else { fullname = dupstr(scp_sftp_remotepath); } @@ -812,6 +823,15 @@ int scp_send_filedata(char *data, int len) } while (!xfer_upload_ready(scp_sftp_xfer)) { + if (toplevel_callback_pending()) { + /* If we have pending callbacks, they might make + * xfer_upload_ready start to return true. So we should + * run them and then re-check xfer_upload_ready, before + * we go as far as waiting for an entire packet to + * arrive. */ + run_toplevel_callbacks(); + continue; + } pktin = sftp_recv(); ret = xfer_upload_gotpkt(scp_sftp_xfer, pktin); if (ret <= 0) { @@ -917,7 +937,7 @@ int scp_send_dirname(const char *name, int modes) bool ret; if (scp_sftp_targetisdir) { - fullname = dupcat(scp_sftp_remotepath, "/", name, NULL); + fullname = dupcat(scp_sftp_remotepath, "/", name); } else { fullname = dupstr(scp_sftp_remotepath); } @@ -1128,8 +1148,7 @@ int scp_get_sink_action(struct scp_sink_action *act) if (head->namepos < head->namelen) { head->matched_something = true; fname = dupcat(head->dirpath, "/", - head->names[head->namepos++].filename, - NULL); + head->names[head->namepos++].filename); must_free_fname = true; } else { /* @@ -1307,7 +1326,7 @@ int scp_get_sink_action(struct scp_sink_action *act) act->action = SCP_SINK_RETRY; } else { act->action = SCP_SINK_DIR; - act->buf->len = 0; + strbuf_clear(act->buf); put_asciz(act->buf, stripslashes(fname, false)); act->name = act->buf->s; act->size = 0; /* duhh, it's a directory */ @@ -1327,7 +1346,7 @@ int scp_get_sink_action(struct scp_sink_action *act) * It's a file. Return SCP_SINK_FILE. */ act->action = SCP_SINK_FILE; - act->buf->len = 0; + strbuf_clear(act->buf); put_asciz(act->buf, stripslashes(fname, false)); act->name = act->buf->s; if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { @@ -1355,7 +1374,7 @@ int scp_get_sink_action(struct scp_sink_action *act) char ch; act->settime = false; - act->buf->len = 0; + strbuf_clear(act->buf); while (!done) { if (!ssh_scp_recv(&ch, 1)) @@ -1388,7 +1407,7 @@ int scp_get_sink_action(struct scp_sink_action *act) &act->mtime, &act->atime) == 2) { act->settime = true; backend_send(backend, "", 1); - act->buf->len = 0; + strbuf_clear(act->buf); continue; /* go round again */ } bump("Protocol error: Illegal time format"); @@ -1542,14 +1561,14 @@ int scp_finish_filerecv(void) * Send an error message to the other side and to the screen. * Increment error counter. */ -static void run_err(const char *fmt, ...) +static PRINTF_LIKE(1, 2) void run_err(const char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); errs++; str = dupvprintf(fmt, ap); - str2 = dupcat("pscp: ", str, "\n", NULL); + str2 = dupcat("pscp: ", str, "\n"); sfree(str); scp_send_errmsg(str2); abandon_stats(); @@ -1697,7 +1716,7 @@ static void rsource(const char *src) if (dir != NULL) { char *filename; while ((filename = read_filename(dir)) != NULL) { - char *foundfile = dupcat(src, "/", filename, NULL); + char *foundfile = dupcat(src, "/", filename); source(foundfile); sfree(foundfile); sfree(filename); @@ -1790,7 +1809,7 @@ static void sink(const char *targ, const char *src) with_stripctrl(santarg, act.name) { tell_user(stderr, "warning: remote host sent a" " compound pathname '%s'", sanname); - tell_user(stderr, " renaming local", + tell_user(stderr, " renaming local" " file to '%s'", santarg); } } @@ -2177,12 +2196,16 @@ static void usage(void) printf(" -l user connect with specified username\n"); printf(" -pw passw login with specified password\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); + printf(" -ssh -ssh-connection\n"); + printf(" force use of particular SSH protocol variant\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); - printf(" -hostkey aa:bb:cc:...\n"); + printf(" -no-trivial-auth\n"); + printf(" disconnect if SSH authentication succeeds trivially\n"); + printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -batch disable all interactive prompts\n"); printf(" -no-sanitise-stderr don't strip control chars from" @@ -2195,6 +2218,9 @@ static void usage(void) printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); + printf(" -logoverwrite\n"); + printf(" -logappend\n"); + printf(" control what happens when a log file already exists\n"); cleanup_exit(1); } @@ -2223,6 +2249,8 @@ const bool share_can_be_upstream = false; static stdio_sink stderr_ss; static StripCtrlChars *stderr_scc; +const unsigned cmdline_tooltype = TOOLTYPE_FILETRANSFER; + /* * Main program. (Called `psftp_main' because it gets called from * *sftp.c; bit silly, I know, but it had to be called _something_.) @@ -2232,20 +2260,11 @@ int psftp_main(int argc, char *argv[]) int i; bool sanitise_stderr = true; - default_protocol = PROT_TELNET; - - flags = 0 -#ifdef FLAG_SYNCAGENT - | FLAG_SYNCAGENT -#endif - ; - cmdline_tooltype = TOOLTYPE_FILETRANSFER; sk_init(); /* Load Default Settings before doing anything else. */ conf = conf_new(); do_defaults(NULL, conf); - loaded_session = false; for (i = 1; i < argc; i++) { int ret; @@ -2258,7 +2277,7 @@ int psftp_main(int argc, char *argv[]) i++; /* skip next argument */ } else if (ret == 1) { /* We have our own verbosity in addition to `flags'. */ - if (flags & FLAG_VERBOSE) + if (cmdline_verbose()) verbose = true; } else if (strcmp(argv[i], "-pgpfp") == 0) { pgp_fingerprints(); diff --git a/psftp.c b/psftp.c index c0d2f9d..c4b5374 100644 --- a/psftp.c +++ b/psftp.c @@ -8,7 +8,6 @@ #include #include -#define PUTTY_DO_GLOBALS #include "putty.h" #include "psftp.h" #include "storage.h" @@ -32,11 +31,11 @@ static void do_sftp_cleanup(void); * sftp client state. */ -char *pwd, *homedir; +static char *pwd, *homedir; static LogContext *psftp_logctx = NULL; static Backend *backend; -Conf *conf; -bool sent_eof = false; +static Conf *conf; +static bool sent_eof = false; /* ------------------------------------------------------------ * Seat vtable. @@ -46,24 +45,27 @@ static size_t psftp_output(Seat *, bool is_stderr, const void *, size_t); static bool psftp_eof(Seat *); static const SeatVtable psftp_seat_vt = { - psftp_output, - psftp_eof, - filexfer_get_userpass_input, - nullseat_notify_remote_exit, - console_connection_fatal, - nullseat_update_specials_menu, - nullseat_get_ttymode, - nullseat_set_busy_status, - console_verify_ssh_host_key, - console_confirm_weak_crypto_primitive, - console_confirm_weak_cached_hostkey, - nullseat_is_never_utf8, - nullseat_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - nullseat_get_window_pixel_size, - console_stripctrl_new, - nullseat_set_trust_status_vacuously, + .output = psftp_output, + .eof = psftp_eof, + .get_userpass_input = filexfer_get_userpass_input, + .notify_remote_exit = nullseat_notify_remote_exit, + .connection_fatal = console_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = nullseat_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .verify_ssh_host_key = console_verify_ssh_host_key, + .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = nullseat_get_window_pixel_size, + .stripctrl_new = console_stripctrl_new, + .set_trust_status = nullseat_set_trust_status_vacuously, + .verbose = cmdline_seat_verbose, + .interactive = nullseat_interactive_yes, + .get_cursor_position = nullseat_get_cursor_position, }; static Seat psftp_seat[1] = {{ &psftp_seat_vt }}; @@ -124,7 +126,7 @@ char *canonify(const char *name) slash = ""; else slash = "/"; - fullname = dupcat(pwd, slash, name, NULL); + fullname = dupcat(pwd, slash, name); } req = fxp_realpath_send(fullname); @@ -203,8 +205,8 @@ char *canonify(const char *name) * component. Concatenate the last component and return. */ returnname = dupcat(canonname, - canonname[strlen(canonname) - 1] == - '/' ? "" : "/", fullname + i + 1, NULL); + (strendswith(canonname, "/") ? "" : "/"), + fullname + i + 1); sfree(fullname); sfree(canonname); return returnname; @@ -376,7 +378,7 @@ bool sftp_get_file(char *fname, char *outfname, bool recurse, bool restart) char *nextfname, *nextoutfname; bool retd; - nextfname = dupcat(fname, "/", ournames[i]->filename, NULL); + nextfname = dupcat(fname, "/", ournames[i]->filename); nextoutfname = dir_file_cat(outfname, ournames[i]->filename); retd = sftp_get_file( nextfname, nextoutfname, recurse, restart); @@ -601,7 +603,7 @@ bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart) if (restart) { while (i < nnames) { char *nextoutfname; - nextoutfname = dupcat(outfname, "/", ournames[i], NULL); + nextoutfname = dupcat(outfname, "/", ournames[i]); req = fxp_stat_send(nextoutfname); pktin = sftp_wait_for_reply(req); result = fxp_stat_recv(pktin, req, &attrs); @@ -625,7 +627,7 @@ bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart) bool retd; nextfname = dir_file_cat(fname, ournames[i]); - nextoutfname = dupcat(outfname, "/", ournames[i], NULL); + nextoutfname = dupcat(outfname, "/", ournames[i]); retd = sftp_put_file(nextfname, nextoutfname, recurse, restart); restart = false; /* after first partial file, do full */ sfree(nextoutfname); @@ -724,6 +726,16 @@ bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart) } } + if (toplevel_callback_pending() && !err && !eof) { + /* If we have pending callbacks, they might make + * xfer_upload_ready start to return true. So we should + * run them and then re-check xfer_upload_ready, before + * we go as far as waiting for an entire packet to + * arrive. */ + run_toplevel_callbacks(); + continue; + } + if (!xfer_done(xfer)) { pktin = sftp_recv(); ret = xfer_upload_gotpkt(xfer, pktin); @@ -1557,7 +1569,7 @@ static bool sftp_action_mv(void *vctx, char *srcfname) p = srcfname + strlen(srcfname); while (p > srcfname && p[-1] != '/') p--; - newname = dupcat(ctx->dstfname, "/", p, NULL); + newname = dupcat(ctx->dstfname, "/", p); newcanon = canonify(newname); sfree(newname); @@ -1588,7 +1600,7 @@ static bool sftp_action_mv(void *vctx, char *srcfname) int sftp_cmd_mv(struct sftp_command *cmd) { - struct sftp_context_mv actx, *ctx = &actx; + struct sftp_context_mv ctx[1]; int i, ret; if (!backend) { @@ -1677,7 +1689,7 @@ int sftp_cmd_chmod(struct sftp_command *cmd) { char *mode; int i, ret; - struct sftp_context_chmod actx, *ctx = &actx; + struct sftp_context_chmod ctx[1]; if (!backend) { not_connected(); @@ -2306,6 +2318,16 @@ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) return cmd; } +static void sftp_cmd_free(struct sftp_command *cmd) +{ + if (cmd->words) { + for (size_t i = 0; i < cmd->nwords; i++) + sfree(cmd->words[i]); + sfree(cmd->words); + } + sfree(cmd); +} + static int do_sftp_init(void) { struct sftp_packet *pktin; @@ -2380,13 +2402,7 @@ int do_sftp(int mode, int modeflags, char *batchfile) if (!cmd) break; ret = cmd->obey(cmd); - if (cmd->words) { - int i; - for(i = 0; i < cmd->nwords; i++) - sfree(cmd->words[i]); - sfree(cmd->words); - } - sfree(cmd); + sftp_cmd_free(cmd); if (ret < 0) break; } @@ -2403,6 +2419,7 @@ int do_sftp(int mode, int modeflags, char *batchfile) if (!cmd) break; ret = cmd->obey(cmd); + sftp_cmd_free(cmd); if (ret < 0) break; if (ret == 0) { @@ -2428,12 +2445,6 @@ static bool verbose = false; void ldisc_echoedit_update(Ldisc *ldisc) { } -void agent_schedule_callback(void (*callback)(void *, void *, int), - void *callback_ctx, void *data, int len) -{ - unreachable("all PSFTP agent requests should be synchronous"); -} - /* * Receive a block of data from the SSH link. Block until all data * is available. @@ -2520,12 +2531,16 @@ static void usage(void) printf(" -P port connect to specified port\n"); printf(" -pw passw login with specified password\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); + printf(" -ssh -ssh-connection\n"); + printf(" force use of particular SSH protocol variant\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); - printf(" -hostkey aa:bb:cc:...\n"); + printf(" -no-trivial-auth\n"); + printf(" disconnect if SSH authentication succeeds trivially\n"); + printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -batch disable all interactive prompts\n"); printf(" -no-sanitise-stderr don't strip control chars from" @@ -2535,6 +2550,9 @@ static void usage(void) printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); + printf(" -logoverwrite\n"); + printf(" -logappend\n"); + printf(" control what happens when a log file already exists\n"); cleanup_exit(1); } @@ -2572,7 +2590,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber) * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ - if (!loaded_session) { + if (!cmdline_loaded_session()) { /* Try to load settings for `host' into a temporary config */ Conf *conf2 = conf_new(); conf_set_str(conf2, CONF_host, ""); @@ -2593,10 +2611,12 @@ static int psftp_connect(char *userhost, char *user, int portnumber) } /* - * Force use of SSH. (If they got the protocol wrong we assume the - * port is useless too.) + * Force protocol to SSH if the user has somehow contrived to + * select one we don't support (e.g. by loading an inappropriate + * saved session). In that situation we assume the port number is + * useless too.) */ - if (conf_get_int(conf, CONF_protocol) != PROT_SSH) { + if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) { conf_set_int(conf, CONF_protocol, PROT_SSH); conf_set_int(conf, CONF_port, 22); } @@ -2709,11 +2729,13 @@ static int psftp_connect(char *userhost, char *user, int portnumber) "exec sftp-server"); conf_set_bool(conf, CONF_ssh_subsys2, false); - psftp_logctx = log_init(default_logpolicy, conf); + psftp_logctx = log_init(console_cli_logpolicy, conf); - platform_psftp_pre_conn_setup(); + platform_psftp_pre_conn_setup(console_cli_logpolicy); - err = backend_init(&ssh_backend, psftp_seat, &backend, psftp_logctx, conf, + err = backend_init(backend_vt_from_proto( + conf_get_int(conf, CONF_protocol)), + psftp_seat, &backend, psftp_logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, @@ -2754,6 +2776,8 @@ const bool share_can_be_upstream = false; static stdio_sink stderr_ss; static StripCtrlChars *stderr_scc; +const unsigned cmdline_tooltype = TOOLTYPE_FILETRANSFER; + /* * Main program. Parse arguments etc. */ @@ -2767,12 +2791,6 @@ int psftp_main(int argc, char *argv[]) bool sanitise_stderr = true; char *batchfile = NULL; - flags = FLAG_INTERACTIVE -#ifdef FLAG_SYNCAGENT - | FLAG_SYNCAGENT -#endif - ; - cmdline_tooltype = TOOLTYPE_FILETRANSFER; sk_init(); userhost = user = NULL; @@ -2780,7 +2798,6 @@ int psftp_main(int argc, char *argv[]) /* Load Default Settings before doing anything else. */ conf = conf_new(); do_defaults(NULL, conf); - loaded_session = false; for (i = 1; i < argc; i++) { int ret; @@ -2798,7 +2815,7 @@ int psftp_main(int argc, char *argv[]) i++; /* skip next argument */ } else if (ret == 1) { /* We have our own verbosity in addition to `flags'. */ - if (flags & FLAG_VERBOSE) + if (cmdline_verbose()) verbose = true; } else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "-?") == 0 || diff --git a/psftp.h b/psftp.h index 9ed40b1..79327e7 100644 --- a/psftp.h +++ b/psftp.h @@ -49,7 +49,7 @@ char *ssh_sftp_get_cmdline(const char *prompt, bool backend_required); * Platform-specific function called when we're about to make a * network connection. */ -void platform_psftp_pre_conn_setup(void); +void platform_psftp_pre_conn_setup(LogPolicy *lp); /* * The main program in psftp.c. Called from main() in the platform- diff --git a/psocks.c b/psocks.c new file mode 100644 index 0000000..f29eeaa --- /dev/null +++ b/psocks.c @@ -0,0 +1,569 @@ +/* + * Platform-independent parts of a standalone SOCKS server program + * based on the PuTTY SOCKS code. + */ + +#include +#include + +#include "putty.h" +#include "misc.h" +#include "ssh.h" +#include "sshchan.h" +#include "psocks.h" + +/* + * Possible later TODOs: + * + * - verbosity setting for log messages + * + * - could import proxy.c and use name_lookup rather than + * sk_namelookup, to allow forwarding via some other proxy type + */ + +#define BUFLIMIT 16384 + +#define LOGBITS(X) \ + X(CONNSTATUS) \ + X(DIALOGUE) \ + /* end of list */ + +#define BITINDEX_ENUM(x) LOG_##x##_bitindex, +enum { LOGBITS(BITINDEX_ENUM) }; +#define BITFLAG_ENUM(x) LOG_##x = 1 << LOG_##x##_bitindex, +enum { LOGBITS(BITFLAG_ENUM) }; + +typedef struct psocks_connection psocks_connection; + +typedef enum RecordDestination { + REC_NONE, REC_FILE, REC_PIPE +} RecordDestination; + +struct psocks_state { + const PsocksPlatform *platform; + int listen_port; + bool acceptall; + PortFwdManager *portfwdmgr; + uint64_t next_conn_index; + FILE *logging_fp; + unsigned log_flags; + RecordDestination rec_dest; + char *rec_cmd; + strbuf *subcmd; + + ConnectionLayer cl; +}; + +struct psocks_connection { + psocks_state *ps; + Channel *chan; + char *host, *realhost; + int port; + SockAddr *addr; + Socket *socket; + bool connecting, eof_pfmgr_to_socket, eof_socket_to_pfmgr; + uint64_t index; + PsocksDataSink *rec_sink; + + Plug plug; + SshChannel sc; +}; + +static SshChannel *psocks_lportfwd_open( + ConnectionLayer *cl, const char *hostname, int port, + const char *description, const SocketPeerInfo *pi, Channel *chan); + +static const ConnectionLayerVtable psocks_clvt = { + .lportfwd_open = psocks_lportfwd_open, + /* everything else is NULL */ +}; + +static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, const void *, + size_t); +static void psocks_sc_write_eof(SshChannel *sc); +static void psocks_sc_initiate_close(SshChannel *sc, const char *err); +static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize); + +static const SshChannelVtable psocks_scvt = { + .write = psocks_sc_write, + .write_eof = psocks_sc_write_eof, + .initiate_close = psocks_sc_initiate_close, + .unthrottle = psocks_sc_unthrottle, + /* all the rest are NULL */ +}; + +static void psocks_plug_log(Plug *p, PlugLogType type, SockAddr *addr, + int port, const char *error_msg, int error_code); +static void psocks_plug_closing(Plug *p, const char *error_msg, + int error_code, bool calling_back); +static void psocks_plug_receive(Plug *p, int urgent, + const char *data, size_t len); +static void psocks_plug_sent(Plug *p, size_t bufsize); + +static const PlugVtable psocks_plugvt = { + .log = psocks_plug_log, + .closing = psocks_plug_closing, + .receive = psocks_plug_receive, + .sent = psocks_plug_sent, +}; + +static void psocks_conn_log(psocks_connection *conn, const char *fmt, ...) +{ + if (!conn->ps->logging_fp) + return; + + va_list ap; + va_start(ap, fmt); + char *msg = dupvprintf(fmt, ap); + va_end(ap); + fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s\n", conn->index, msg); + sfree(msg); + fflush(conn->ps->logging_fp); +} + +static void psocks_conn_log_data(psocks_connection *conn, PsocksDirection dir, + const void *vdata, size_t len) +{ + if ((conn->ps->log_flags & LOG_DIALOGUE) && conn->ps->logging_fp) { + const char *data = vdata; + while (len > 0) { + const char *nl = memchr(data, '\n', len); + size_t thislen = nl ? (nl+1) - data : len; + const char *thisdata = data; + data += thislen; + len -= thislen; + + static const char *const direction_names[2] = { + [UP] = "send", [DN] = "recv" }; + + fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s \"", conn->index, + direction_names[dir]); + write_c_string_literal(conn->ps->logging_fp, + make_ptrlen(thisdata, thislen)); + fprintf(conn->ps->logging_fp, "\"\n"); + } + + fflush(conn->ps->logging_fp); + } + + if (conn->rec_sink) + put_data(conn->rec_sink->s[dir], vdata, len); +} + +static void psocks_connection_establish(void *vctx); + +static SshChannel *psocks_lportfwd_open( + ConnectionLayer *cl, const char *hostname, int port, + const char *description, const SocketPeerInfo *pi, Channel *chan) +{ + psocks_state *ps = container_of(cl, psocks_state, cl); + psocks_connection *conn = snew(psocks_connection); + memset(conn, 0, sizeof(*conn)); + conn->ps = ps; + conn->sc.vt = &psocks_scvt; + conn->plug.vt = &psocks_plugvt; + conn->chan = chan; + conn->host = dupstr(hostname); + conn->port = port; + conn->index = ps->next_conn_index++; + if (conn->ps->log_flags & LOG_CONNSTATUS) + psocks_conn_log(conn, "request from %s for %s port %d", + pi->log_text, hostname, port); + switch (conn->ps->rec_dest) { + case REC_FILE: + { + char *fnames[2]; + FILE *fp[2]; + bool ok = true; + + static const char *const direction_names[2] = { + [UP] = "sockout", [DN] = "sockin" }; + + for (size_t i = 0; i < 2; i++) { + fnames[i] = dupprintf("%s.%"PRIu64, direction_names[i], + conn->index); + fp[i] = fopen(fnames[i], "wb"); + if (!fp[i]) { + psocks_conn_log(conn, "cannot log this connection: " + "creating file '%s': %s", + fnames[i], strerror(errno)); + ok = false; + } + } + if (ok) { + if (conn->ps->log_flags & LOG_CONNSTATUS) + psocks_conn_log(conn, "logging to '%s' / '%s'", + fnames[0], fnames[1]); + conn->rec_sink = pds_stdio(fp); + } else { + for (size_t i = 0; i < 2; i++) { + if (fp[i]) { + remove(fnames[i]); + fclose(fp[i]); + } + } + } + for (size_t i = 0; i < 2; i++) + sfree(fnames[i]); + } + break; + case REC_PIPE: + { + static const char *const direction_args[2] = { + [UP] = "out", [DN] = "in" }; + char *index_arg = dupprintf("%"PRIu64, conn->index); + char *err; + conn->rec_sink = conn->ps->platform->open_pipes( + conn->ps->rec_cmd, direction_args, index_arg, &err); + if (!conn->rec_sink) { + psocks_conn_log(conn, "cannot log this connection: " + "creating pipes: %s", err); + sfree(err); + } + sfree(index_arg); + } + break; + default: + break; + } + queue_toplevel_callback(psocks_connection_establish, conn); + return &conn->sc; +} + +static void psocks_conn_free(psocks_connection *conn) +{ + if (conn->ps->log_flags & LOG_CONNSTATUS) + psocks_conn_log(conn, "closed"); + + sfree(conn->host); + sfree(conn->realhost); + if (conn->socket) + sk_close(conn->socket); + if (conn->chan) + chan_free(conn->chan); + if (conn->rec_sink) + pds_free(conn->rec_sink); + delete_callbacks_for_context(conn); + sfree(conn); +} + +static void psocks_connection_establish(void *vctx) +{ + psocks_connection *conn = (psocks_connection *)vctx; + + /* + * Look up destination host name. + */ + conn->addr = sk_namelookup(conn->host, &conn->realhost, ADDRTYPE_UNSPEC); + + const char *err = sk_addr_error(conn->addr); + if (err) { + char *msg = dupprintf("name lookup failed: %s", err); + chan_open_failed(conn->chan, msg); + sfree(msg); + + psocks_conn_free(conn); + return; + } + + /* + * Make the connection. + */ + conn->connecting = true; + conn->socket = sk_new(conn->addr, conn->port, false, false, false, false, + &conn->plug); +} + +static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, + const void *data, size_t len) +{ + psocks_connection *conn = container_of(sc, psocks_connection, sc); + if (!conn->socket) return 0; + + psocks_conn_log_data(conn, UP, data, len); + + return sk_write(conn->socket, data, len); +} + +static void psocks_check_close(void *vctx) +{ + psocks_connection *conn = (psocks_connection *)vctx; + if (chan_want_close(conn->chan, conn->eof_pfmgr_to_socket, + conn->eof_socket_to_pfmgr)) + psocks_conn_free(conn); +} + +static void psocks_sc_write_eof(SshChannel *sc) +{ + psocks_connection *conn = container_of(sc, psocks_connection, sc); + if (!conn->socket) return; + sk_write_eof(conn->socket); + conn->eof_pfmgr_to_socket = true; + + if (conn->ps->log_flags & LOG_DIALOGUE) + psocks_conn_log(conn, "send eof"); + + queue_toplevel_callback(psocks_check_close, conn); +} + +static void psocks_sc_initiate_close(SshChannel *sc, const char *err) +{ + psocks_connection *conn = container_of(sc, psocks_connection, sc); + sk_close(conn->socket); + conn->socket = NULL; +} + +static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize) +{ + psocks_connection *conn = container_of(sc, psocks_connection, sc); + if (bufsize < BUFLIMIT) + sk_set_frozen(conn->socket, false); +} + +static void psocks_plug_log(Plug *plug, PlugLogType type, SockAddr *addr, + int port, const char *error_msg, int error_code) +{ + psocks_connection *conn = container_of(plug, psocks_connection, plug); + char addrbuf[256]; + + if (!(conn->ps->log_flags & LOG_CONNSTATUS)) + return; + + switch (type) { + case PLUGLOG_CONNECT_TRYING: + sk_getaddr(addr, addrbuf, sizeof(addrbuf)); + if (sk_addr_needs_port(addr)) + psocks_conn_log(conn, "trying to connect to %s port %d", + addrbuf, port); + else + psocks_conn_log(conn, "trying to connect to %s", addrbuf); + break; + case PLUGLOG_CONNECT_FAILED: + psocks_conn_log(conn, "connection attempt failed: %s", error_msg); + break; + case PLUGLOG_CONNECT_SUCCESS: + psocks_conn_log(conn, "connection established", error_msg); + if (conn->connecting) { + chan_open_confirmation(conn->chan); + conn->connecting = false; + } + break; + case PLUGLOG_PROXY_MSG: + psocks_conn_log(conn, "connection setup: %s", error_msg); + break; + }; +} + +static void psocks_plug_closing(Plug *plug, const char *error_msg, + int error_code, bool calling_back) +{ + psocks_connection *conn = container_of(plug, psocks_connection, plug); + if (conn->connecting) { + if (conn->ps->log_flags & LOG_CONNSTATUS) + psocks_conn_log(conn, "unable to connect: %s", error_msg); + + chan_open_failed(conn->chan, error_msg); + conn->eof_socket_to_pfmgr = true; + conn->eof_pfmgr_to_socket = true; + conn->connecting = false; + } else { + if (conn->ps->log_flags & LOG_DIALOGUE) + psocks_conn_log(conn, "recv eof"); + + chan_send_eof(conn->chan); + conn->eof_socket_to_pfmgr = true; + } + queue_toplevel_callback(psocks_check_close, conn); +} + +static void psocks_plug_receive(Plug *plug, int urgent, + const char *data, size_t len) +{ + psocks_connection *conn = container_of(plug, psocks_connection, plug); + size_t bufsize = chan_send(conn->chan, false, data, len); + sk_set_frozen(conn->socket, bufsize > BUFLIMIT); + + psocks_conn_log_data(conn, DN, data, len); +} + +static void psocks_plug_sent(Plug *plug, size_t bufsize) +{ + psocks_connection *conn = container_of(plug, psocks_connection, plug); + sk_set_frozen(conn->socket, bufsize > BUFLIMIT); +} + +psocks_state *psocks_new(const PsocksPlatform *platform) +{ + psocks_state *ps = snew(psocks_state); + memset(ps, 0, sizeof(*ps)); + + ps->listen_port = 1080; + ps->acceptall = false; + + ps->cl.vt = &psocks_clvt; + ps->portfwdmgr = portfwdmgr_new(&ps->cl); + + ps->logging_fp = stderr; /* could make this configurable later */ + ps->log_flags = LOG_CONNSTATUS; + ps->rec_dest = REC_NONE; + ps->platform = platform; + ps->subcmd = strbuf_new(); + + return ps; +} + +void psocks_free(psocks_state *ps) +{ + portfwdmgr_free(ps->portfwdmgr); + strbuf_free(ps->subcmd); + sfree(ps->rec_cmd); + sfree(ps); +} + +void psocks_cmdline(psocks_state *ps, int argc, char **argv) +{ + bool doing_opts = true; + bool accumulating_exec_args = false; + size_t args_seen = 0; + + while (--argc > 0) { + const char *p = *++argv; + + if (doing_opts && p[0] == '-' && p[1]) { + if (!strcmp(p, "--")) { + doing_opts = false; + } else if (!strcmp(p, "-g")) { + ps->acceptall = true; + } else if (!strcmp(p, "-d")) { + ps->log_flags |= LOG_DIALOGUE; + } else if (!strcmp(p, "-f")) { + ps->rec_dest = REC_FILE; + } else if (!strcmp(p, "-p")) { + if (!ps->platform->open_pipes) { + fprintf(stderr, "psocks: '-p' is not supported on this " + "platform\n"); + exit(1); + } + if (--argc > 0) { + ps->rec_cmd = dupstr(*++argv); + } else { + fprintf(stderr, "psocks: expected an argument to '-p'\n"); + exit(1); + } + ps->rec_dest = REC_PIPE; + } else if (!strcmp(p, "--exec")) { + if (!ps->platform->start_subcommand) { + fprintf(stderr, "psocks: running a subcommand is not " + "supported on this platform\n"); + exit(1); + } + accumulating_exec_args = true; + /* Now consume all further argv words for the + * subcommand, even if they look like options */ + doing_opts = false; + } else if (!strcmp(p, "--help")) { + printf("usage: psocks [ -d ] [ -f"); + if (ps->platform->open_pipes) + printf(" | -p pipe-cmd"); + printf(" ] [ -g ] port-number"); + printf("\n"); + printf("where: -d log all connection contents to" + " standard output\n"); + printf(" -f record each half-connection to " + "a file sockin.N/sockout.N\n"); + if (ps->platform->open_pipes) + printf(" -p pipe-cmd pipe each half-connection" + " to 'pipe-cmd [in|out] N'\n"); + printf(" -g accept connections from anywhere," + " not just localhost\n"); + if (ps->platform->start_subcommand) + printf(" --exec subcmd [args...] run command, and " + "terminate when it exits\n"); + printf(" port-number listen on this port" + " (default 1080)\n"); + printf("also: psocks --help display this help text\n"); + exit(0); + } else { + fprintf(stderr, "psocks: unrecognised option '%s'\n", p); + exit(1); + } + } else { + if (accumulating_exec_args) { + put_asciz(ps->subcmd, p); + } else switch (args_seen++) { + case 0: + ps->listen_port = atoi(p); + break; + default: + fprintf(stderr, "psocks: unexpected extra argument '%s'\n", p); + exit(1); + break; + } + } + } +} + +void psocks_start(psocks_state *ps) +{ + Conf *conf = conf_new(); + conf_set_bool(conf, CONF_lport_acceptall, ps->acceptall); + char *key = dupprintf("AL%d", ps->listen_port); + conf_set_str_str(conf, CONF_portfwd, key, "D"); + sfree(key); + + portfwdmgr_config(ps->portfwdmgr, conf); + + if (ps->subcmd->len) + ps->platform->start_subcommand(ps->subcmd); + + conf_free(conf); +} + +/* + * Some stubs that are needed to link against PuTTY modules. + */ + +int verify_host_key(const char *hostname, int port, + const char *keytype, const char *key) +{ + unreachable("host keys not handled in this tool"); +} + +void store_host_key(const char *hostname, int port, + const char *keytype, const char *key) +{ + unreachable("host keys not handled in this tool"); +} + +/* + * stdio-targeted PsocksDataSink. + */ +typedef struct PsocksDataSinkStdio { + stdio_sink sink[2]; + PsocksDataSink pds; +} PsocksDataSinkStdio; + +static void stdio_free(PsocksDataSink *pds) +{ + PsocksDataSinkStdio *pdss = container_of(pds, PsocksDataSinkStdio, pds); + + for (size_t i = 0; i < 2; i++) + fclose(pdss->sink[i].fp); + + sfree(pdss); +} + +PsocksDataSink *pds_stdio(FILE *fp[2]) +{ + PsocksDataSinkStdio *pdss = snew(PsocksDataSinkStdio); + + for (size_t i = 0; i < 2; i++) { + setvbuf(fp[i], NULL, _IONBF, 0); + stdio_sink_init(&pdss->sink[i], fp[i]); + pdss->pds.s[i] = BinarySink_UPCAST(&pdss->sink[i]); + } + + pdss->pds.free = stdio_free; + + return &pdss->pds; +} diff --git a/psocks.h b/psocks.h new file mode 100644 index 0000000..d1120a3 --- /dev/null +++ b/psocks.h @@ -0,0 +1,28 @@ +typedef struct psocks_state psocks_state; + +typedef struct PsocksPlatform PsocksPlatform; +typedef struct PsocksDataSink PsocksDataSink; + +/* indices into PsocksDataSink arrays */ +typedef enum PsocksDirection { UP, DN } PsocksDirection; + +typedef struct PsocksDataSink { + void (*free)(PsocksDataSink *); + BinarySink *s[2]; +} PsocksDataSink; +static inline void pds_free(PsocksDataSink *pds) +{ pds->free(pds); } + +PsocksDataSink *pds_stdio(FILE *fp[2]); + +struct PsocksPlatform { + PsocksDataSink *(*open_pipes)( + const char *cmd, const char *const *direction_args, + const char *index_arg, char **err); + void (*start_subcommand)(strbuf *args); +}; + +psocks_state *psocks_new(const PsocksPlatform *); +void psocks_free(psocks_state *ps); +void psocks_cmdline(psocks_state *ps, int argc, char **argv); +void psocks_start(psocks_state *ps); diff --git a/putty.h b/putty.h index 8f9643e..7789f21 100644 --- a/putty.h +++ b/putty.h @@ -4,19 +4,6 @@ #include /* for wchar_t */ #include /* for INT_MAX */ -/* - * Global variables. Most modules declare these `extern', but - * window.c will do `#define PUTTY_DO_GLOBALS' before including this - * module, and so will get them properly defined. - */ -#ifndef GLOBAL -#ifdef PUTTY_DO_GLOBALS -#define GLOBAL -#else -#define GLOBAL extern -#endif -#endif - #include "defs.h" #include "puttyps.h" #include "network.h" @@ -43,6 +30,151 @@ #define PGP_PREV_MASTER_KEY_FP \ "440D E3B5 B7A1 CA85 B3CC 1718 AB58 5DC6 0467 6F7C" +/* + * Definitions of three separate indexing schemes for colour palette + * entries. + * + * Why three? Because history, sorry. + * + * Two of the colour indexings are used in escape sequences. The + * Linux-console style OSC P sequences for setting the palette use an + * indexing in which the eight standard ANSI SGR colours come first, + * then their bold versions, and then six extra colours for default + * fg/bg and the terminal cursor. And the xterm OSC 4 sequences for + * querying the palette use a related indexing in which the six extra + * colours are pushed up to indices 256 and onwards, with the previous + * 16 being the first part of the xterm 256-colour space, and 240 + * additional terminal-accessible colours inserted in the middle. + * + * The third indexing is the order that the colours appear in the + * PuTTY configuration panel, and also the order in which they're + * described in the saved session files. This order specifies the same + * set of colours as the OSC P encoding, but in a different order, + * with the default fg/bg colours (which users are most likely to want + * to reconfigure) at the start, and the ANSI SGR colours coming + * later. + * + * So all three indices really are needed, because all three appear in + * protocols or file formats outside the PuTTY binary. (Changing the + * saved-session encoding would have a backwards-compatibility impact; + * also, if we ever do, it would be better to replace the numeric + * indices with descriptive keywords.) + * + * Since the OSC 4 encoding contains the full set of colours used in + * the terminal display, that's the encoding used by front ends to + * store any actual data associated with their palette entries. So the + * TermWin palette_set and palette_get_overrides methods use that + * encoding, and so does the bitwise encoding of attribute words used + * in terminal redraw operations. + * + * The Conf encoding, of course, is used by config.c and settings.c. + * + * The aim is that those two sections of the code should never need to + * come directly into contact, and the only module that should have to + * deal directly with the mapping between these colour encodings - or + * to deal _at all_ with the intermediate OSC P encoding - is + * terminal.c itself. + */ + +#define CONF_NCOLOURS 22 /* 16 + 6 special ones */ +#define OSCP_NCOLOURS 22 /* same as CONF, but different order */ +#define OSC4_NCOLOURS 262 /* 256 + the same 6 special ones */ + +/* The list macro for the conf colours also gives the textual names + * used in the GUI configurer */ +#define CONF_COLOUR_LIST(X) \ + X(fg, "Default Foreground") \ + X(fg_bold, "Default Bold Foreground") \ + X(bg, "Default Background") \ + X(bg_bold, "Default Bold Background") \ + X(cursor_fg, "Cursor Text") \ + X(cursor_bg, "Cursor Colour") \ + X(black, "ANSI Black") \ + X(black_bold, "ANSI Black Bold") \ + X(red, "ANSI Red") \ + X(red_bold, "ANSI Red Bold") \ + X(green, "ANSI Green") \ + X(green_bold, "ANSI Green Bold") \ + X(yellow, "ANSI Yellow") \ + X(yellow_bold, "ANSI Yellow Bold") \ + X(blue, "ANSI Blue") \ + X(blue_bold, "ANSI Blue Bold") \ + X(magenta, "ANSI Magenta") \ + X(magenta_bold, "ANSI Magenta Bold") \ + X(cyan, "ANSI Cyan") \ + X(cyan_bold, "ANSI Cyan Bold") \ + X(white, "ANSI White") \ + X(white_bold, "ANSI White Bold") \ + /* end of list */ + +#define OSCP_COLOUR_LIST(X) \ + X(black) \ + X(red) \ + X(green) \ + X(yellow) \ + X(blue) \ + X(magenta) \ + X(cyan) \ + X(white) \ + X(black_bold) \ + X(red_bold) \ + X(green_bold) \ + X(yellow_bold) \ + X(blue_bold) \ + X(magenta_bold) \ + X(cyan_bold) \ + X(white_bold) \ + /* + * In the OSC 4 indexing, this is where the extra 240 colours go. + * They consist of: + * + * - 216 colours forming a 6x6x6 cube, with R the most + * significant colour and G the least. In other words, these + * occupy the space of indices 16 <= i < 232, with each + * individual colour found as i = 16 + 36*r + 6*g + b, for all + * 0 <= r,g,b <= 5. + * + * - The remaining indices, 232 <= i < 256, consist of a uniform + * series of grey shades running between black and white (but + * not including either, since actual black and white are + * already provided in the previous colour cube). + * + * After that, we have the remaining 6 special colours: + */ \ + X(fg) \ + X(fg_bold) \ + X(bg) \ + X(bg_bold) \ + X(cursor_fg) \ + X(cursor_bg) \ + /* end of list */ + +/* Enumerations of the colour lists. These are available everywhere in + * the code. The OSC P encoding shouldn't be used outside terminal.c, + * but the easiest way to define the OSC 4 enum is to have the OSC P + * one available to compute with. */ +enum { + #define ENUM_DECL(id,name) CONF_COLOUR_##id, + CONF_COLOUR_LIST(ENUM_DECL) + #undef ENUM_DECL +}; +enum { + #define ENUM_DECL(id) OSCP_COLOUR_##id, + OSCP_COLOUR_LIST(ENUM_DECL) + #undef ENUM_DECL +}; +enum { + #define ENUM_DECL(id) OSC4_COLOUR_##id = \ + OSCP_COLOUR_##id + (OSCP_COLOUR_##id >= 16 ? 240 : 0), + OSCP_COLOUR_LIST(ENUM_DECL) + #undef ENUM_DECL +}; + +/* Mapping tables defined in terminal.c */ +extern const int colour_indices_conf_to_oscp[CONF_NCOLOURS]; +extern const int colour_indices_conf_to_osc4[CONF_NCOLOURS]; +extern const int colour_indices_oscp_to_osc4[OSCP_NCOLOURS]; + /* Three attribute types: * The ATTRs (normal attributes) are stored with the characters in * the main display arrays @@ -84,9 +216,9 @@ #define ATTR_INVALID 0x03FFFFU -/* Like Linux use the F000 page for direct to font. */ -#define CSET_OEMCP 0x0000F000UL /* OEM Codepage DTF */ -#define CSET_ACP 0x0000F100UL /* Ansi Codepage DTF */ +/* Use the DC00 page for direct to font. */ +#define CSET_OEMCP 0x0000DC00UL /* OEM Codepage DTF */ +#define CSET_ACP 0x0000DD00UL /* Ansi Codepage DTF */ /* These are internal use overlapping with the UTF-16 surrogates */ #define CSET_ASCII 0x0000D800UL /* normal ASCII charset ESC ( B */ @@ -96,7 +228,7 @@ #define CSET_MASK 0xFFFFFF00UL /* Character set mask */ #define DIRECT_CHAR(c) ((c&0xFFFFFC00)==0xD800) -#define DIRECT_FONT(c) ((c&0xFFFFFE00)==0xF000) +#define DIRECT_FONT(c) ((c&0xFFFFFE00)==0xDC00) #define UCSERR (CSET_LINEDRW|'a') /* UCS Format error character. */ /* @@ -115,34 +247,16 @@ #define ATTR_UNDER 0x0080000U #define ATTR_REVERSE 0x0100000U #define ATTR_BLINK 0x0200000U -#define ATTR_FGMASK 0x00001FFU -#define ATTR_BGMASK 0x003FE00U +#define ATTR_FGMASK 0x00001FFU /* stores a colour in OSC 4 indexing */ +#define ATTR_BGMASK 0x003FE00U /* stores a colour in OSC 4 indexing */ #define ATTR_COLOURS 0x003FFFFU #define ATTR_DIM 0x1000000U +#define ATTR_STRIKE 0x2000000U #define ATTR_FGSHIFT 0 #define ATTR_BGSHIFT 9 -/* - * The definitive list of colour numbers stored in terminal - * attribute words is kept here. It is: - * - * - 0-7 are ANSI colours (KRGYBMCW). - * - 8-15 are the bold versions of those colours. - * - 16-255 are the remains of the xterm 256-colour mode (a - * 216-colour cube with R at most significant and B at least, - * followed by a uniform series of grey shades running between - * black and white but not including either on grounds of - * redundancy). - * - 256 is default foreground - * - 257 is default bold foreground - * - 258 is default background - * - 259 is default bold background - * - 260 is cursor foreground - * - 261 is cursor background - */ - -#define ATTR_DEFFG (256 << ATTR_FGSHIFT) -#define ATTR_DEFBG (258 << ATTR_BGSHIFT) +#define ATTR_DEFFG (OSC4_COLOUR_fg << ATTR_FGSHIFT) +#define ATTR_DEFBG (OSC4_COLOUR_bg << ATTR_BGSHIFT) #define ATTR_DEFAULT (ATTR_DEFFG | ATTR_DEFBG) struct sesslist { @@ -324,6 +438,7 @@ enum { HK_DSA, HK_ECDSA, HK_ED25519, + HK_ED448, HK_MAX }; @@ -376,12 +491,20 @@ enum { TITLE_NONE, TITLE_EMPTY, TITLE_REAL }; +enum { + /* SUPDUP character set options */ + SUPDUP_CHARSET_ASCII, SUPDUP_CHARSET_ITS, SUPDUP_CHARSET_WAITS +}; + enum { /* Protocol back ends. (CONF_protocol) */ - PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, + PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, PROT_SSHCONN, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ - PROT_SERIAL + PROT_SERIAL, + /* PROT_SUPDUP is the historical RFC 734 protocol. */ + PROT_SUPDUP, + PROTOCOL_LIMIT, /* upper bound on number of protocols */ }; enum { @@ -490,14 +613,19 @@ enum { ADDRTYPE_NAME /* SockAddr storing an unresolved host name */ }; +/* Backend flags */ +#define BACKEND_RESIZE_FORBIDDEN 0x01 /* Backend does not allow + resizing terminal */ +#define BACKEND_NEEDS_TERMINAL 0x02 /* Backend must have terminal */ + struct Backend { const BackendVtable *vt; }; struct BackendVtable { - const char *(*init) (Seat *seat, Backend **backend_out, - LogContext *logctx, Conf *conf, - const char *host, int port, - char **realhost, bool nodelay, bool keepalive); + char *(*init) (const BackendVtable *vt, Seat *seat, + Backend **backend_out, LogContext *logctx, Conf *conf, + const char *host, int port, char **realhost, + bool nodelay, bool keepalive); void (*free) (Backend *be); /* Pass in a replacement configuration. */ @@ -524,16 +652,31 @@ struct BackendVtable { /* Only implemented in the SSH protocol: check whether a * connection-sharing upstream exists for a given configuration. */ bool (*test_for_upstream)(const char *host, int port, Conf *conf); + /* Special-purpose function to return additional information to put + * in a "are you sure you want to close this session" dialog; + * return NULL if no such info, otherwise caller must free. + * Only implemented in the SSH protocol, to warn about downstream + * connections that would be lost if this one were terminated. */ + char *(*close_warn_text)(Backend *be); + + /* 'id' is a machine-readable name for the backend, used in + * saved-session storage. 'displayname' is a human-readable name + * for error messages. */ + const char *id, *displayname; - const char *name; int protocol; int default_port; + unsigned flags; + + /* Only relevant for the serial protocol: bit masks of which + * parity and flow control settings are supported. */ + unsigned serial_parity_mask, serial_flow_mask; }; -static inline const char *backend_init( +static inline char *backend_init( const BackendVtable *vt, Seat *seat, Backend **out, LogContext *logctx, Conf *conf, const char *host, int port, char **rhost, bool nd, bool ka) -{ return vt->init(seat, out, logctx, conf, host, port, rhost, nd, ka); } +{ return vt->init(vt, seat, out, logctx, conf, host, port, rhost, nd, ka); } static inline void backend_free(Backend *be) { be->vt->free(be); } static inline void backend_reconfig(Backend *be, Conf *conf) @@ -565,6 +708,12 @@ static inline int backend_cfg_info(Backend *be) { return be->vt->cfg_info(be); } extern const struct BackendVtable *const backends[]; +/* + * In programs with a config UI, only the first few members of + * backends[] will be displayed at the top-level; the others will be + * relegated to a drop-down. + */ +extern const size_t n_ui_backends; /* * Suggested default protocol provided by the backend link module. @@ -578,45 +727,6 @@ extern const int be_default_protocol; */ extern const char *const appname; -/* - * Some global flags denoting the type of application. - * - * FLAG_VERBOSE is set when the user requests verbose details. - * - * FLAG_INTERACTIVE is set when a full interactive shell session is - * being run, _either_ because no remote command has been provided - * _or_ because the application is GUI and can't run non- - * interactively. - * - * These flags describe the type of _application_ - they wouldn't - * vary between individual sessions - and so it's OK to have this - * variable be GLOBAL. - * - * Note that additional flags may be defined in platform-specific - * headers. It's probably best if those ones start from 0x1000, to - * avoid collision. - */ -#define FLAG_VERBOSE 0x0001 -#define FLAG_INTERACTIVE 0x0002 -GLOBAL int flags; - -/* - * Likewise, these two variables are set up when the application - * initialises, and inform all default-settings accesses after - * that. - */ -GLOBAL int default_protocol; -GLOBAL int default_port; - -/* - * This is set true by cmdline.c iff a session is loaded with "-load". - */ -GLOBAL bool loaded_session; -/* - * This is set to the name of the loaded session. - */ -GLOBAL char *cmdline_session_name; - /* * Mechanism for getting text strings such as usernames and passwords * from the front-end. @@ -636,19 +746,7 @@ GLOBAL char *cmdline_session_name; typedef struct { char *prompt; bool echo; - /* - * 'result' must be a dynamically allocated array of exactly - * 'resultsize' chars. The code for actually reading input may - * realloc it bigger (and adjust resultsize accordingly) if it has - * to. The caller should free it again when finished with it. - * - * If resultsize==0, then result may be NULL. When setting up a - * prompt_t, it's therefore easiest to initialise them this way, - * which means all actual allocation is done by the callee. This - * is what add_prompt does. - */ - char *result; - size_t resultsize; + strbuf *result; } prompt_t; typedef struct { /* @@ -679,11 +777,11 @@ typedef struct { void *data; /* slot for housekeeping data, managed by * seat_get_userpass_input(); initially NULL */ } prompts_t; -prompts_t *new_prompts(); +prompts_t *new_prompts(void); void add_prompt(prompts_t *p, char *promptstr, bool echo); void prompt_set_result(prompt_t *pr, const char *newstr); -void prompt_ensure_result_size(prompt_t *pr, int len); -/* Burn the evidence. (Assumes _all_ strings want free()ing.) */ +char *prompt_get_result(prompt_t *pr); +const char *prompt_get_result_ref(prompt_t *pr); void free_prompts(prompts_t *p); /* @@ -880,8 +978,8 @@ struct SeatVtable { * or +1'. */ int (*verify_ssh_host_key)( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *key_fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); /* @@ -966,6 +1064,23 @@ struct SeatVtable { * prompts by malicious servers. */ bool (*set_trust_status)(Seat *seat, bool trusted); + + /* + * Ask the seat whether it would like verbose messages. + */ + bool (*verbose)(Seat *seat); + + /* + * Ask the seat whether it's an interactive program. + */ + bool (*interactive)(Seat *seat); + + /* + * Return the seat's current idea of where the output cursor is. + * + * Returns true if the seat has a cursor. Returns false if not. + */ + bool (*get_cursor_position)(Seat *seat, int *x, int *y); }; static inline size_t seat_output( @@ -986,8 +1101,9 @@ static inline void seat_set_busy_status(Seat *seat, BusyStatus status) { seat->vt->set_busy_status(seat, status); } static inline int seat_verify_ssh_host_key( Seat *seat, const char *h, int p, const char *ktyp, char *kstr, - char *fp, void (*cb)(void *ctx, int result), void *ctx) -{ return seat->vt->verify_ssh_host_key(seat, h, p, ktyp, kstr, fp, cb, ctx); } + const char *kdsp, char **fps, void (*cb)(void *ctx, int result), void *ctx) +{ return seat->vt->verify_ssh_host_key(seat, h, p, ktyp, kstr, kdsp, fps, + cb, ctx); } static inline int seat_confirm_weak_crypto_primitive( Seat *seat, const char *atyp, const char *aname, void (*cb)(void *ctx, int result), void *ctx) @@ -1011,11 +1127,17 @@ static inline StripCtrlChars *seat_stripctrl_new( { return seat->vt->stripctrl_new(seat, bs, sic); } static inline bool seat_set_trust_status(Seat *seat, bool trusted) { return seat->vt->set_trust_status(seat, trusted); } +static inline bool seat_verbose(Seat *seat) +{ return seat->vt->verbose(seat); } +static inline bool seat_interactive(Seat *seat) +{ return seat->vt->interactive(seat); } +static inline bool seat_get_cursor_position(Seat *seat, int *x, int *y) +{ return seat->vt->get_cursor_position(seat, x, y); } /* Unlike the seat's actual method, the public entry point * seat_connection_fatal is a wrapper function with a printf-like API, * defined in misc.c. */ -void seat_connection_fatal(Seat *seat, const char *fmt, ...); +void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3); /* Handy aliases for seat_output which set is_stderr to a fixed value. */ static inline size_t seat_stdout(Seat *seat, const void *data, size_t len) @@ -1044,8 +1166,8 @@ void nullseat_update_specials_menu(Seat *seat); char *nullseat_get_ttymode(Seat *seat, const char *mode); void nullseat_set_busy_status(Seat *seat, BusyStatus status); int nullseat_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *key_fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); int nullseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, @@ -1063,6 +1185,11 @@ StripCtrlChars *nullseat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic); bool nullseat_set_trust_status(Seat *seat, bool trusted); bool nullseat_set_trust_status_vacuously(Seat *seat, bool trusted); +bool nullseat_verbose_no(Seat *seat); +bool nullseat_verbose_yes(Seat *seat); +bool nullseat_interactive_no(Seat *seat); +bool nullseat_interactive_yes(Seat *seat); +bool nullseat_get_cursor_position(Seat *seat, int *x, int *y); /* * Seat functions provided by the platform's console-application @@ -1071,8 +1198,8 @@ bool nullseat_set_trust_status_vacuously(Seat *seat, bool trusted); void console_connection_fatal(Seat *seat, const char *message); int console_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *key_fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); int console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, @@ -1088,6 +1215,11 @@ bool console_set_trust_status(Seat *seat, bool trusted); * Other centralised seat functions. */ int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input); +bool cmdline_seat_verbose(Seat *seat); + +typedef struct rgb { + uint8_t r, g, b; +} rgb; /* * Data type 'TermWin', which is a vtable encapsulating all the @@ -1127,7 +1259,13 @@ struct TermWinVtable { void (*set_cursor_pos)(TermWin *, int x, int y); + /* set_raw_mouse_mode instructs the front end to start sending mouse events + * in raw mode suitable for translating into mouse-tracking terminal data + * (e.g. include scroll-wheel events and don't bother to identify double- + * and triple-clicks). set_raw_mouse_mode_pointer instructs the front end + * to change the mouse pointer shape to *indicate* raw mouse mode. */ void (*set_raw_mouse_mode)(TermWin *, bool enable); + void (*set_raw_mouse_mode_pointer)(TermWin *, bool enable); void (*set_scrollbar)(TermWin *, int total, int start, int page); @@ -1149,19 +1287,26 @@ struct TermWinVtable { * the window it remembers whether to go back to normal or * maximised. */ void (*set_minimised)(TermWin *, bool minimised); - bool (*is_minimised)(TermWin *); void (*set_maximised)(TermWin *, bool maximised); void (*move)(TermWin *, int x, int y); void (*set_zorder)(TermWin *, bool top); - bool (*palette_get)(TermWin *, int n, int *r, int *g, int *b); - void (*palette_set)(TermWin *, int n, int r, int g, int b); - void (*palette_reset)(TermWin *); + /* Set the colour palette that the TermWin will use to display + * text. One call to this function sets 'ncolours' consecutive + * colours in the OSC 4 sequence, starting at 'start'. */ + void (*palette_set)(TermWin *, unsigned start, unsigned ncolours, + const rgb *colours); - void (*get_pos)(TermWin *, int *x, int *y); - void (*get_pixels)(TermWin *, int *x, int *y); - const char *(*get_title)(TermWin *, bool icon); - bool (*is_utf8)(TermWin *); + /* Query the front end for any OS-local overrides to the default + * colours stored in Conf. The front end should set any it cares + * about by calling term_palette_override. + * + * The Terminal object is passed in as a parameter, because this + * can be called as a callback from term_init(). So the TermWin + * itself won't yet have been told where to find its Terminal + * object, because that doesn't happen until term_init + * returns. */ + void (*palette_get_overrides)(TermWin *, Terminal *); }; static inline bool win_setup_draw_ctx(TermWin *win) @@ -1184,6 +1329,8 @@ static inline void win_set_cursor_pos(TermWin *win, int x, int y) { win->vt->set_cursor_pos(win, x, y); } static inline void win_set_raw_mouse_mode(TermWin *win, bool enable) { win->vt->set_raw_mouse_mode(win, enable); } +static inline void win_set_raw_mouse_mode_pointer(TermWin *win, bool enable) +{ win->vt->set_raw_mouse_mode_pointer(win, enable); } static inline void win_set_scrollbar(TermWin *win, int t, int s, int p) { win->vt->set_scrollbar(win, t, s, p); } static inline void win_bell(TermWin *win, int mode) @@ -1204,34 +1351,23 @@ static inline void win_set_icon_title(TermWin *win, const char *icontitle) { win->vt->set_icon_title(win, icontitle); } static inline void win_set_minimised(TermWin *win, bool minimised) { win->vt->set_minimised(win, minimised); } -static inline bool win_is_minimised(TermWin *win) -{ return win->vt->is_minimised(win); } static inline void win_set_maximised(TermWin *win, bool maximised) { win->vt->set_maximised(win, maximised); } static inline void win_move(TermWin *win, int x, int y) { win->vt->move(win, x, y); } static inline void win_set_zorder(TermWin *win, bool top) { win->vt->set_zorder(win, top); } -static inline bool win_palette_get(TermWin *win, int n, int *r, int *g, int *b) -{ return win->vt->palette_get(win, n, r, g, b); } -static inline void win_palette_set(TermWin *win, int n, int r, int g, int b) -{ win->vt->palette_set(win, n, r, g, b); } -static inline void win_palette_reset(TermWin *win) -{ win->vt->palette_reset(win); } -static inline void win_get_pos(TermWin *win, int *x, int *y) -{ win->vt->get_pos(win, x, y); } -static inline void win_get_pixels(TermWin *win, int *x, int *y) -{ win->vt->get_pixels(win, x, y); } -static inline const char *win_get_title(TermWin *win, bool icon) -{ return win->vt->get_title(win, icon); } -static inline bool win_is_utf8(TermWin *win) -{ return win->vt->is_utf8(win); } +static inline void win_palette_set( + TermWin *win, unsigned start, unsigned ncolours, const rgb *colours) +{ win->vt->palette_set(win, start, ncolours, colours); } +static inline void win_palette_get_overrides(TermWin *win, Terminal *term) +{ win->vt->palette_get_overrides(win, term); } /* * Global functions not specific to a connection instance. */ -void nonfatal(const char *, ...); -NORETURN void modalfatalbox(const char *, ...); +void nonfatal(const char *, ...) PRINTF_LIKE(1, 2); +NORETURN void modalfatalbox(const char *, ...) PRINTF_LIKE(1, 2); NORETURN void cleanup_exit(int); /* @@ -1268,6 +1404,7 @@ NORETURN void cleanup_exit(int); X(BOOL, NONE, compression) \ X(INT, INT, ssh_kexlist) \ X(INT, INT, ssh_hklist) \ + X(BOOL, NONE, ssh_prefer_known_hostkeys) \ X(INT, NONE, ssh_rekey_time) /* in minutes */ \ X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \ X(BOOL, NONE, tryagent) \ @@ -1291,6 +1428,7 @@ NORETURN void cleanup_exit(int); X(INT, NONE, sshprot) \ X(BOOL, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ X(BOOL, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ + X(BOOL, NONE, ssh_no_trivial_userauth) /* disable trivial types of auth */ \ X(BOOL, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ X(BOOL, NONE, try_tis_auth) \ X(BOOL, NONE, try_ki_auth) \ @@ -1322,6 +1460,11 @@ NORETURN void cleanup_exit(int); X(INT, NONE, serstopbits) \ X(INT, NONE, serparity) /* SER_PAR_NONE, SER_PAR_ODD, ... */ \ X(INT, NONE, serflow) /* SER_FLOW_NONE, SER_FLOW_XONXOFF, ... */ \ + /* Supdup options */ \ + X(STR, NONE, supdup_location) \ + X(INT, NONE, supdup_ascii_set) \ + X(BOOL, NONE, supdup_more) \ + X(BOOL, NONE, supdup_scroll) \ /* Keyboard options */ \ X(BOOL, NONE, bksp_is_delete) \ X(BOOL, NONE, rxvt_homeend) \ @@ -1402,7 +1545,7 @@ NORETURN void cleanup_exit(int); X(BOOL, NONE, system_colour) \ X(BOOL, NONE, try_palette) \ X(INT, NONE, bold_style) /* 1=font 2=colour (3=both) */ \ - X(INT, INT, colours) \ + X(INT, INT, colours) /* indexed by the CONF_COLOUR_* enum encoding */ \ /* Selection options */ \ X(INT, NONE, mouse_is_xterm) /* 0=compromise 1=xterm 2=Windows */ \ X(BOOL, NONE, rect_select) \ @@ -1491,8 +1634,6 @@ NORETURN void cleanup_exit(int); enum config_primary_key { CONFIG_OPTIONS(CONF_ENUM_DEF) N_CONFIG_OPTIONS }; #undef CONF_ENUM_DEF -#define NCFGCOLOURS 22 /* number of colours in CONF_colours above */ - /* Functions handling configuration structures. */ Conf *conf_new(void); /* create an empty configuration */ void conf_free(Conf *conf); @@ -1586,6 +1727,8 @@ void load_open_settings(settings_r *sesskey, Conf *conf); void get_sesslist(struct sesslist *, bool allocate); bool do_defaults(const char *, Conf *); void registry_cleanup(void); +void settings_set_default_protocol(int); +void settings_set_default_port(int); /* * Functions used by settings.c to provide platform-specific @@ -1631,6 +1774,7 @@ void term_blink(Terminal *, bool set_cursor); void term_do_paste(Terminal *, const wchar_t *, int); void term_nopaste(Terminal *); void term_copyall(Terminal *, const int *, int); +void term_pre_reconfig(Terminal *, Conf *); void term_reconfig(Terminal *, Conf *); void term_request_copy(Terminal *, const int *clipboards, int n_clipboards); void term_request_paste(Terminal *, int clipboard); @@ -1644,6 +1788,13 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input); void term_set_trust_status(Terminal *term, bool trusted); void term_keyinput(Terminal *, int codepage, const void *buf, int len); void term_keyinputw(Terminal *, const wchar_t * widebuf, int len); +void term_get_cursor_position(Terminal *term, int *x, int *y); +void term_setup_window_titles(Terminal *term, const char *title_hostname); +void term_notify_minimised(Terminal *term, bool minimised); +void term_notify_palette_changed(Terminal *term); +void term_notify_window_pos(Terminal *term, int x, int y); +void term_notify_window_size_pixels(Terminal *term, int x, int y); +void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb); typedef enum SmallKeypadKey { SKK_HOME, SKK_END, SKK_INSERT, SKK_DELETE, SKK_PGUP, SKK_PGDN, @@ -1693,6 +1844,11 @@ struct LogPolicyVtable { * file :-) */ void (*logging_error)(LogPolicy *lp, const char *event); + + /* + * Ask whether extra verbose log messages are required. + */ + bool (*verbose)(LogPolicy *lp); }; struct LogPolicy { const LogPolicyVtable *vt; @@ -1706,6 +1862,19 @@ static inline int lp_askappend( { return lp->vt->askappend(lp, filename, callback, ctx); } static inline void lp_logging_error(LogPolicy *lp, const char *event) { lp->vt->logging_error(lp, event); } +static inline bool lp_verbose(LogPolicy *lp) +{ return lp->vt->verbose(lp); } + +/* Defined in conscli.c, used in several console command-line tools */ +extern LogPolicy console_cli_logpolicy[]; + +int console_askappend(LogPolicy *lp, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx); +void console_logging_error(LogPolicy *lp, const char *string); +void console_eventlog(LogPolicy *lp, const char *string); +bool null_lp_verbose_yes(LogPolicy *lp); +bool null_lp_verbose_no(LogPolicy *lp); +bool cmdline_lp_verbose(LogPolicy *lp); LogContext *log_init(LogPolicy *lp, Conf *conf); void log_free(LogContext *logctx); @@ -1715,7 +1884,7 @@ void logfclose(LogContext *logctx); void logtraffic(LogContext *logctx, unsigned char c, int logmode); void logflush(LogContext *logctx); void logevent(LogContext *logctx, const char *event); -void logeventf(LogContext *logctx, const char *fmt, ...); +void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3); void logeventvf(LogContext *logctx, const char *fmt, va_list ap); /* @@ -1737,10 +1906,6 @@ void log_packet(LogContext *logctx, int direction, int type, const unsigned long *sequence, unsigned downstream_id, const char *additional_log_text); -/* This is defined by applications that have an obvious logging - * destination like standard error or the GUI. */ -extern LogPolicy default_logpolicy[1]; - /* * Exports from testback.c */ @@ -1770,6 +1935,12 @@ extern const struct BackendVtable telnet_backend; * Exports from ssh.c. */ extern const struct BackendVtable ssh_backend; +extern const struct BackendVtable sshconn_backend; + +/* + * Exports from supdup.c. + */ +extern const struct BackendVtable supdup_backend; /* * Exports from ldisc.c. @@ -1800,9 +1971,14 @@ void random_unref(void); * logical main() no matter whether it needed random numbers or * not. */ void random_clear(void); -/* random_setup_special is used by PuTTYgen. It makes an extra-big - * random number generator. */ -void random_setup_special(); +/* random_setup_custom sets up the process-global random number + * generator specially, with a hash function of your choice. */ +void random_setup_custom(const ssh_hashalg *hash); +/* random_setup_special() is a macro wrapper on that, which makes an + * extra-big one based on the largest hash function we have. It's + * defined this way to avoid what would otherwise be an unnecessary + * module dependency from sshrand.c to a hash function implementation. */ +#define random_setup_special() random_setup_custom(&ssh_shake256_114bytes) /* Manually drop a random seed into the random number generator, e.g. * just before generating a key. */ void random_reseed(ptrlen seed); @@ -1901,6 +2077,9 @@ void agent_cancel_query(agent_pending_query *); void agent_query_synchronous(strbuf *in, void **out, int *outlen); bool agent_exists(void); +/* For stream-oriented agent connections, if available. */ +Socket *agent_connect(Plug *plug); + /* * Exports from wildcard.c */ @@ -1929,7 +2108,8 @@ bool is_interactive(void); void console_print_error_msg(const char *prefix, const char *msg); void console_print_error_msg_fmt_v( const char *prefix, const char *fmt, va_list ap); -void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...); +void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) + PRINTF_LIKE(2, 3); /* * Exports from printing.c. @@ -1958,16 +2138,38 @@ void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p); bool cmdline_host_ok(Conf *); -#define TOOLTYPE_FILETRANSFER 1 -#define TOOLTYPE_NONNETWORK 2 -#define TOOLTYPE_HOST_ARG 4 -#define TOOLTYPE_HOST_ARG_CAN_BE_SESSION 8 -#define TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX 16 -#define TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD 32 -#define TOOLTYPE_PORT_ARG 64 -extern int cmdline_tooltype; +bool cmdline_verbose(void); +bool cmdline_loaded_session(void); + +/* + * Here we have a flags word provided by each tool, which describes + * the capabilities of that tool that cmdline.c needs to know about. + * It will refuse certain command-line options if a particular tool + * inherently can't do anything sensible. For example, the file + * transfer tools (psftp, pscp) can't do a great deal with protocol + * selections (ever tried running scp over telnet?) or with port + * forwarding (even if it wasn't a hideously bad idea, they don't have + * the select/poll infrastructure to make them work). + */ +extern const unsigned cmdline_tooltype; + +/* Bit flags for the above */ +#define TOOLTYPE_LIST(X) \ + X(TOOLTYPE_FILETRANSFER) \ + X(TOOLTYPE_NONNETWORK) \ + X(TOOLTYPE_HOST_ARG) \ + X(TOOLTYPE_HOST_ARG_CAN_BE_SESSION) \ + X(TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) \ + X(TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) \ + X(TOOLTYPE_PORT_ARG) \ + X(TOOLTYPE_NO_VERBOSE_OPTION) \ + /* end of list */ +#define BITFLAG_INDEX(val) val ## _bitflag_index, +enum { TOOLTYPE_LIST(BITFLAG_INDEX) }; +#define BITFLAG_DEF(val) val = 1U << (val ## _bitflag_index), +enum { TOOLTYPE_LIST(BITFLAG_DEF) }; -void cmdline_error(const char *, ...); +void cmdline_error(const char *, ...) PRINTF_LIKE(1, 2); /* * Exports from config.c. @@ -1985,8 +2187,6 @@ void conf_filesel_handler(union control *ctrl, dlgparam *dlg, void *data, int event); void conf_fontsel_handler(union control *ctrl, dlgparam *dlg, void *data, int event); -/* Much more special-purpose function needed by sercfg.c */ -void config_protocolbuttons_handler(union control *, dlgparam *, void *, int); void setup_config_box(struct controlbox *b, bool midsession, int protocol, int protcfginfo); diff --git a/raw.c b/raw.c index abea744..0c45498 100644 --- a/raw.c +++ b/raw.c @@ -33,7 +33,7 @@ static void c_write(Raw *raw, const void *buf, size_t len) sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG); } -static void raw_log(Plug *plug, int type, SockAddr *addr, int port, +static void raw_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Raw *raw = container_of(plug, Raw, plug); @@ -105,10 +105,10 @@ static void raw_sent(Plug *plug, size_t bufsize) } static const PlugVtable Raw_plugvt = { - raw_log, - raw_closing, - raw_receive, - raw_sent + .log = raw_log, + .closing = raw_closing, + .receive = raw_receive, + .sent = raw_sent, }; /* @@ -119,10 +119,10 @@ static const PlugVtable Raw_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *raw_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, char **realhost, - bool nodelay, bool keepalive) +static char *raw_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; @@ -135,7 +135,7 @@ static const char *raw_init(Seat *seat, Backend **backend_handle, raw = snew(Raw); raw->plug.vt = &Raw_plugvt; - raw->backend.vt = &raw_backend; + raw->backend.vt = vt; raw->s = NULL; raw->closed_on_socket_error = false; *backend_handle = &raw->backend; @@ -155,7 +155,7 @@ static const char *raw_init(Seat *seat, Backend **backend_handle, raw->logctx, "main connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); - return err; + return dupstr(err); } if (port < 0) @@ -167,7 +167,7 @@ static const char *raw_init(Seat *seat, Backend **backend_handle, raw->s = new_connection(addr, *realhost, port, false, true, nodelay, keepalive, &raw->plug, conf); if ((err = sk_socket_error(raw->s)) != NULL) - return err; + return dupstr(err); loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { @@ -307,24 +307,24 @@ static int raw_cfg_info(Backend *be) return 0; } -const struct BackendVtable raw_backend = { - raw_init, - raw_free, - raw_reconfig, - raw_send, - raw_sendbuffer, - raw_size, - raw_special, - raw_get_specials, - raw_connected, - raw_exitcode, - raw_sendok, - raw_ldisc, - raw_provide_ldisc, - raw_unthrottle, - raw_cfg_info, - NULL /* test_for_upstream */, - "raw", - PROT_RAW, - 0 +const BackendVtable raw_backend = { + .init = raw_init, + .free = raw_free, + .reconfig = raw_reconfig, + .send = raw_send, + .sendbuffer = raw_sendbuffer, + .size = raw_size, + .special = raw_special, + .get_specials = raw_get_specials, + .connected = raw_connected, + .exitcode = raw_exitcode, + .sendok = raw_sendok, + .ldisc_option_state = raw_ldisc, + .provide_ldisc = raw_provide_ldisc, + .unthrottle = raw_unthrottle, + .cfg_info = raw_cfg_info, + .id = "raw", + .displayname = "Raw", + .protocol = PROT_RAW, + .default_port = 0, }; diff --git a/release.pl b/release.pl index 1c83f78..c4e9822 100755 --- a/release.pl +++ b/release.pl @@ -24,7 +24,7 @@ "no-ftp" => \$skip_ftp) or &usage(); -# --set-version: construct a local commit which updates the version +# --setver: construct a local commit which updates the version # number, and the command-line help transcripts in the docs. if ($setver) { defined $version or die "use --version"; diff --git a/rlogin.c b/rlogin.c index e03210e..2a3714e 100644 --- a/rlogin.c +++ b/rlogin.c @@ -37,7 +37,7 @@ static void c_write(Rlogin *rlogin, const void *buf, size_t len) sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG); } -static void rlogin_log(Plug *plug, int type, SockAddr *addr, int port, +static void rlogin_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Rlogin *rlogin = container_of(plug, Rlogin, plug); @@ -139,10 +139,10 @@ static void rlogin_startup(Rlogin *rlogin, const char *ruser) } static const PlugVtable Rlogin_plugvt = { - rlogin_log, - rlogin_closing, - rlogin_receive, - rlogin_sent + .log = rlogin_log, + .closing = rlogin_closing, + .receive = rlogin_receive, + .sent = rlogin_sent, }; /* @@ -153,10 +153,10 @@ static const PlugVtable Rlogin_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *rlogin_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, char **realhost, - bool nodelay, bool keepalive) +static char *rlogin_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; @@ -167,7 +167,7 @@ static const char *rlogin_init(Seat *seat, Backend **backend_handle, rlogin = snew(Rlogin); rlogin->plug.vt = &Rlogin_plugvt; - rlogin->backend.vt = &rlogin_backend; + rlogin->backend.vt = vt; rlogin->s = NULL; rlogin->closed_on_socket_error = false; rlogin->seat = seat; @@ -188,7 +188,7 @@ static const char *rlogin_init(Seat *seat, Backend **backend_handle, rlogin->logctx, "rlogin connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); - return err; + return dupstr(err); } if (port < 0) @@ -200,7 +200,7 @@ static const char *rlogin_init(Seat *seat, Backend **backend_handle, rlogin->s = new_connection(addr, *realhost, port, true, false, nodelay, keepalive, &rlogin->plug, conf); if ((err = sk_socket_error(rlogin->s)) != NULL) - return err; + return dupstr(err); loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { @@ -237,7 +237,8 @@ static const char *rlogin_init(Seat *seat, Backend **backend_handle, if (ret >= 0) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); - rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); + rlogin_startup(rlogin, prompt_get_result_ref( + rlogin->prompt->prompts[0])); } } @@ -286,7 +287,8 @@ static size_t rlogin_send(Backend *be, const char *buf, size_t len) if (ret >= 0) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); - rlogin_startup(rlogin, rlogin->prompt->prompts[0]->result); + rlogin_startup(rlogin, prompt_get_result_ref( + rlogin->prompt->prompts[0])); /* that nulls out rlogin->prompt, so then we'll start sending * data down the wire in the obvious way */ } @@ -403,24 +405,24 @@ static int rlogin_cfg_info(Backend *be) return 0; } -const struct BackendVtable rlogin_backend = { - rlogin_init, - rlogin_free, - rlogin_reconfig, - rlogin_send, - rlogin_sendbuffer, - rlogin_size, - rlogin_special, - rlogin_get_specials, - rlogin_connected, - rlogin_exitcode, - rlogin_sendok, - rlogin_ldisc, - rlogin_provide_ldisc, - rlogin_unthrottle, - rlogin_cfg_info, - NULL /* test_for_upstream */, - "rlogin", - PROT_RLOGIN, - 513 +const BackendVtable rlogin_backend = { + .init = rlogin_init, + .free = rlogin_free, + .reconfig = rlogin_reconfig, + .send = rlogin_send, + .sendbuffer = rlogin_sendbuffer, + .size = rlogin_size, + .special = rlogin_special, + .get_specials = rlogin_get_specials, + .connected = rlogin_connected, + .exitcode = rlogin_exitcode, + .sendok = rlogin_sendok, + .ldisc_option_state = rlogin_ldisc, + .provide_ldisc = rlogin_provide_ldisc, + .unthrottle = rlogin_unthrottle, + .cfg_info = rlogin_cfg_info, + .id = "rlogin", + .displayname = "Rlogin", + .protocol = PROT_RLOGIN, + .default_port = 513, }; diff --git a/scpserver.c b/scpserver.c index 02e2fd0..3c6e455 100644 --- a/scpserver.c +++ b/scpserver.c @@ -334,15 +334,15 @@ static void scp_reply_attrs( reply->attrs = attrs; } -static const struct SftpReplyBuilderVtable ScpReplyReceiver_vt = { - scp_reply_ok, - scp_reply_error, - scp_reply_simple_name, - scp_reply_name_count, - scp_reply_full_name, - scp_reply_handle, - scp_reply_data, - scp_reply_attrs, +static const SftpReplyBuilderVtable ScpReplyReceiver_vt = { + .reply_ok = scp_reply_ok, + .reply_error = scp_reply_error, + .reply_simple_name = scp_reply_simple_name, + .reply_name_count = scp_reply_name_count, + .reply_full_name = scp_reply_full_name, + .reply_handle = scp_reply_handle, + .reply_data = scp_reply_data, + .reply_attrs = scp_reply_attrs, }; static void scp_reply_setup(ScpReplyReceiver *reply) @@ -431,7 +431,8 @@ static char *scp_source_err_base(ScpSource *scp, const char *fmt, va_list ap) sshfwd_write_ext(scp->sc, true, "\012", 1); return msg; } -static void scp_source_err(ScpSource *scp, const char *fmt, ...) +static PRINTF_LIKE(2, 3) void scp_source_err( + ScpSource *scp, const char *fmt, ...) { va_list ap; @@ -439,7 +440,8 @@ static void scp_source_err(ScpSource *scp, const char *fmt, ...) sfree(scp_source_err_base(scp, fmt, ap)); va_end(ap); } -static void scp_source_abort(ScpSource *scp, const char *fmt, ...) +static PRINTF_LIKE(2, 3) void scp_source_abort( + ScpSource *scp, const char *fmt, ...) { va_list ap; char *msg; @@ -485,11 +487,11 @@ static size_t scp_source_send(ScpServer *s, const void *data, size_t length); static void scp_source_eof(ScpServer *s); static void scp_source_throttle(ScpServer *s, bool throttled); -static struct ScpServerVtable ScpSource_ScpServer_vt = { - scp_source_free, - scp_source_send, - scp_source_throttle, - scp_source_eof, +static const ScpServerVtable ScpSource_ScpServer_vt = { + .free = scp_source_free, + .send = scp_source_send, + .throttle = scp_source_throttle, + .eof = scp_source_eof, }; static ScpSource *scp_source_new( @@ -1001,11 +1003,11 @@ static size_t scp_sink_send(ScpServer *s, const void *data, size_t length); static void scp_sink_eof(ScpServer *s); static void scp_sink_throttle(ScpServer *s, bool throttled) {} -static struct ScpServerVtable ScpSink_ScpServer_vt = { - scp_sink_free, - scp_sink_send, - scp_sink_throttle, - scp_sink_eof, +static const ScpServerVtable ScpSink_ScpServer_vt = { + .free = scp_sink_free, + .send = scp_sink_send, + .throttle = scp_sink_throttle, + .eof = scp_sink_eof, }; static void scp_sink_coroutine(ScpSink *scp); @@ -1074,7 +1076,7 @@ static void scp_sink_coroutine(ScpSink *scp) * Send an ack, and read a command. */ sshfwd_write(scp->sc, "\0", 1); - scp->command->len = 0; + strbuf_clear(scp->command); while (1) { crMaybeWaitUntilV(scp->input_eof || bufchain_size(&scp->data) > 0); if (scp->input_eof) @@ -1095,7 +1097,7 @@ static void scp_sink_coroutine(ScpSink *scp) /* * Parse the command. */ - scp->command->len--; /* chomp the newline */ + strbuf_chomp(scp->command, '\n'); scp->command_chr = scp->command->len > 0 ? scp->command->s[0] : '\0'; if (scp->command_chr == 'T') { unsigned long dummy1, dummy2; @@ -1131,7 +1133,7 @@ static void scp_sink_coroutine(ScpSink *scp) ptrlen leafname = make_ptrlen( p, scp->command->len - (p - scp->command->s)); - scp->filename_sb->len = 0; + strbuf_clear(scp->filename_sb); put_datapl(scp->filename_sb, scp->head->destpath); if (scp->head->isdir) { if (scp->filename_sb->len > 0 && @@ -1295,11 +1297,11 @@ static size_t scp_error_send(ScpServer *s, const void *data, size_t length) static void scp_error_eof(ScpServer *s) {} static void scp_error_throttle(ScpServer *s, bool throttled) {} -static struct ScpServerVtable ScpError_ScpServer_vt = { - scp_error_free, - scp_error_send, - scp_error_throttle, - scp_error_eof, +static const ScpServerVtable ScpError_ScpServer_vt = { + .free = scp_error_free, + .send = scp_error_send, + .throttle = scp_error_throttle, + .eof = scp_error_eof, }; static void scp_error_send_message_cb(void *vscp) @@ -1312,7 +1314,8 @@ static void scp_error_send_message_cb(void *vscp) sshfwd_initiate_close(scp->sc, scp->message); } -static ScpError *scp_error_new(SshChannel *sc, const char *fmt, ...) +static PRINTF_LIKE(2, 3) ScpError *scp_error_new( + SshChannel *sc, const char *fmt, ...) { va_list ap; ScpError *scp = snew(ScpError); diff --git a/sercfg.c b/sercfg.c deleted file mode 100644 index 9cd0e06..0000000 --- a/sercfg.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * sercfg.c - the serial-port specific parts of the PuTTY - * configuration box. Centralised as cross-platform code because - * more than one platform will want to use it, but not part of the - * main configuration. The expectation is that each platform's - * local config function will call out to ser_setup_config_box() if - * it needs to set up the standard serial stuff. (Of course, it can - * then apply local tweaks after ser_setup_config_box() returns, if - * it needs to.) - */ - -#include -#include - -#include "putty.h" -#include "dialog.h" -#include "storage.h" - -static void serial_parity_handler(union control *ctrl, dlgparam *dlg, - void *data, int event) -{ - static const struct { - const char *name; - int val; - } parities[] = { - {"None", SER_PAR_NONE}, - {"Odd", SER_PAR_ODD}, - {"Even", SER_PAR_EVEN}, - {"Mark", SER_PAR_MARK}, - {"Space", SER_PAR_SPACE}, - }; - int mask = ctrl->listbox.context.i; - int i, j; - Conf *conf = (Conf *)data; - - if (event == EVENT_REFRESH) { - /* Fetching this once at the start of the function ensures we - * remember what the right value is supposed to be when - * operations below cause reentrant calls to this function. */ - int oldparity = conf_get_int(conf, CONF_serparity); - - dlg_update_start(ctrl, dlg); - dlg_listbox_clear(ctrl, dlg); - for (i = 0; i < lenof(parities); i++) { - if (mask & (1 << i)) - dlg_listbox_addwithid(ctrl, dlg, parities[i].name, - parities[i].val); - } - for (i = j = 0; i < lenof(parities); i++) { - if (mask & (1 << i)) { - if (oldparity == parities[i].val) { - dlg_listbox_select(ctrl, dlg, j); - break; - } - j++; - } - } - if (i == lenof(parities)) { /* an unsupported setting was chosen */ - dlg_listbox_select(ctrl, dlg, 0); - oldparity = SER_PAR_NONE; - } - dlg_update_done(ctrl, dlg); - conf_set_int(conf, CONF_serparity, oldparity); /* restore */ - } else if (event == EVENT_SELCHANGE) { - int i = dlg_listbox_index(ctrl, dlg); - if (i < 0) - i = SER_PAR_NONE; - else - i = dlg_listbox_getid(ctrl, dlg, i); - conf_set_int(conf, CONF_serparity, i); - } -} - -static void serial_flow_handler(union control *ctrl, dlgparam *dlg, - void *data, int event) -{ - static const struct { - const char *name; - int val; - } flows[] = { - {"None", SER_FLOW_NONE}, - {"XON/XOFF", SER_FLOW_XONXOFF}, - {"RTS/CTS", SER_FLOW_RTSCTS}, - {"DSR/DTR", SER_FLOW_DSRDTR}, - }; - int mask = ctrl->listbox.context.i; - int i, j; - Conf *conf = (Conf *)data; - - if (event == EVENT_REFRESH) { - /* Fetching this once at the start of the function ensures we - * remember what the right value is supposed to be when - * operations below cause reentrant calls to this function. */ - int oldflow = conf_get_int(conf, CONF_serflow); - - dlg_update_start(ctrl, dlg); - dlg_listbox_clear(ctrl, dlg); - for (i = 0; i < lenof(flows); i++) { - if (mask & (1 << i)) - dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val); - } - for (i = j = 0; i < lenof(flows); i++) { - if (mask & (1 << i)) { - if (oldflow == flows[i].val) { - dlg_listbox_select(ctrl, dlg, j); - break; - } - j++; - } - } - if (i == lenof(flows)) { /* an unsupported setting was chosen */ - dlg_listbox_select(ctrl, dlg, 0); - oldflow = SER_FLOW_NONE; - } - dlg_update_done(ctrl, dlg); - conf_set_int(conf, CONF_serflow, oldflow);/* restore */ - } else if (event == EVENT_SELCHANGE) { - int i = dlg_listbox_index(ctrl, dlg); - if (i < 0) - i = SER_FLOW_NONE; - else - i = dlg_listbox_getid(ctrl, dlg, i); - conf_set_int(conf, CONF_serflow, i); - } -} - -void ser_setup_config_box(struct controlbox *b, bool midsession, - int parity_mask, int flow_mask) -{ - struct controlset *s; - union control *c; - - if (!midsession) { - int i; - - /* - * Add the serial back end to the protocols list at the - * top of the config box. - */ - s = ctrl_getset(b, "Session", "hostport", - "Specify the destination you want to connect to"); - - for (i = 0; i < s->ncontrols; i++) { - c = s->ctrls[i]; - if (c->generic.type == CTRL_RADIO && - c->generic.handler == config_protocolbuttons_handler) { - c->radio.nbuttons++; - c->radio.ncolumns++; - c->radio.buttons = - sresize(c->radio.buttons, c->radio.nbuttons, char *); - c->radio.buttons[c->radio.nbuttons-1] = - dupstr("Serial"); - c->radio.buttondata = - sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); - c->radio.buttondata[c->radio.nbuttons-1] = I(PROT_SERIAL); - if (c->radio.shortcuts) { - c->radio.shortcuts = - sresize(c->radio.shortcuts, c->radio.nbuttons, char); - c->radio.shortcuts[c->radio.nbuttons-1] = 'r'; - } - } - } - } - - /* - * Entirely new Connection/Serial panel for serial port - * configuration. - */ - ctrl_settitle(b, "Connection/Serial", - "Options controlling local serial lines"); - - if (!midsession) { - /* - * We don't permit switching to a different serial port in - * midflight, although we do allow all other - * reconfiguration. - */ - s = ctrl_getset(b, "Connection/Serial", "serline", - "Select a serial line"); - ctrl_editbox(s, "Serial line to connect to", 'l', 40, - HELPCTX(serial_line), - conf_editbox_handler, I(CONF_serline), I(1)); - } - - s = ctrl_getset(b, "Connection/Serial", "sercfg", "Configure the serial line"); - ctrl_editbox(s, "Speed (baud)", 's', 40, - HELPCTX(serial_speed), - conf_editbox_handler, I(CONF_serspeed), I(-1)); - ctrl_editbox(s, "Data bits", 'b', 40, - HELPCTX(serial_databits), - conf_editbox_handler, I(CONF_serdatabits), I(-1)); - /* - * Stop bits come in units of one half. - */ - ctrl_editbox(s, "Stop bits", 't', 40, - HELPCTX(serial_stopbits), - conf_editbox_handler, I(CONF_serstopbits), I(-2)); - ctrl_droplist(s, "Parity", 'p', 40, - HELPCTX(serial_parity), - serial_parity_handler, I(parity_mask)); - ctrl_droplist(s, "Flow control", 'f', 40, - HELPCTX(serial_flow), - serial_flow_handler, I(flow_mask)); -} diff --git a/sesschan.c b/sesschan.c index f9ea141..0cf85b3 100644 --- a/sesschan.c +++ b/sesschan.c @@ -13,6 +13,12 @@ #include "sshserver.h" #include "sftp.h" +struct agentfwd { + ConnectionLayer *cl; + Socket *socket; + Plug plug; +}; + typedef struct sesschan { SshChannel *c; @@ -35,8 +41,7 @@ typedef struct sesschan { int n_x11_sockets; Socket *x11_sockets[MAX_X11_SOCKETS]; - Plug agentfwd_plug; - Socket *agentfwd_socket; + agentfwd *agent; Backend *backend; @@ -72,29 +77,29 @@ static bool sesschan_change_window_size( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight); -static const struct ChannelVtable sesschan_channelvt = { - sesschan_free, - chan_remotely_opened_confirmation, - chan_remotely_opened_failure, - sesschan_send, - sesschan_send_eof, - sesschan_set_input_wanted, - sesschan_log_close_msg, - sesschan_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - sesschan_run_shell, - sesschan_run_command, - sesschan_run_subsystem, - sesschan_enable_x11_forwarding, - sesschan_enable_agent_forwarding, - sesschan_allocate_pty, - sesschan_set_env, - sesschan_send_break, - sesschan_send_signal, - sesschan_change_window_size, - chan_no_request_response, +static const ChannelVtable sesschan_channelvt = { + .free = sesschan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = sesschan_send, + .send_eof = sesschan_send_eof, + .set_input_wanted = sesschan_set_input_wanted, + .log_close_msg = sesschan_log_close_msg, + .want_close = sesschan_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = sesschan_run_shell, + .run_command = sesschan_run_command, + .run_subsystem = sesschan_run_subsystem, + .enable_x11_forwarding = sesschan_enable_x11_forwarding, + .enable_agent_forwarding = sesschan_enable_agent_forwarding, + .allocate_pty = sesschan_allocate_pty, + .set_env = sesschan_set_env, + .send_break = sesschan_send_break, + .send_signal = sesschan_send_signal, + .change_window_size = sesschan_change_window_size, + .request_response = chan_no_request_response, }; static size_t sftp_chan_send( @@ -102,29 +107,29 @@ static size_t sftp_chan_send( static void sftp_chan_send_eof(Channel *chan); static char *sftp_log_close_msg(Channel *chan); -static const struct ChannelVtable sftp_channelvt = { - sesschan_free, - chan_remotely_opened_confirmation, - chan_remotely_opened_failure, - sftp_chan_send, - sftp_chan_send_eof, - sesschan_set_input_wanted, - sftp_log_close_msg, - chan_default_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - chan_no_request_response, +static const ChannelVtable sftp_channelvt = { + .free = sesschan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = sftp_chan_send, + .send_eof = sftp_chan_send_eof, + .set_input_wanted = sesschan_set_input_wanted, + .log_close_msg = sftp_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, }; static size_t scp_chan_send( @@ -133,29 +138,29 @@ static void scp_chan_send_eof(Channel *chan); static void scp_set_input_wanted(Channel *chan, bool wanted); static char *scp_log_close_msg(Channel *chan); -static const struct ChannelVtable scp_channelvt = { - sesschan_free, - chan_remotely_opened_confirmation, - chan_remotely_opened_failure, - scp_chan_send, - scp_chan_send_eof, - scp_set_input_wanted, - scp_log_close_msg, - chan_default_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - chan_no_request_response, +static const ChannelVtable scp_channelvt = { + .free = sesschan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = scp_chan_send, + .send_eof = scp_chan_send_eof, + .set_input_wanted = scp_set_input_wanted, + .log_close_msg = scp_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, }; static void sesschan_eventlog(LogPolicy *lp, const char *event) {} @@ -165,9 +170,10 @@ static int sesschan_askappend( void (*callback)(void *ctx, int result), void *ctx) { return 2; } static const LogPolicyVtable sesschan_logpolicy_vt = { - sesschan_eventlog, - sesschan_askappend, - sesschan_logging_error, + .eventlog = sesschan_eventlog, + .askappend = sesschan_askappend, + .logging_error = sesschan_logging_error, + .verbose = null_lp_verbose_no, }; static size_t sesschan_seat_output( @@ -178,24 +184,27 @@ static void sesschan_connection_fatal(Seat *seat, const char *message); static bool sesschan_get_window_pixel_size(Seat *seat, int *w, int *h); static const SeatVtable sesschan_seat_vt = { - sesschan_seat_output, - sesschan_seat_eof, - nullseat_get_userpass_input, - sesschan_notify_remote_exit, - sesschan_connection_fatal, - nullseat_update_specials_menu, - nullseat_get_ttymode, - nullseat_set_busy_status, - nullseat_verify_ssh_host_key, - nullseat_confirm_weak_crypto_primitive, - nullseat_confirm_weak_cached_hostkey, - nullseat_is_never_utf8, - nullseat_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - sesschan_get_window_pixel_size, - nullseat_stripctrl_new, - nullseat_set_trust_status, + .output = sesschan_seat_output, + .eof = sesschan_seat_eof, + .get_userpass_input = nullseat_get_userpass_input, + .notify_remote_exit = sesschan_notify_remote_exit, + .connection_fatal = sesschan_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = nullseat_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .verify_ssh_host_key = nullseat_verify_ssh_host_key, + .confirm_weak_crypto_primitive = nullseat_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = nullseat_confirm_weak_cached_hostkey, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = sesschan_get_window_pixel_size, + .stripctrl_new = nullseat_stripctrl_new, + .set_trust_status = nullseat_set_trust_status, + .verbose = nullseat_verbose_no, + .interactive = nullseat_interactive_no, + .get_cursor_position = nullseat_get_cursor_position, }; Channel *sesschan_new(SshChannel *c, LogContext *logctx, @@ -244,8 +253,8 @@ static void sesschan_free(Channel *chan) sftpsrv_free(sess->sftpsrv); for (i = 0; i < sess->n_x11_sockets; i++) sk_close(sess->x11_sockets[i]); - if (sess->agentfwd_socket) - sk_close(sess->agentfwd_socket); + if (sess->agent) + agentfwd_free(sess->agent); sfree(sess); } @@ -349,7 +358,7 @@ bool sesschan_run_subsystem(Channel *chan, ptrlen subsys) return false; } -static void fwd_log(Plug *plug, int type, SockAddr *addr, int port, +static void fwd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { /* don't expect any weirdnesses from a listening socket */ } static void fwd_closing(Plug *plug, const char *error_msg, int error_code, @@ -365,7 +374,7 @@ static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) SocketPeerInfo *pi; const char *err; - chan = portfwd_raw_new(sess->c->cl, &plug); + chan = portfwd_raw_new(sess->c->cl, &plug, false); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) { portfwd_raw_free(chan); @@ -379,11 +388,9 @@ static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) } static const PlugVtable xfwd_plugvt = { - fwd_log, - fwd_closing, - NULL, /* recv */ - NULL, /* send */ - xfwd_accepting, + .log = fwd_log, + .closing = fwd_closing, + .accepting = xfwd_accepting, }; bool sesschan_enable_x11_forwarding( @@ -435,53 +442,74 @@ bool sesschan_enable_x11_forwarding( static int agentfwd_accepting( Plug *p, accept_fn_t constructor, accept_ctx_t ctx) { - sesschan *sess = container_of(p, sesschan, agentfwd_plug); + agentfwd *agent = container_of(p, agentfwd, plug); Plug *plug; Channel *chan; Socket *s; const char *err; - chan = portfwd_raw_new(sess->c->cl, &plug); + chan = portfwd_raw_new(agent->cl, &plug, false); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) { portfwd_raw_free(chan); return 1; } - portfwd_raw_setup(chan, s, ssh_serverside_agent_open(sess->c->cl, chan)); + portfwd_raw_setup(chan, s, ssh_serverside_agent_open(agent->cl, chan)); return 0; } static const PlugVtable agentfwd_plugvt = { - fwd_log, - fwd_closing, - NULL, /* recv */ - NULL, /* send */ - agentfwd_accepting, + .log = fwd_log, + .closing = fwd_closing, + .accepting = agentfwd_accepting, }; +agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out) +{ + agentfwd *agent = snew(agentfwd); + agent->cl = cl; + agent->plug.vt = &agentfwd_plugvt; + + char *dir_prefix = dupprintf("/tmp/%s-agentfwd", appname); + char *error = NULL, *socketname = NULL; + agent->socket = platform_make_agent_socket( + &agent->plug, dir_prefix, &error, &socketname); + sfree(dir_prefix); + sfree(error); + + if (!agent->socket) { + sfree(agent); + sfree(socketname); + return NULL; + } + + *socketname_out = socketname; + return agent; +} + +void agentfwd_free(agentfwd *agent) +{ + if (agent->socket) + sk_close(agent->socket); + sfree(agent); +} + bool sesschan_enable_agent_forwarding(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); - char *error, *socketname, *dir_prefix; - - dir_prefix = dupprintf("/tmp/%s-agentfwd", appname); + char *socketname; - sess->agentfwd_plug.vt = &agentfwd_plugvt; - sess->agentfwd_socket = platform_make_agent_socket( - &sess->agentfwd_plug, dir_prefix, &error, &socketname); + assert(!sess->agent); - sfree(dir_prefix); + sess->agent = agentfwd_new(sess->c->cl, &socketname); - if (sess->agentfwd_socket) { - conf_set_str_str(sess->conf, CONF_environmt, - "SSH_AUTH_SOCK", socketname); - } + if (!sess->agent) + return false; - sfree(error); + conf_set_str_str(sess->conf, CONF_environmt, "SSH_AUTH_SOCK", socketname); sfree(socketname); - - return sess->agentfwd_socket != NULL; + return true; } bool sesschan_allocate_pty( @@ -640,10 +668,10 @@ static void sesschan_notify_remote_exit(Seat *seat) sshfwd_send_exit_signal( sess->c, signame, false, ptrlen_from_asciz(sigmsg)); - sfree(sigmsg); - got_signal = true; } + + sfree(sigmsg); } else { int signum = pty_backend_exit_signum(sess->backend); diff --git a/settings.c b/settings.c index 1f84fee..452e58a 100644 --- a/settings.c +++ b/settings.c @@ -39,6 +39,7 @@ static const struct keyvalwhere kexnames[] = { static const struct keyvalwhere hknames[] = { { "ed25519", HK_ED25519, -1, +1 }, + { "ed448", HK_ED448, -1, +1 }, { "ecdsa", HK_ECDSA, -1, -1 }, { "dsa", HK_DSA, -1, -1 }, { "rsa", HK_RSA, -1, -1 }, @@ -69,6 +70,10 @@ const char *const ttymodes[] = { "CS7", "CS8", "PARENB", "PARODD", NULL }; +static int default_protocol, default_port; +void settings_set_default_protocol(int newval) { default_protocol = newval; } +void settings_set_default_port(int newval) { default_port = newval; } + /* * Convenience functions to access the backends[] array * (which is only present in tools that manage settings). @@ -78,7 +83,7 @@ const struct BackendVtable *backend_vt_from_name(const char *name) { const struct BackendVtable *const *p; for (p = backends; *p != NULL; p++) - if (!strcmp((*p)->name, name)) + if (!strcmp((*p)->id, name)) return *p; return NULL; } @@ -488,14 +493,12 @@ static void write_clip_setting(settings_w *sesskey, const char *savekey, case CLIPUI_EXPLICIT: write_setting_s(sesskey, savekey, "explicit"); break; - case CLIPUI_CUSTOM: - { - char *sval = dupcat("custom:", conf_get_str(conf, strconfkey), - (const char *)NULL); - write_setting_s(sesskey, savekey, sval); - sfree(sval); - } + case CLIPUI_CUSTOM: { + char *sval = dupcat("custom:", conf_get_str(conf, strconfkey)); + write_setting_s(sesskey, savekey, sval); + sfree(sval); break; + } } } @@ -554,7 +557,7 @@ void save_open_settings(settings_w *sesskey, Conf *conf) const struct BackendVtable *vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); if (vt) - p = vt->name; + p = vt->id; } write_setting_s(sesskey, "Protocol", p); write_setting_i(sesskey, "PortNumber", conf_get_int(conf, CONF_port)); @@ -599,12 +602,14 @@ void save_open_settings(settings_w *sesskey, Conf *conf) wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist); wprefs(sesskey, "HostKey", hknames, HK_MAX, conf, CONF_ssh_hklist); + write_setting_b(sesskey, "PreferKnownHostKeys", conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys)); write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time)); #ifndef NO_GSSAPI write_setting_i(sesskey, "GssapiRekey", conf_get_int(conf, CONF_gssapirekey)); #endif write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data)); write_setting_b(sesskey, "SshNoAuth", conf_get_bool(conf, CONF_ssh_no_userauth)); + write_setting_b(sesskey, "SshNoTrivialAuth", conf_get_bool(conf, CONF_ssh_no_trivial_userauth)); write_setting_b(sesskey, "SshBanner", conf_get_bool(conf, CONF_ssh_show_banner)); write_setting_b(sesskey, "AuthTIS", conf_get_bool(conf, CONF_try_tis_auth)); write_setting_b(sesskey, "AuthKI", conf_get_bool(conf, CONF_try_ki_auth)); @@ -783,6 +788,14 @@ void save_open_settings(settings_w *sesskey, Conf *conf) write_setting_b(sesskey, "ConnectionSharingUpstream", conf_get_bool(conf, CONF_ssh_connection_sharing_upstream)); write_setting_b(sesskey, "ConnectionSharingDownstream", conf_get_bool(conf, CONF_ssh_connection_sharing_downstream)); wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, false); + + /* + * SUPDUP settings + */ + write_setting_s(sesskey, "SUPDUPLocation", conf_get_str(conf, CONF_supdup_location)); + write_setting_i(sesskey, "SUPDUPCharset", conf_get_int(conf, CONF_supdup_ascii_set)); + write_setting_b(sesskey, "SUPDUPMoreProcessing", conf_get_bool(conf, CONF_supdup_more)); + write_setting_b(sesskey, "SUPDUPScrolling", conf_get_bool(conf, CONF_supdup_scroll)); } bool load_settings(const char *section, Conf *conf) @@ -995,6 +1008,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf) } gprefs(sesskey, "HostKey", "ed25519,ecdsa,rsa,dsa,WARN", hknames, HK_MAX, conf, CONF_ssh_hklist); + gppb(sesskey, "PreferKnownHostKeys", true, conf, CONF_ssh_prefer_known_hostkeys); gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); #ifndef NO_GSSAPI gppi(sesskey, "GssapiRekey", GSS_DEF_REKEY_MINS, conf, CONF_gssapirekey); @@ -1012,6 +1026,7 @@ void load_open_settings(settings_r *sesskey, Conf *conf) gpps(sesskey, "LogHost", "", conf, CONF_loghost); gppb(sesskey, "SSH2DES", false, conf, CONF_ssh2_des_cbc); gppb(sesskey, "SshNoAuth", false, conf, CONF_ssh_no_userauth); + gppb(sesskey, "SshNoTrivialAuth", false, conf, CONF_ssh_no_trivial_userauth); gppb(sesskey, "SshBanner", true, conf, CONF_ssh_show_banner); gppb(sesskey, "AuthTIS", false, conf, CONF_try_tis_auth); gppb(sesskey, "AuthKI", true, conf, CONF_try_ki_auth); @@ -1073,6 +1088,8 @@ void load_open_settings(settings_r *sesskey, Conf *conf) gppb(sesskey, "HideMousePtr", false, conf, CONF_hide_mouseptr); gppb(sesskey, "SunkenEdge", false, conf, CONF_sunken_edge); gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border); + //gppi(sesskey, "CurType", 0, conf, CONF_cursor_type); + //gppb(sesskey, "BlinkCur", false, conf, CONF_blink_cur); gppi(sesskey, "CurType", 1, conf, CONF_cursor_type); gppb(sesskey, "BlinkCur", true, conf, CONF_blink_cur); /* pedantic compiler tells me I can't use conf, CONF_beep as an int * :-) */ @@ -1252,6 +1269,14 @@ void load_open_settings(settings_r *sesskey, Conf *conf) gppb(sesskey, "ConnectionSharingDownstream", true, conf, CONF_ssh_connection_sharing_downstream); gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys); + + /* + * SUPDUP settings + */ + gpps(sesskey, "SUPDUPLocation", "The Internet", conf, CONF_supdup_location); + gppi(sesskey, "SUPDUPCharset", false, conf, CONF_supdup_ascii_set); + gppb(sesskey, "SUPDUPMoreProcessing", false, conf, CONF_supdup_more); + gppb(sesskey, "SUPDUPScrolling", false, conf, CONF_supdup_scroll); } bool do_defaults(const char *session, Conf *conf) diff --git a/sftp.c b/sftp.c index 0b9355d..a76702f 100644 --- a/sftp.c +++ b/sftp.c @@ -38,7 +38,17 @@ struct sftp_packet *sftp_recv(void) if (!sftp_recvdata(x, 4)) return NULL; - pkt = sftp_recv_prepare(GET_32BIT_MSB_FIRST(x)); + /* Impose _some_ upper bound on packet size. We never expect to + * receive more than 32K of data in response to an FXP_READ, + * because we decide how much data to ask for. FXP_READDIR and + * pathname-returning things like FXP_REALPATH don't have an + * explicit bound, so I suppose we just have to trust the server + * to be sensible. */ + unsigned pktlen = GET_32BIT_MSB_FIRST(x); + if (pktlen > (1<<20)) + return NULL; + + pkt = sftp_recv_prepare(pktlen); if (!sftp_recvdata(pkt->data, pkt->length)) { sftp_pkt_free(pkt); diff --git a/sftpserver.c b/sftpserver.c index 1ff3d37..ba21687 100644 --- a/sftpserver.c +++ b/sftpserver.c @@ -15,6 +15,7 @@ struct sftp_packet *sftp_handle_request( { struct sftp_packet *reply; unsigned id; + uint32_t flags; ptrlen path, dstpath, handle, data; uint64_t offset; unsigned length; @@ -266,13 +267,13 @@ static void default_reply_attrs( put_fxp_attrs(d->pkt, attrs); } -const struct SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = { - default_reply_ok, - default_reply_error, - default_reply_simple_name, - default_reply_name_count, - default_reply_full_name, - default_reply_handle, - default_reply_data, - default_reply_attrs, +const SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = { + .reply_ok = default_reply_ok, + .reply_error = default_reply_error, + .reply_simple_name = default_reply_simple_name, + .reply_name_count = default_reply_name_count, + .reply_full_name = default_reply_full_name, + .reply_handle = default_reply_handle, + .reply_data = default_reply_data, + .reply_attrs = default_reply_attrs, }; diff --git a/smallprimes.c b/smallprimes.c new file mode 100644 index 0000000..a43b0bd --- /dev/null +++ b/smallprimes.c @@ -0,0 +1,44 @@ +/* + * smallprimes.c: implementation of the array of small primes defined + * in sshkeygen.h. + */ + +#include +#include "ssh.h" +#include "sshkeygen.h" + +/* The real array that stores the primes. It has to be writable in + * this module, but outside this module, we only expose the + * const-qualified pointer 'smallprimes' so that nobody else can + * accidentally overwrite it. */ +static unsigned short smallprimes_array[NSMALLPRIMES]; + +const unsigned short *const smallprimes = smallprimes_array; + +void init_smallprimes(void) +{ + if (smallprimes_array[0]) + return; /* already done */ + + bool A[65536]; + + for (size_t i = 2; i < lenof(A); i++) + A[i] = true; + + for (size_t i = 2; i < lenof(A); i++) { + if (!A[i]) + continue; + for (size_t j = 2*i; j < lenof(A); j += i) + A[j] = false; + } + + size_t pos = 0; + for (size_t i = 2; i < lenof(A); i++) { + if (A[i]) { + assert(pos < NSMALLPRIMES); + smallprimes_array[pos++] = i; + } + } + + assert(pos == NSMALLPRIMES); +} diff --git a/ssh.c b/ssh.c index 417d6c1..91e7e86 100644 --- a/ssh.c +++ b/ssh.c @@ -254,7 +254,9 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, connection_layer, ssh->savedhost, ssh->fullhostname, conf_get_filename(ssh->conf, CONF_keyfile), conf_get_bool(ssh->conf, CONF_ssh_show_banner), - conf_get_bool(ssh->conf, CONF_tryagent), username, + conf_get_bool(ssh->conf, CONF_tryagent), + conf_get_bool(ssh->conf, CONF_ssh_no_trivial_userauth), + username, conf_get_bool(ssh->conf, CONF_change_username), conf_get_bool(ssh->conf, CONF_try_ki_auth), #ifndef NO_GSSAPI @@ -311,8 +313,8 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, ssh_connect_bpp(ssh); connection_layer = ssh2_connection_new( - ssh, NULL, false, ssh->conf, ssh_verstring_get_remote(old_bpp), - &ssh->cl); + ssh, ssh->connshare, false, ssh->conf, + ssh_verstring_get_remote(old_bpp), &ssh->cl); ssh_connect_ppl(ssh, connection_layer); ssh->base_layer = connection_layer; } @@ -457,7 +459,8 @@ static void ssh_initiate_connection_close(Ssh *ssh) va_list ap; \ va_start(ap, fmt); \ msg = dupvprintf(fmt, ap); \ - va_end(ap); + va_end(ap); \ + ((void)0) /* eat trailing semicolon */ void ssh_remote_error(Ssh *ssh, const char *fmt, ...) { @@ -557,12 +560,12 @@ void ssh_user_close(Ssh *ssh, const char *fmt, ...) } } -void ssh_deferred_abort_callback(void *vctx) +static void ssh_deferred_abort_callback(void *vctx) { Ssh *ssh = (Ssh *)vctx; char *msg = ssh->deferred_abort_message; ssh->deferred_abort_message = NULL; - ssh_sw_abort(ssh, msg); + ssh_sw_abort(ssh, "%s", msg); sfree(msg); } @@ -575,8 +578,8 @@ void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) } } -static void ssh_socket_log(Plug *plug, int type, SockAddr *addr, int port, - const char *error_msg, int error_code) +static void ssh_socket_log(Plug *plug, PlugLogType type, SockAddr *addr, + int port, const char *error_msg, int error_code) { Ssh *ssh = container_of(plug, Ssh, plug); @@ -690,12 +693,24 @@ static bool ssh_test_for_upstream(const char *host, int port, Conf *conf) return ret; } +static char *ssh_close_warn_text(Backend *be) +{ + Ssh *ssh = container_of(be, Ssh, backend); + if (!ssh->connshare) + return NULL; + int ndowns = share_ndownstreams(ssh->connshare); + if (ndowns == 0) + return NULL; + char *msg = dupprintf("This will also close %d downstream connection%s.", + ndowns, ndowns==1 ? "" : "s"); + return msg; +} + static const PlugVtable Ssh_plugvt = { - ssh_socket_log, - ssh_closing, - ssh_receive, - ssh_sent, - NULL + .log = ssh_socket_log, + .closing = ssh_closing, + .receive = ssh_receive, + .sent = ssh_sent, }; /* @@ -704,7 +719,7 @@ static const PlugVtable Ssh_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *connect_to_host( +static char *connect_to_host( Ssh *ssh, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { @@ -743,7 +758,7 @@ static const char *connect_to_host( ssh->fullhostname = NULL; *realhost = dupstr(host); /* best we can do */ - if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) { + if (seat_verbose(ssh->seat) || seat_interactive(ssh->seat)) { /* In an interactive session, or in verbose mode, announce * in the console window that we're a sharing downstream, * to avoid confusing users as to why this session doesn't @@ -765,7 +780,7 @@ static const char *connect_to_host( ssh->logctx, "SSH connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); - return err; + return dupstr(err); } ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */ @@ -775,7 +790,7 @@ static const char *connect_to_host( if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; seat_notify_remote_exit(ssh->seat); - return err; + return dupstr(err); } } @@ -860,17 +875,28 @@ static void ssh_cache_conf_values(Ssh *ssh) ssh->pls.omit_data = conf_get_bool(ssh->conf, CONF_logomitdata); } +bool ssh_is_bare(Ssh *ssh) +{ + return ssh->backend.vt->protocol == PROT_SSHCONN; +} + +/* Dummy connlayer must provide ssh_sharing_no_more_downstreams, + * because it might be called early due to plink -shareexists */ +static void dummy_sharing_no_more_downstreams(ConnectionLayer *cl) {} +static const ConnectionLayerVtable dummy_connlayer_vtable = { + .sharing_no_more_downstreams = dummy_sharing_no_more_downstreams, +}; + /* * Called to set up the connection. * * Returns an error message, or NULL on success. */ -static const char *ssh_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, char **realhost, - bool nodelay, bool keepalive) +static char *ssh_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { - const char *p; Ssh *ssh; ssh = snew(Ssh); @@ -890,24 +916,28 @@ static const char *ssh_init(Seat *seat, Backend **backend_handle, ssh->term_width = conf_get_int(ssh->conf, CONF_width); ssh->term_height = conf_get_int(ssh->conf, CONF_height); - ssh->backend.vt = &ssh_backend; + ssh->backend.vt = vt; *backend_handle = &ssh->backend; + ssh->bare_connection = (vt->protocol == PROT_SSHCONN); + ssh->seat = seat; + ssh->cl_dummy.vt = &dummy_connlayer_vtable; ssh->cl_dummy.logctx = ssh->logctx = logctx; random_ref(); /* do this now - may be needed by sharing setup code */ ssh->need_random_unref = true; - p = connect_to_host(ssh, host, port, realhost, nodelay, keepalive); - if (p != NULL) { + char *conn_err = connect_to_host( + ssh, host, port, realhost, nodelay, keepalive); + if (conn_err) { /* Call random_unref now instead of waiting until the caller * frees this useless Ssh object, in case the caller is * impatient and just exits without bothering, in which case * the random seed won't be re-saved. */ ssh->need_random_unref = false; random_unref(); - return p; + return conn_err; } return NULL; @@ -996,7 +1026,8 @@ static size_t ssh_sendbuffer(Backend *be) backlog = ssh_stdin_backlog(ssh->cl); - /* FIXME: also include sizes of pqs */ + if (ssh->base_layer) + backlog += ssh_ppl_queued_data_size(ssh->base_layer); /* * If the SSH socket itself has backed up, add the total backup @@ -1052,21 +1083,21 @@ static const SessionSpecial *ssh_get_specials(Backend *be) * and amalgamate the list into one combined one. */ - struct ssh_add_special_ctx ctx; + struct ssh_add_special_ctx ctx[1]; - ctx.specials = NULL; - ctx.nspecials = ctx.specials_size = 0; + ctx->specials = NULL; + ctx->nspecials = ctx->specials_size = 0; if (ssh->base_layer) - ssh_ppl_get_specials(ssh->base_layer, ssh_add_special, &ctx); + ssh_ppl_get_specials(ssh->base_layer, ssh_add_special, ctx); - if (ctx.specials) { + if (ctx->specials) { /* If the list is non-empty, terminate it with a SS_EXITMENU. */ - ssh_add_special(&ctx, NULL, SS_EXITMENU, 0); + ssh_add_special(ctx, NULL, SS_EXITMENU, 0); } sfree(ssh->specials); - ssh->specials = ctx.specials; + ssh->specials = ctx->specials; return ssh->specials; } @@ -1171,24 +1202,49 @@ void ssh_got_fallback_cmd(Ssh *ssh) ssh->fallback_cmd = true; } -const struct BackendVtable ssh_backend = { - ssh_init, - ssh_free, - ssh_reconfig, - ssh_send, - ssh_sendbuffer, - ssh_size, - ssh_special, - ssh_get_specials, - ssh_connected, - ssh_return_exitcode, - ssh_sendok, - ssh_ldisc, - ssh_provide_ldisc, - ssh_unthrottle, - ssh_cfg_info, - ssh_test_for_upstream, - "ssh", - PROT_SSH, - 22 +const BackendVtable ssh_backend = { + .init = ssh_init, + .free = ssh_free, + .reconfig = ssh_reconfig, + .send = ssh_send, + .sendbuffer = ssh_sendbuffer, + .size = ssh_size, + .special = ssh_special, + .get_specials = ssh_get_specials, + .connected = ssh_connected, + .exitcode = ssh_return_exitcode, + .sendok = ssh_sendok, + .ldisc_option_state = ssh_ldisc, + .provide_ldisc = ssh_provide_ldisc, + .unthrottle = ssh_unthrottle, + .cfg_info = ssh_cfg_info, + .test_for_upstream = ssh_test_for_upstream, + .close_warn_text = ssh_close_warn_text, + .id = "ssh", + .displayname = "SSH", + .protocol = PROT_SSH, + .default_port = 22, +}; + +const BackendVtable sshconn_backend = { + .init = ssh_init, + .free = ssh_free, + .reconfig = ssh_reconfig, + .send = ssh_send, + .sendbuffer = ssh_sendbuffer, + .size = ssh_size, + .special = ssh_special, + .get_specials = ssh_get_specials, + .connected = ssh_connected, + .exitcode = ssh_return_exitcode, + .sendok = ssh_sendok, + .ldisc_option_state = ssh_ldisc, + .provide_ldisc = ssh_provide_ldisc, + .unthrottle = ssh_unthrottle, + .cfg_info = ssh_cfg_info, + .test_for_upstream = ssh_test_for_upstream, + .close_warn_text = ssh_close_warn_text, + .id = "ssh-connection", + .displayname = "Bare ssh-connection", + .protocol = PROT_SSHCONN, }; diff --git a/ssh.h b/ssh.h index 0672524..49ab279 100644 --- a/ssh.h +++ b/ssh.h @@ -52,6 +52,7 @@ struct ssh_channel; typedef struct PacketQueueNode PacketQueueNode; struct PacketQueueNode { PacketQueueNode *next, *prev; + size_t formal_size; /* contribution to PacketQueueBase's total_size */ bool on_free_queue; /* is this packet scheduled for freeing? */ }; @@ -84,6 +85,7 @@ typedef struct PktOut { typedef struct PacketQueueBase { PacketQueueNode end; + size_t total_size; /* sum of all formal_size fields on the queue */ struct IdempotentCallback *ic; } PacketQueueBase; @@ -207,6 +209,8 @@ struct ssh_rportfwd { }; void free_rportfwd(struct ssh_rportfwd *rpf); +typedef struct ConnectionLayerVtable ConnectionLayerVtable; + struct ConnectionLayerVtable { /* Allocate and free remote-to-local port forwardings, called by * PortFwdManager or by connection sharing */ @@ -290,11 +294,10 @@ struct ConnectionLayerVtable { * channel) what its preference for line-discipline options is. */ void (*set_ldisc_option)(ConnectionLayer *cl, int option, bool value); - /* Communicate to the connection layer whether X and agent - * forwarding were successfully enabled (for purposes of - * knowing whether to accept subsequent channel-opens). */ + /* Communicate to the connection layer whether X forwarding was + * successfully enabled (for purposes of knowing whether to accept + * subsequent channel-opens). */ void (*enable_x_fwd)(ConnectionLayer *cl); - void (*enable_agent_fwd)(ConnectionLayer *cl); /* Communicate to the connection layer whether the main session * channel currently wants user input. */ @@ -366,8 +369,6 @@ static inline void ssh_set_ldisc_option(ConnectionLayer *cl, int opt, bool val) { cl->vt->set_ldisc_option(cl, opt, val); } static inline void ssh_enable_x_fwd(ConnectionLayer *cl) { cl->vt->enable_x_fwd(cl); } -static inline void ssh_enable_agent_fwd(ConnectionLayer *cl) -{ cl->vt->enable_agent_fwd(cl); } static inline void ssh_set_wants_user_input(ConnectionLayer *cl, bool wanted) { cl->vt->set_wants_user_input(cl, wanted); } @@ -383,7 +384,7 @@ char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret, bool portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port, const char *keyhost, int keyport, Conf *conf); bool portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port); -Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug); +Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug, bool start_ready); void portfwd_raw_free(Channel *pfchan); void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc); @@ -397,18 +398,19 @@ void ssh_throttle_conn(Ssh *ssh, int adjust); void ssh_got_exitcode(Ssh *ssh, int status); void ssh_ldisc_update(Ssh *ssh); void ssh_got_fallback_cmd(Ssh *ssh); +bool ssh_is_bare(Ssh *ssh); /* Communications back to ssh.c from the BPP */ void ssh_conn_processed_data(Ssh *ssh); void ssh_check_frozen(Ssh *ssh); /* Functions to abort the connection, for various reasons. */ -void ssh_remote_error(Ssh *ssh, const char *fmt, ...); -void ssh_remote_eof(Ssh *ssh, const char *fmt, ...); -void ssh_proto_error(Ssh *ssh, const char *fmt, ...); -void ssh_sw_abort(Ssh *ssh, const char *fmt, ...); -void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...); -void ssh_user_close(Ssh *ssh, const char *fmt, ...); +void ssh_remote_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); +void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); +void ssh_proto_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); +void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); +void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); +void ssh_user_close(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); /* Bit positions in the SSH-1 cipher protocol word */ #define SSH1_CIPHER_IDEA 1 @@ -473,6 +475,7 @@ struct ec_ecurve EdwardsCurve *ec; EdwardsPoint *G; mp_int *G_order; + unsigned log2_cofactor; }; typedef enum EllipticCurveType { @@ -502,6 +505,7 @@ const ssh_keyalg *ec_alg_by_oid(int len, const void *oid, const struct ec_curve **curve); const unsigned char *ec_alg_oid(const ssh_keyalg *alg, int *oidlen); extern const int ec_nist_curve_lengths[], n_ec_nist_curve_lengths; +extern const int ec_ed_curve_lengths[], n_ec_ed_curve_lengths; bool ec_nist_alg_and_curve_by_bits(int bits, const struct ec_curve **curve, const ssh_keyalg **alg); @@ -525,6 +529,24 @@ struct eddsa_key { WeierstrassPoint *ecdsa_public(mp_int *private_key, const ssh_keyalg *alg); EdwardsPoint *eddsa_public(mp_int *private_key, const ssh_keyalg *alg); +typedef struct key_components { + size_t ncomponents, componentsize; + struct { + char *name; + bool is_mp_int; + union { + char *text; + mp_int *mp; + }; + } *components; +} key_components; +key_components *key_components_new(void); +void key_components_add_text(key_components *kc, + const char *name, const char *value); +void key_components_add_mp(key_components *kc, + const char *name, mp_int *value); +void key_components_free(key_components *kc); + /* * SSH-1 never quite decided which order to store the two components * of an RSA key. During connection setup, the server sends its host @@ -539,16 +561,20 @@ void BinarySource_get_rsa_ssh1_pub( BinarySource *src, RSAKey *result, RsaSsh1Order order); void BinarySource_get_rsa_ssh1_priv( BinarySource *src, RSAKey *rsa); +RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src); bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key); mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key); bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key, strbuf *outbuf); char *rsastr_fmt(RSAKey *key); char *rsa_ssh1_fingerprint(RSAKey *key); +char **rsa_ssh1_fake_all_fingerprints(RSAKey *key); bool rsa_verify(RSAKey *key); void rsa_ssh1_public_blob(BinarySink *bs, RSAKey *key, RsaSsh1Order order); int rsa_ssh1_public_blob_len(ptrlen data); +void rsa_ssh1_private_blob_agent(BinarySink *bs, RSAKey *key); void freersapriv(RSAKey *key); void freersakey(RSAKey *key); +key_components *rsa_components(RSAKey *key); uint32_t crc32_rfc1662(ptrlen data); uint32_t crc32_ssh1(ptrlen data); @@ -714,32 +740,52 @@ struct ssh_hash { struct ssh_hashalg { ssh_hash *(*new)(const ssh_hashalg *alg); - ssh_hash *(*copy)(ssh_hash *); - void (*final)(ssh_hash *, unsigned char *); /* ALSO FREES THE ssh_hash! */ + void (*reset)(ssh_hash *); + void (*copyfrom)(ssh_hash *dest, ssh_hash *src); + void (*digest)(ssh_hash *, unsigned char *); void (*free)(ssh_hash *); - int hlen; /* output length in bytes */ - int blocklen; /* length of the hash's input block, or 0 for N/A */ + size_t hlen; /* output length in bytes */ + size_t blocklen; /* length of the hash's input block, or 0 for N/A */ const char *text_basename; /* the semantic name of the hash */ const char *annotation; /* extra info, e.g. which of multiple impls */ const char *text_name; /* both combined, e.g. "SHA-n (unaccelerated)" */ + const void *extra; /* private to the hash implementation */ }; static inline ssh_hash *ssh_hash_new(const ssh_hashalg *alg) -{ return alg->new(alg); } -static inline ssh_hash *ssh_hash_copy(ssh_hash *h) -{ return h->vt->copy(h); } -static inline void ssh_hash_final(ssh_hash *h, unsigned char *out) -{ h->vt->final(h, out); } +{ ssh_hash *h = alg->new(alg); if (h) h->vt->reset(h); return h; } +static inline ssh_hash *ssh_hash_copy(ssh_hash *orig) +{ ssh_hash *h = orig->vt->new(orig->vt); h->vt->copyfrom(h, orig); return h; } +static inline void ssh_hash_digest(ssh_hash *h, unsigned char *out) +{ h->vt->digest(h, out); } static inline void ssh_hash_free(ssh_hash *h) { h->vt->free(h); } static inline const ssh_hashalg *ssh_hash_alg(ssh_hash *h) { return h->vt; } +/* The reset and copyfrom vtable methods return void. But for call-site + * convenience, these wrappers return their input pointer. */ +static inline ssh_hash *ssh_hash_reset(ssh_hash *h) +{ h->vt->reset(h); return h; } +static inline ssh_hash *ssh_hash_copyfrom(ssh_hash *dest, ssh_hash *src) +{ dest->vt->copyfrom(dest, src); return dest; } + +/* ssh_hash_final emits the digest _and_ frees the ssh_hash */ +static inline void ssh_hash_final(ssh_hash *h, unsigned char *out) +{ h->vt->digest(h, out); h->vt->free(h); } + +/* ssh_hash_digest_nondestructive generates a finalised hash from the + * given object without changing its state, so you can continue + * appending data to get a hash of an extended string. */ +static inline void ssh_hash_digest_nondestructive(ssh_hash *h, + unsigned char *out) +{ ssh_hash_final(ssh_hash_copy(h), out); } + /* Handy macros for defining all those text-name fields at once */ #define HASHALG_NAMES_BARE(base) \ - base, NULL, base -#define HASHALG_NAMES_ANNOTATED(base, annotation) \ - base, annotation, base " (" annotation ")" + .text_basename = base, .annotation = NULL, .text_name = base +#define HASHALG_NAMES_ANNOTATED(base, ann) \ + .text_basename = base, .annotation = ann, .text_name = base " (" ann ")" void hash_simple(const ssh_hashalg *alg, ptrlen data, void *output); @@ -777,6 +823,7 @@ struct ssh_keyalg { void (*private_blob)(ssh_key *key, BinarySink *); void (*openssh_blob) (ssh_key *key, BinarySink *); char *(*cache_str) (ssh_key *key); + key_components *(*components) (ssh_key *key); /* 'Class methods' that don't deal with an ssh_key at all */ int (*pubkey_bits) (const ssh_keyalg *self, ptrlen blob); @@ -813,6 +860,8 @@ static inline void ssh_key_openssh_blob(ssh_key *key, BinarySink *bs) { key->vt->openssh_blob(key, bs); } static inline char *ssh_key_cache_str(ssh_key *key) { return key->vt->cache_str(key); } +static inline key_components *ssh_key_components(ssh_key *key) +{ return key->vt->components(key); } static inline int ssh_key_public_bits(const ssh_keyalg *self, ptrlen blob) { return self->pubkey_bits(self, blob); } static inline const ssh_keyalg *ssh_key_alg(ssh_key *key) @@ -882,8 +931,20 @@ struct ssh2_userkey { char *comment; /* the key comment */ }; +/* Argon2 password hashing function */ +typedef enum { Argon2d = 0, Argon2i = 1, Argon2id = 2 } Argon2Flavour; +void argon2(Argon2Flavour, uint32_t mem, uint32_t passes, + uint32_t parallel, uint32_t taglen, + ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out); +void argon2_choose_passes( + Argon2Flavour, uint32_t mem, uint32_t milliseconds, uint32_t *passes, + uint32_t parallel, uint32_t taglen, ptrlen P, ptrlen S, ptrlen K, ptrlen X, + strbuf *out); +/* The H' hash defined in Argon2, exposed just for testcrypt */ +strbuf *argon2_long_hash(unsigned length, ptrlen data); + /* The maximum length of any hash algorithm. (bytes) */ -#define MAX_HASH_LEN (64) /* longest is SHA-512 */ +#define MAX_HASH_LEN (114) /* longest is SHAKE256 with 114-byte output */ extern const ssh_cipheralg ssh_3des_ssh1; extern const ssh_cipheralg ssh_blowfish_ssh1; @@ -928,20 +989,34 @@ extern const ssh_hashalg ssh_sha256; extern const ssh_hashalg ssh_sha256_hw; extern const ssh_hashalg ssh_sha256_sw; extern const ssh_hashalg ssh_sha384; +extern const ssh_hashalg ssh_sha384_hw; +extern const ssh_hashalg ssh_sha384_sw; extern const ssh_hashalg ssh_sha512; +extern const ssh_hashalg ssh_sha512_hw; +extern const ssh_hashalg ssh_sha512_sw; +extern const ssh_hashalg ssh_sha3_224; +extern const ssh_hashalg ssh_sha3_256; +extern const ssh_hashalg ssh_sha3_384; +extern const ssh_hashalg ssh_sha3_512; +extern const ssh_hashalg ssh_shake256_114bytes; +extern const ssh_hashalg ssh_blake2b; extern const ssh_kexes ssh_diffiehellman_group1; extern const ssh_kexes ssh_diffiehellman_group14; extern const ssh_kexes ssh_diffiehellman_gex; extern const ssh_kexes ssh_gssk5_sha1_kex; extern const ssh_kexes ssh_rsa_kex; extern const ssh_kex ssh_ec_kex_curve25519; +extern const ssh_kex ssh_ec_kex_curve448; extern const ssh_kex ssh_ec_kex_nistp256; extern const ssh_kex ssh_ec_kex_nistp384; extern const ssh_kex ssh_ec_kex_nistp521; extern const ssh_kexes ssh_ecdh_kex; extern const ssh_keyalg ssh_dss; extern const ssh_keyalg ssh_rsa; +extern const ssh_keyalg ssh_rsa_sha256; +extern const ssh_keyalg ssh_rsa_sha512; extern const ssh_keyalg ssh_ecdsa_ed25519; +extern const ssh_keyalg ssh_ecdsa_ed448; extern const ssh_keyalg ssh_ecdsa_nistp256; extern const ssh_keyalg ssh_ecdsa_nistp384; extern const ssh_keyalg ssh_ecdsa_nistp521; @@ -954,6 +1029,10 @@ extern const ssh2_macalg ssh_hmac_sha256; extern const ssh2_macalg ssh2_poly1305; extern const ssh_compression_alg ssh_zlib; +/* Special constructor: BLAKE2b can be instantiated with any hash + * length up to 128 bytes */ +ssh_hash *blake2b_new_general(unsigned hashlen); + /* * On some systems, you have to detect hardware crypto acceleration by * asking the local OS API rather than OS-agnostically asking the CPU @@ -963,6 +1042,7 @@ extern const ssh_compression_alg ssh_zlib; bool platform_aes_hw_available(void); bool platform_sha256_hw_available(void); bool platform_sha1_hw_available(void); +bool platform_sha512_hw_available(void); /* * PuTTY version number formatted as an SSH version string. @@ -1123,13 +1203,6 @@ mp_int *dh_create_e(dh_ctx *, int nbits); const char *dh_validate_f(dh_ctx *, mp_int *f); mp_int *dh_find_K(dh_ctx *, mp_int *f); -bool rsa_ssh1_encrypted(const Filename *filename, char **comment); -int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs, - char **commentptr, const char **errorstr); -int rsa_ssh1_loadkey(const Filename *filename, RSAKey *key, - const char *passphrase, const char **errorstr); -bool rsa_ssh1_savekey(const Filename *filename, RSAKey *key, char *passphrase); - static inline bool is_base64_char(char c) { return ((c >= '0' && c <= '9') || @@ -1144,21 +1217,75 @@ extern void base64_encode_atom(const unsigned char *data, int n, char *out); extern void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl); -/* ssh2_load_userkey can return this as an error */ +/* ppk_load_* can return this as an error */ extern ssh2_userkey ssh2_wrong_passphrase; #define SSH2_WRONG_PASSPHRASE (&ssh2_wrong_passphrase) -bool ssh2_userkey_encrypted(const Filename *filename, char **comment); -ssh2_userkey *ssh2_load_userkey( - const Filename *filename, const char *passphrase, const char **errorstr); -bool ssh2_userkey_loadpub( - const Filename *filename, char **algorithm, BinarySink *bs, - char **commentptr, const char **errorstr); -bool ssh2_save_userkey( - const Filename *filename, ssh2_userkey *key, char *passphrase); +bool ppk_encrypted_s(BinarySource *src, char **comment); +bool ppk_encrypted_f(const Filename *filename, char **comment); +bool rsa1_encrypted_s(BinarySource *src, char **comment); +bool rsa1_encrypted_f(const Filename *filename, char **comment); + +ssh2_userkey *ppk_load_s(BinarySource *src, const char *passphrase, + const char **errorstr); +ssh2_userkey *ppk_load_f(const Filename *filename, const char *passphrase, + const char **errorstr); +int rsa1_load_s(BinarySource *src, RSAKey *key, + const char *passphrase, const char **errorstr); +int rsa1_load_f(const Filename *filename, RSAKey *key, + const char *passphrase, const char **errorstr); + +typedef struct ppk_save_parameters { + unsigned fmt_version; /* currently 2 or 3 */ + + /* + * Parameters for fmt_version == 3 + */ + Argon2Flavour argon2_flavour; + uint32_t argon2_mem; /* in Kbyte */ + bool argon2_passes_auto; + union { + uint32_t argon2_passes; /* if auto == false */ + uint32_t argon2_milliseconds; /* if auto == true */ + }; + uint32_t argon2_parallelism; + + /* The ability to choose a specific salt is only intended for the + * use of the automated test of PuTTYgen. It's a (mild) security + * risk to do it with any passphrase you actually care about, + * because it invalidates the entire point of having a salt in the + * first place. */ + const uint8_t *salt; + size_t saltlen; +} ppk_save_parameters; +extern const ppk_save_parameters ppk_save_default_parameters; + +strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase, + const ppk_save_parameters *params); +bool ppk_save_f(const Filename *filename, ssh2_userkey *key, + const char *passphrase, const ppk_save_parameters *params); +strbuf *rsa1_save_sb(RSAKey *key, const char *passphrase); +bool rsa1_save_f(const Filename *filename, RSAKey *key, + const char *passphrase); + +bool ppk_loadpub_s(BinarySource *src, char **algorithm, BinarySink *bs, + char **commentptr, const char **errorstr); +bool ppk_loadpub_f(const Filename *filename, char **algorithm, BinarySink *bs, + char **commentptr, const char **errorstr); +int rsa1_loadpub_s(BinarySource *src, BinarySink *bs, + char **commentptr, const char **errorstr); +int rsa1_loadpub_f(const Filename *filename, BinarySink *bs, + char **commentptr, const char **errorstr); + +extern const ssh_keyalg *const all_keyalgs[]; +extern const size_t n_keyalgs; const ssh_keyalg *find_pubkey_alg(const char *name); const ssh_keyalg *find_pubkey_alg_len(ptrlen name); +/* Convenient wrappers on the LoadedFile mechanism suitable for key files */ +LoadedFile *lf_load_keyfile(const Filename *filename, const char **errptr); +LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr); + enum { SSH_KEYTYPE_UNOPENABLE, SSH_KEYTYPE_UNKNOWN, @@ -1202,24 +1329,47 @@ enum { SSH_KEYTYPE_SSH2_PUBLIC_RFC4716, SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH }; + +typedef enum { + SSH_FPTYPE_MD5, + SSH_FPTYPE_SHA256, +} FingerprintType; + +#define SSH_FPTYPE_DEFAULT SSH_FPTYPE_SHA256 +#define SSH_N_FPTYPES (SSH_FPTYPE_SHA256 + 1) + +FingerprintType ssh2_pick_fingerprint(char **fingerprints, + FingerprintType preferred_type); +FingerprintType ssh2_pick_default_fingerprint(char **fingerprints); + char *ssh1_pubkey_str(RSAKey *ssh1key); void ssh1_write_pubkey(FILE *fp, RSAKey *ssh1key); char *ssh2_pubkey_openssh_str(ssh2_userkey *key); void ssh2_write_pubkey(FILE *fp, const char *comment, const void *v_pub_blob, int pub_len, int keytype); -char *ssh2_fingerprint_blob(ptrlen); -char *ssh2_fingerprint(ssh_key *key); +char *ssh2_fingerprint_blob(ptrlen, FingerprintType); +char *ssh2_fingerprint(ssh_key *key, FingerprintType); +char **ssh2_all_fingerprints_for_blob(ptrlen); +char **ssh2_all_fingerprints(ssh_key *key); +void ssh2_free_all_fingerprints(char **); int key_type(const Filename *filename); +int key_type_s(BinarySource *src); const char *key_type_to_str(int type); bool import_possible(int type); int import_target_type(int type); bool import_encrypted(const Filename *filename, int type, char **comment); +bool import_encrypted_s(const Filename *filename, BinarySource *src, + int type, char **comment); int import_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase, const char **errmsg_p); +int import_ssh1_s(BinarySource *src, int type, + RSAKey *key, char *passphrase, const char **errmsg_p); ssh2_userkey *import_ssh2(const Filename *filename, int type, char *passphrase, const char **errmsg_p); +ssh2_userkey *import_ssh2_s(BinarySource *src, int type, + char *passphrase, const char **errmsg_p); bool export_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase); bool export_ssh2(const Filename *filename, int type, @@ -1231,8 +1381,10 @@ void des3_decrypt_pubkey_ossh(const void *key, const void *iv, void *blk, int len); void des3_encrypt_pubkey_ossh(const void *key, const void *iv, void *blk, int len); -void aes256_encrypt_pubkey(const void *key, void *blk, int len); -void aes256_decrypt_pubkey(const void *key, void *blk, int len); +void aes256_encrypt_pubkey(const void *key, const void *iv, + void *blk, int len); +void aes256_decrypt_pubkey(const void *key, const void *iv, + void *blk, int len); void des_encrypt_xdmauth(const void *key, void *blk, int len); void des_decrypt_xdmauth(const void *key, void *blk, int len); @@ -1241,30 +1393,6 @@ void openssh_bcrypt(const char *passphrase, const unsigned char *salt, int saltbytes, int rounds, unsigned char *out, int outbytes); -/* - * For progress updates in the key generation utility. - */ -#define PROGFN_INITIALISE 1 -#define PROGFN_LIN_PHASE 2 -#define PROGFN_EXP_PHASE 3 -#define PROGFN_PHASE_EXTENT 4 -#define PROGFN_READY 5 -#define PROGFN_PROGRESS 6 -typedef void (*progfn_t) (void *param, int action, int phase, int progress); - -int rsa_generate(RSAKey *key, int bits, progfn_t pfn, - void *pfnparam); -int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, - void *pfnparam); -int ecdsa_generate(struct ecdsa_key *key, int bits, progfn_t pfn, - void *pfnparam); -int eddsa_generate(struct eddsa_key *key, int bits, progfn_t pfn, - void *pfnparam); -mp_int *primegen( - int bits, int modulus, int residue, mp_int *factor, - int phase, progfn_t pfn, void *pfnparam, unsigned firstbits); -void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation); - /* * Connection-sharing API provided by platforms. This function must * either: @@ -1351,6 +1479,7 @@ void platform_ssh_share_cleanup(const char *name); X(y, SSH2_MSG_DEBUG, 4) \ X(y, SSH2_MSG_SERVICE_REQUEST, 5) \ X(y, SSH2_MSG_SERVICE_ACCEPT, 6) \ + X(y, SSH2_MSG_EXT_INFO, 7) \ X(y, SSH2_MSG_KEXINIT, 20) \ X(y, SSH2_MSG_NEWKEYS, 21) \ K(y, SSH2_MSG_KEXDH_INIT, 30, SSH2_PKTCTX_DHGROUP) \ @@ -1441,6 +1570,8 @@ enum { #define SSH2_AGENTC_ADD_IDENTITY 17 #define SSH2_AGENTC_REMOVE_IDENTITY 18 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 +#define SSH2_AGENTC_EXTENSION 27 +#define SSH_AGENT_EXTENSION_FAILURE 28 /* * Assorted other SSH-related enumerations. @@ -1555,8 +1686,7 @@ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset); void add_to_commasep(strbuf *buf, const char *data); bool get_commasep_word(ptrlen *list, ptrlen *word); -int verify_ssh_manual_host_key( - Conf *conf, const char *fingerprint, ssh_key *key); +int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key); typedef struct ssh_transient_hostkey_cache ssh_transient_hostkey_cache; ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void); diff --git a/ssh1bpp.c b/ssh1bpp.c index 0bc729e..46eccbe 100644 --- a/ssh1bpp.c +++ b/ssh1bpp.c @@ -36,13 +36,13 @@ static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category); static PktOut *ssh1_bpp_new_pktout(int type); -static const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = { - ssh1_bpp_free, - ssh1_bpp_handle_input, - ssh1_bpp_handle_output, - ssh1_bpp_new_pktout, - ssh1_bpp_queue_disconnect, - 0xFFFFFFFF, /* no special packet size limit for this bpp */ +static const BinaryPacketProtocolVtable ssh1_bpp_vtable = { + .free = ssh1_bpp_free, + .handle_input = ssh1_bpp_handle_input, + .handle_output = ssh1_bpp_handle_output, + .new_pktout = ssh1_bpp_new_pktout, + .queue_disconnect = ssh1_bpp_queue_disconnect, + .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ }; BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx) @@ -236,6 +236,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp) NULL, 0, NULL); } + s->pktin->qnode.formal_size = get_avail(s->pktin); pq_push(&s->bpp.in_pq, s->pktin); { diff --git a/ssh1connection-client.c b/ssh1connection-client.c index 850a94f..cf7dd04 100644 --- a/ssh1connection-client.c +++ b/ssh1connection-client.c @@ -151,7 +151,7 @@ bool ssh1_handle_direction_specific_packet( remid = get_uint32(pktin); /* Refuse if agent forwarding is disabled. */ - if (!s->agent_fwd_enabled) { + if (!ssh_agent_forwarding_permitted(&s->cl)) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, remid); @@ -161,9 +161,31 @@ bool ssh1_handle_direction_specific_packet( c->connlayer = s; ssh1_channel_init(c); c->remoteid = remid; - c->chan = agentf_new(&c->sc); c->halfopen = false; + /* + * If possible, make a stream-oriented connection to the + * agent and set up an ordinary port-forwarding type + * channel over it. + */ + Plug *plug; + Channel *ch = portfwd_raw_new(&s->cl, &plug, true); + Socket *skt = agent_connect(plug); + if (!sk_socket_error(skt)) { + portfwd_raw_setup(ch, skt, &c->sc); + c->chan = ch; + } else { + portfwd_raw_free(ch); + + /* + * Otherwise, fall back to the old-fashioned system of + * parsing the forwarded data stream ourselves for + * message boundaries, and passing each individual + * message to the one-off agent_query(). + */ + c->chan = agentf_new(&c->sc); + } + pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION); put_uint32(pktout, c->remoteid); @@ -240,15 +262,14 @@ bool ssh1_handle_direction_specific_packet( return true; - case SSH1_SMSG_EXIT_STATUS: - { - int exitcode = get_uint32(pktin); - ppl_logevent("Server sent command exit status %d", exitcode); - ssh_got_exitcode(s->ppl.ssh, exitcode); + case SSH1_SMSG_EXIT_STATUS: { + int exitcode = get_uint32(pktin); + ppl_logevent("Server sent command exit status %d", exitcode); + ssh_got_exitcode(s->ppl.ssh, exitcode); - s->session_terminated = true; - } + s->session_terminated = true; return true; + } default: return false; @@ -420,28 +441,21 @@ static void ssh1mainchan_write_eof(SshChannel *sc) pq_push(s->ppl.out_pq, pktout); } -static const struct SshChannelVtable ssh1mainchan_vtable = { - ssh1mainchan_write, - ssh1mainchan_write_eof, - NULL /* unclean_close */, - NULL /* unthrottle */, - NULL /* get_conf */, - NULL /* window_override_removed is only used by SSH-2 sharing */, - NULL /* x11_sharing_handover, likewise */, - NULL /* send_exit_status */, - NULL /* send_exit_signal */, - NULL /* send_exit_signal_numeric */, - ssh1mainchan_request_x11_forwarding, - ssh1mainchan_request_agent_forwarding, - ssh1mainchan_request_pty, - ssh1mainchan_send_env_var, - ssh1mainchan_start_shell, - ssh1mainchan_start_command, - ssh1mainchan_start_subsystem, - ssh1mainchan_send_serial_break, - ssh1mainchan_send_signal, - ssh1mainchan_send_terminal_size_change, - ssh1mainchan_hint_channel_is_simple, +static const SshChannelVtable ssh1mainchan_vtable = { + .write = ssh1mainchan_write, + .write_eof = ssh1mainchan_write_eof, + .request_x11_forwarding = ssh1mainchan_request_x11_forwarding, + .request_agent_forwarding = ssh1mainchan_request_agent_forwarding, + .request_pty = ssh1mainchan_request_pty, + .send_env_var = ssh1mainchan_send_env_var, + .start_shell = ssh1mainchan_start_shell, + .start_command = ssh1mainchan_start_command, + .start_subsystem = ssh1mainchan_start_subsystem, + .send_serial_break = ssh1mainchan_send_serial_break, + .send_signal = ssh1mainchan_send_signal, + .send_terminal_size_change = ssh1mainchan_send_terminal_size_change, + .hint_channel_is_simple = ssh1mainchan_hint_channel_is_simple, + /* other methods are NULL */ }; static void ssh1_session_confirm_callback(void *vctx) diff --git a/ssh1connection-server.c b/ssh1connection-server.c index aa44c52..4d55abe 100644 --- a/ssh1connection-server.c +++ b/ssh1connection-server.c @@ -21,28 +21,13 @@ static void ssh1sesschan_send_exit_status(SshChannel *c, int status); static void ssh1sesschan_send_exit_signal( SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg); -static const struct SshChannelVtable ssh1sesschan_vtable = { - ssh1sesschan_write, - ssh1sesschan_write_eof, - ssh1sesschan_initiate_close, - NULL /* unthrottle */, - NULL /* get_conf */, - NULL /* window_override_removed is only used by SSH-2 sharing */, - NULL /* x11_sharing_handover, likewise */, - ssh1sesschan_send_exit_status, - ssh1sesschan_send_exit_signal, - NULL /* send_exit_signal_numeric */, - NULL /* request_x11_forwarding */, - NULL /* request_agent_forwarding */, - NULL /* request_pty */, - NULL /* send_env_var */, - NULL /* start_shell */, - NULL /* start_command */, - NULL /* start_subsystem */, - NULL /* send_serial_break */, - NULL /* send_signal */, - NULL /* send_terminal_size_change */, - NULL /* hint_channel_is_simple */, +static const SshChannelVtable ssh1sesschan_vtable = { + .write = ssh1sesschan_write, + .write_eof = ssh1sesschan_write_eof, + .initiate_close = ssh1sesschan_initiate_close, + .send_exit_status = ssh1sesschan_send_exit_status, + .send_exit_signal = ssh1sesschan_send_exit_signal, + /* everything else is NULL */ }; void ssh1connection_server_configure( @@ -115,35 +100,35 @@ bool ssh1_handle_direction_specific_packet( return true; - case SSH1_CMSG_REQUEST_PTY: + case SSH1_CMSG_REQUEST_PTY: { if (s->finished_setup) goto unexpected_setup_packet; - { - ptrlen termtype = get_string(pktin); - unsigned height = get_uint32(pktin); - unsigned width = get_uint32(pktin); - unsigned pixwidth = get_uint32(pktin); - unsigned pixheight = get_uint32(pktin); - struct ssh_ttymodes modes = read_ttymodes_from_packet( - BinarySource_UPCAST(pktin), 1); - - if (get_err(pktin)) { - ppl_logevent("Unable to decode pty request packet"); - success = false; - } else if (!chan_allocate_pty( - s->mainchan_chan, termtype, width, height, - pixwidth, pixheight, modes)) { - ppl_logevent("Unable to allocate a pty"); - success = false; - } else { - success = true; - } + + ptrlen termtype = get_string(pktin); + unsigned height = get_uint32(pktin); + unsigned width = get_uint32(pktin); + unsigned pixwidth = get_uint32(pktin); + unsigned pixheight = get_uint32(pktin); + struct ssh_ttymodes modes = read_ttymodes_from_packet( + BinarySource_UPCAST(pktin), 1); + + if (get_err(pktin)) { + ppl_logevent("Unable to decode pty request packet"); + success = false; + } else if (!chan_allocate_pty( + s->mainchan_chan, termtype, width, height, + pixwidth, pixheight, modes)) { + ppl_logevent("Unable to allocate a pty"); + success = false; + } else { + success = true; } pktout = ssh_bpp_new_pktout( s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE)); pq_push(s->ppl.out_pq, pktout); return true; + } case SSH1_CMSG_PORT_FORWARD_REQUEST: if (s->finished_setup) @@ -166,25 +151,24 @@ bool ssh1_handle_direction_specific_packet( pq_push(s->ppl.out_pq, pktout); return true; - case SSH1_CMSG_X11_REQUEST_FORWARDING: + case SSH1_CMSG_X11_REQUEST_FORWARDING: { if (s->finished_setup) goto unexpected_setup_packet; - { - ptrlen authproto = get_string(pktin); - ptrlen authdata = get_string(pktin); - unsigned screen_number = 0; - if (s->remote_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) - screen_number = get_uint32(pktin); + ptrlen authproto = get_string(pktin); + ptrlen authdata = get_string(pktin); + unsigned screen_number = 0; + if (s->remote_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) + screen_number = get_uint32(pktin); - success = chan_enable_x11_forwarding( - s->mainchan_chan, false, authproto, authdata, screen_number); - } + success = chan_enable_x11_forwarding( + s->mainchan_chan, false, authproto, authdata, screen_number); pktout = ssh_bpp_new_pktout( s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE)); pq_push(s->ppl.out_pq, pktout); return true; + } case SSH1_CMSG_AGENT_REQUEST_FORWARDING: if (s->finished_setup) diff --git a/ssh1connection.c b/ssh1connection.c index 01df6e5..d805dd2 100644 --- a/ssh1connection.c +++ b/ssh1connection.c @@ -35,15 +35,16 @@ static bool ssh1_connection_want_user_input(PacketProtocolLayer *ppl); static void ssh1_connection_got_user_input(PacketProtocolLayer *ppl); static void ssh1_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf); -static const struct PacketProtocolLayerVtable ssh1_connection_vtable = { - ssh1_connection_free, - ssh1_connection_process_queue, - ssh1_common_get_specials, - ssh1_connection_special_cmd, - ssh1_connection_want_user_input, - ssh1_connection_got_user_input, - ssh1_connection_reconfigure, - NULL /* no layer names in SSH-1 */, +static const PacketProtocolLayerVtable ssh1_connection_vtable = { + .free = ssh1_connection_free, + .process_queue = ssh1_connection_process_queue, + .get_specials = ssh1_common_get_specials, + .special_cmd = ssh1_connection_special_cmd, + .want_user_input = ssh1_connection_want_user_input, + .got_user_input = ssh1_connection_got_user_input, + .reconfigure = ssh1_connection_reconfigure, + .queued_data_size = ssh_ppl_default_queued_data_size, + .name = NULL, /* no layer names in SSH-1 */ }; static void ssh1_rportfwd_remove( @@ -61,34 +62,26 @@ static void ssh1_throttle_all_channels(ConnectionLayer *cl, bool throttled); static bool ssh1_ldisc_option(ConnectionLayer *cl, int option); static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, bool value); static void ssh1_enable_x_fwd(ConnectionLayer *cl); -static void ssh1_enable_agent_fwd(ConnectionLayer *cl); static void ssh1_set_wants_user_input(ConnectionLayer *cl, bool wanted); -static const struct ConnectionLayerVtable ssh1_connlayer_vtable = { - ssh1_rportfwd_alloc, - ssh1_rportfwd_remove, - ssh1_lportfwd_open, - ssh1_session_open, - ssh1_serverside_x11_open, - ssh1_serverside_agent_open, - ssh1_add_x11_display, - NULL /* add_sharing_x11_display */, - NULL /* remove_sharing_x11_display */, - NULL /* send_packet_from_downstream */, - NULL /* alloc_sharing_channel */, - NULL /* delete_sharing_channel */, - NULL /* sharing_queue_global_request */, - NULL /* sharing_no_more_downstreams */, - ssh1_agent_forwarding_permitted, - ssh1_terminal_size, - ssh1_stdout_unthrottle, - ssh1_stdin_backlog, - ssh1_throttle_all_channels, - ssh1_ldisc_option, - ssh1_set_ldisc_option, - ssh1_enable_x_fwd, - ssh1_enable_agent_fwd, - ssh1_set_wants_user_input, +static const ConnectionLayerVtable ssh1_connlayer_vtable = { + .rportfwd_alloc = ssh1_rportfwd_alloc, + .rportfwd_remove = ssh1_rportfwd_remove, + .lportfwd_open = ssh1_lportfwd_open, + .session_open = ssh1_session_open, + .serverside_x11_open = ssh1_serverside_x11_open, + .serverside_agent_open = ssh1_serverside_agent_open, + .add_x11_display = ssh1_add_x11_display, + .agent_forwarding_permitted = ssh1_agent_forwarding_permitted, + .terminal_size = ssh1_terminal_size, + .stdout_unthrottle = ssh1_stdout_unthrottle, + .stdin_backlog = ssh1_stdin_backlog, + .throttle_all_channels = ssh1_throttle_all_channels, + .ldisc_option = ssh1_ldisc_option, + .set_ldisc_option = ssh1_set_ldisc_option, + .enable_x_fwd = ssh1_enable_x_fwd, + .set_wants_user_input = ssh1_set_wants_user_input, + /* other methods are NULL */ }; static size_t ssh1channel_write( @@ -99,28 +92,14 @@ static void ssh1channel_unthrottle(SshChannel *c, size_t bufsize); static Conf *ssh1channel_get_conf(SshChannel *c); static void ssh1channel_window_override_removed(SshChannel *c) { /* ignore */ } -static const struct SshChannelVtable ssh1channel_vtable = { - ssh1channel_write, - ssh1channel_write_eof, - ssh1channel_initiate_close, - ssh1channel_unthrottle, - ssh1channel_get_conf, - ssh1channel_window_override_removed, - NULL /* x11_sharing_handover is only used by SSH-2 connection sharing */, - NULL /* send_exit_status */, - NULL /* send_exit_signal */, - NULL /* send_exit_signal_numeric */, - NULL /* request_x11_forwarding */, - NULL /* request_agent_forwarding */, - NULL /* request_pty */, - NULL /* send_env_var */, - NULL /* start_shell */, - NULL /* start_command */, - NULL /* start_subsystem */, - NULL /* send_serial_break */, - NULL /* send_signal */, - NULL /* send_terminal_size_change */, - NULL /* hint_channel_is_simple */, +static const SshChannelVtable ssh1channel_vtable = { + .write = ssh1channel_write, + .write_eof = ssh1channel_write_eof, + .initiate_close = ssh1channel_initiate_close, + .unthrottle = ssh1channel_unthrottle, + .get_conf = ssh1channel_get_conf, + .window_override_removed = ssh1channel_window_override_removed, + /* everything else is NULL */ }; static void ssh1_channel_try_eof(struct ssh1_channel *c); @@ -520,10 +499,12 @@ static void ssh1_channel_close_local(struct ssh1_channel *c, { struct ssh1_connection_state *s = c->connlayer; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ - const char *msg = chan_log_close_msg(c->chan); + char *msg = chan_log_close_msg(c->chan); - if (msg != NULL) + if (msg != NULL) { ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : ""); + sfree(msg); + } chan_free(c->chan); c->chan = zombiechan_new(); @@ -787,14 +768,6 @@ static void ssh1_enable_x_fwd(ConnectionLayer *cl) s->X11_fwd_enabled = true; } -static void ssh1_enable_agent_fwd(ConnectionLayer *cl) -{ - struct ssh1_connection_state *s = - container_of(cl, struct ssh1_connection_state, cl); - - s->agent_fwd_enabled = true; -} - static void ssh1_set_wants_user_input(ConnectionLayer *cl, bool wanted) { struct ssh1_connection_state *s = diff --git a/ssh1connection.h b/ssh1connection.h index cb9bbe9..4437078 100644 --- a/ssh1connection.h +++ b/ssh1connection.h @@ -5,8 +5,6 @@ struct outstanding_succfail; struct ssh1_connection_state { int crState; - Ssh *ssh; - Conf *conf; int local_protoflags, remote_protoflags; @@ -33,8 +31,6 @@ struct ssh1_connection_state { struct X11FakeAuth *x11auth; tree234 *x11authtree; - bool agent_fwd_enabled; - tree234 *rportfwds; PortFwdManager *portfwdmgr; bool portfwdmgr_configured; diff --git a/ssh1login-server.c b/ssh1login-server.c index e7ff93d..c9c10ee 100644 --- a/ssh1login-server.c +++ b/ssh1login-server.c @@ -11,6 +11,7 @@ #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" +#include "sshkeygen.h" struct ssh1_login_server_state { int crState; @@ -57,19 +58,18 @@ static void ssh1_login_server_got_user_input(PacketProtocolLayer *ppl) {} static void ssh1_login_server_reconfigure( PacketProtocolLayer *ppl, Conf *conf) {} -static const struct PacketProtocolLayerVtable ssh1_login_server_vtable = { - ssh1_login_server_free, - ssh1_login_server_process_queue, - ssh1_login_server_get_specials, - ssh1_login_server_special_cmd, - ssh1_login_server_want_user_input, - ssh1_login_server_got_user_input, - ssh1_login_server_reconfigure, - NULL /* no layer names in SSH-1 */, +static const PacketProtocolLayerVtable ssh1_login_server_vtable = { + .free = ssh1_login_server_free, + .process_queue = ssh1_login_server_process_queue, + .get_specials = ssh1_login_server_get_specials, + .special_cmd = ssh1_login_server_special_cmd, + .want_user_input = ssh1_login_server_want_user_input, + .got_user_input = ssh1_login_server_got_user_input, + .reconfigure = ssh1_login_server_reconfigure, + .queued_data_size = ssh_ppl_default_queued_data_size, + .name = NULL, /* no layer names in SSH-1 */ }; -static void no_progress(void *param, int action, int phase, int iprogress) {} - PacketProtocolLayer *ssh1_login_server_new( PacketProtocolLayer *successor_layer, RSAKey *hostkey, AuthPolicy *authpolicy, const SshServerConfig *ssc) @@ -140,7 +140,14 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl) if (server_key_bits < 512) server_key_bits = s->hostkey->bytes + 256; s->servkey = snew(RSAKey); - rsa_generate(s->servkey, server_key_bits, no_progress, NULL); + + PrimeGenerationContext *pgc = primegen_new_context( + &primegen_probabilistic); + ProgressReceiver null_progress; + null_progress.vt = &null_progress_vt; + rsa_generate(s->servkey, server_key_bits, false, pgc, &null_progress); + primegen_free_context(pgc); + s->servkey->comment = NULL; s->servkey_generated_here = true; } @@ -218,7 +225,7 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl) if (rsa_ssh1_decrypt_pkcs1(s->sesskey, larger, data)) { mp_free(s->sesskey); s->sesskey = mp_from_bytes_be(ptrlen_from_strbuf(data)); - data->len = 0; + strbuf_clear(data); if (rsa_ssh1_decrypt_pkcs1(s->sesskey, smaller, data) && data->len == sizeof(s->session_key)) { memcpy(s->session_key, data->u, sizeof(s->session_key)); @@ -291,18 +298,34 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl) mp_int *modulus = get_mp_ssh1(pktin); s->authkey = auth_publickey_ssh1( s->authpolicy, s->username, modulus); + + if (!s->authkey && + s->ssc->stunt_pretend_to_accept_any_pubkey) { + mp_int *zero = mp_from_integer(0); + mp_int *fake_challenge = mp_random_in_range(zero, modulus); + + pktout = ssh_bpp_new_pktout( + s->ppl.bpp, SSH1_SMSG_AUTH_RSA_CHALLENGE); + put_mp_ssh1(pktout, fake_challenge); + pq_push(s->ppl.out_pq, pktout); + + mp_free(zero); + mp_free(fake_challenge); + } + mp_free(modulus); } - if (!s->authkey) + if (!s->authkey && + !s->ssc->stunt_pretend_to_accept_any_pubkey) continue; - if (s->authkey->bytes < 32) { + if (s->authkey && s->authkey->bytes < 32) { ppl_logevent("Auth key far too small"); continue; } - { + if (s->authkey) { unsigned char *rsabuf = snewn(s->authkey->bytes, unsigned char); @@ -342,6 +365,9 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl) return; } + if (!s->authkey) + continue; + { ptrlen response = get_data(pktin, 16); ptrlen expected = make_ptrlen( diff --git a/ssh1login.c b/ssh1login.c index 98b7fb5..519838d 100644 --- a/ssh1login.c +++ b/ssh1login.c @@ -12,6 +12,12 @@ #include "sshppl.h" #include "sshcr.h" +typedef struct agent_key { + RSAKey key; + strbuf *comment; + ptrlen blob; /* only used during initial parsing of agent response */ +} agent_key; + struct ssh1_login_state { int crState; @@ -21,7 +27,7 @@ struct ssh1_login_state { char *savedhost; int savedport; - bool try_agent_auth; + bool try_agent_auth, is_trivial_auth; int remote_protoflags; int local_protoflags; @@ -47,11 +53,11 @@ struct ssh1_login_state { void *agent_response_to_free; ptrlen agent_response; BinarySource asrc[1]; /* response from SSH agent */ - int keyi, nkeys; + size_t agent_keys_len; + agent_key *agent_keys; + size_t agent_key_index, agent_key_limit; bool authed; RSAKey key; - mp_int *challenge; - strbuf *agent_comment; int dlgret; Filename *keyfile; RSAKey servkey, hostkey; @@ -72,15 +78,16 @@ static bool ssh1_login_want_user_input(PacketProtocolLayer *ppl); static void ssh1_login_got_user_input(PacketProtocolLayer *ppl); static void ssh1_login_reconfigure(PacketProtocolLayer *ppl, Conf *conf); -static const struct PacketProtocolLayerVtable ssh1_login_vtable = { - ssh1_login_free, - ssh1_login_process_queue, - ssh1_common_get_specials, - ssh1_login_special_cmd, - ssh1_login_want_user_input, - ssh1_login_got_user_input, - ssh1_login_reconfigure, - NULL /* no layer names in SSH-1 */, +static const PacketProtocolLayerVtable ssh1_login_vtable = { + .free = ssh1_login_free, + .process_queue = ssh1_login_process_queue, + .get_specials = ssh1_common_get_specials, + .special_cmd = ssh1_login_special_cmd, + .want_user_input = ssh1_login_want_user_input, + .got_user_input = ssh1_login_got_user_input, + .reconfigure = ssh1_login_reconfigure, + .queued_data_size = ssh_ppl_default_queued_data_size, + .name = NULL, /* no layer names in SSH-1 */ }; static void ssh1_login_agent_query(struct ssh1_login_state *s, strbuf *req); @@ -98,7 +105,8 @@ PacketProtocolLayer *ssh1_login_new( s->savedhost = dupstr(host); s->savedport = port; s->successor_layer = successor_layer; - s->agent_comment = strbuf_new(); + s->is_trivial_auth = true; + return &s->ppl; } @@ -117,9 +125,15 @@ static void ssh1_login_free(PacketProtocolLayer *ppl) if (s->publickey_blob) strbuf_free(s->publickey_blob); sfree(s->publickey_comment); - strbuf_free(s->agent_comment); if (s->cur_prompt) free_prompts(s->cur_prompt); + if (s->agent_keys) { + for (size_t i = 0; i < s->agent_keys_len; i++) { + freersakey(&s->agent_keys[i].key); + strbuf_free(s->agent_keys[i].comment); + } + sfree(s->agent_keys); + } sfree(s->agent_response_to_free); if (s->auth_agent_query) agent_cancel_query(s->auth_agent_query); @@ -232,23 +246,24 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) /* * First format the key into a string. */ - char *fingerprint; char *keystr = rsastr_fmt(&s->hostkey); - fingerprint = rsa_ssh1_fingerprint(&s->hostkey); + char **fingerprints = rsa_ssh1_fake_all_fingerprints(&s->hostkey); /* First check against manually configured host keys. */ - s->dlgret = verify_ssh_manual_host_key(s->conf, fingerprint, NULL); + s->dlgret = verify_ssh_manual_host_key(s->conf, fingerprints, NULL); if (s->dlgret == 0) { /* did not match */ - sfree(fingerprint); + ssh2_free_all_fingerprints(fingerprints); sfree(keystr); ssh_proto_error(s->ppl.ssh, "Host key did not appear in manually " "configured list"); return; } else if (s->dlgret < 0) { /* none configured; use standard handling */ + char *keydisp = ssh1_pubkey_str(&s->hostkey); s->dlgret = seat_verify_ssh_host_key( - s->ppl.seat, s->savedhost, s->savedport, - "rsa", keystr, fingerprint, ssh1_login_dialog_callback, s); - sfree(fingerprint); + s->ppl.seat, s->savedhost, s->savedport, "rsa", keystr, + keydisp, fingerprints, ssh1_login_dialog_callback, s); + sfree(keydisp); + ssh2_free_all_fingerprints(fingerprints); sfree(keystr); #ifdef FUZZING s->dlgret = 1; @@ -261,7 +276,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) return; } } else { - sfree(fingerprint); + ssh2_free_all_fingerprints(fingerprints); sfree(keystr); } } @@ -416,7 +431,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) ssh_user_close(s->ppl.ssh, "No username provided"); return; } - s->username = dupstr(s->cur_prompt->prompts[0]->result); + s->username = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); s->cur_prompt = NULL; } @@ -426,7 +441,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) pq_push(s->ppl.out_pq, pkt); ppl_logevent("Sent username \"%s\"", s->username); - if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) + if (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat)) ppl_printf("Sent username \"%s\"\r\n", s->username); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); @@ -451,13 +466,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) keytype == SSH_KEYTYPE_SSH1_PUBLIC) { const char *error; s->publickey_blob = strbuf_new(); - if (rsa_ssh1_loadpub(s->keyfile, - BinarySink_UPCAST(s->publickey_blob), - &s->publickey_comment, &error)) { + if (rsa1_loadpub_f(s->keyfile, + BinarySink_UPCAST(s->publickey_blob), + &s->publickey_comment, &error)) { s->privatekey_available = (keytype == SSH_KEYTYPE_SSH1); if (!s->privatekey_available) ppl_logevent("Key file contains public key only"); - s->privatekey_encrypted = rsa_ssh1_encrypted(s->keyfile, NULL); + s->privatekey_encrypted = rsa1_encrypted_f(s->keyfile, NULL); } else { ppl_logevent("Unable to load key (%s)", error); ppl_printf("Unable to load key file \"%s\" (%s)\r\n", @@ -503,122 +518,166 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) get_uint32(s->asrc); /* skip length field */ if (get_byte(s->asrc) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) { - s->nkeys = toint(get_uint32(s->asrc)); - if (s->nkeys < 0) { - ppl_logevent("Pageant reported negative key count %d", - s->nkeys); - s->nkeys = 0; - } - ppl_logevent("Pageant has %d SSH-1 keys", s->nkeys); - for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) { - size_t start, end; - start = s->asrc->pos; - get_rsa_ssh1_pub(s->asrc, &s->key, - RSA_SSH1_EXPONENT_FIRST); - end = s->asrc->pos; - s->agent_comment->len = 0; - put_datapl(s->agent_comment, get_string(s->asrc)); + size_t nkeys = get_uint32(s->asrc); + size_t origpos = s->asrc->pos; + + /* + * Check that the agent response is well formed. + */ + for (size_t i = 0; i < nkeys; i++) { + get_rsa_ssh1_pub(s->asrc, NULL, RSA_SSH1_EXPONENT_FIRST); + get_string(s->asrc); /* comment */ if (get_err(s->asrc)) { - ppl_logevent("Pageant key list packet was truncated"); - break; + ppl_logevent("Pageant's response was truncated"); + goto parsed_agent_query; } - if (s->publickey_blob) { - ptrlen keystr = make_ptrlen( - (const char *)s->asrc->data + start, end - start); - - if (keystr.len == s->publickey_blob->len && - !memcmp(keystr.ptr, s->publickey_blob->s, - s->publickey_blob->len)) { - ppl_logevent("Pageant key #%d matches " - "configured key file", s->keyi); - s->tried_publickey = true; - } else - /* Skip non-configured key */ - continue; + } + + /* + * Copy the list of public-key blobs out of the Pageant + * response. + */ + BinarySource_REWIND_TO(s->asrc, origpos); + s->agent_keys_len = nkeys; + s->agent_keys = snewn(s->agent_keys_len, agent_key); + for (size_t i = 0; i < nkeys; i++) { + memset(&s->agent_keys[i].key, 0, + sizeof(s->agent_keys[i].key)); + + const char *blobstart = get_ptr(s->asrc); + get_rsa_ssh1_pub(s->asrc, &s->agent_keys[i].key, + RSA_SSH1_EXPONENT_FIRST); + const char *blobend = get_ptr(s->asrc); + + s->agent_keys[i].comment = strbuf_new(); + put_datapl(s->agent_keys[i].comment, get_string(s->asrc)); + + s->agent_keys[i].blob = make_ptrlen( + blobstart, blobend - blobstart); + } + + ppl_logevent("Pageant has %"SIZEu" SSH-1 keys", nkeys); + + if (s->publickey_blob) { + /* + * If we've been given a specific public key blob, + * filter the list of keys to try from the agent + * down to only that one, or none if it's not + * there. + */ + ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob); + size_t i; + + for (i = 0; i < nkeys; i++) { + if (ptrlen_eq_ptrlen(our_blob, s->agent_keys[i].blob)) + break; } - ppl_logevent("Trying Pageant key #%d", s->keyi); - pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA); - put_mp_ssh1(pkt, s->key.modulus); - pq_push(s->ppl.out_pq, pkt); - crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) - != NULL); - if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) { - ppl_logevent("Key refused"); - continue; + + if (i < nkeys) { + ppl_logevent("Pageant key #%"SIZEu" matches " + "configured key file", i); + s->agent_key_index = i; + s->agent_key_limit = i+1; + } else { + ppl_logevent("Configured key file not in Pageant"); + s->agent_key_index = 0; + s->agent_key_limit = 0; } - ppl_logevent("Received RSA challenge"); - s->challenge = get_mp_ssh1(pktin); + } else { + /* + * Otherwise, try them all. + */ + s->agent_key_index = 0; + s->agent_key_limit = nkeys; + } + } else { + ppl_logevent("Failed to get reply from Pageant"); + } + parsed_agent_query:; + + for (; s->agent_key_index < s->agent_key_limit; + s->agent_key_index++) { + ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index); + pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA); + put_mp_ssh1(pkt, + s->agent_keys[s->agent_key_index].key.modulus); + pq_push(s->ppl.out_pq, pkt); + crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) + != NULL); + if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) { + ppl_logevent("Key refused"); + continue; + } + ppl_logevent("Received RSA challenge"); + + { + mp_int *challenge = get_mp_ssh1(pktin); if (get_err(pktin)) { - mp_free(s->challenge); + mp_free(challenge); ssh_proto_error(s->ppl.ssh, "Server's RSA challenge " "was badly formatted"); return; } - { - strbuf *agentreq; - const char *ret; - - agentreq = strbuf_new_for_agent_query(); - put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE); - put_uint32(agentreq, mp_get_nbits(s->key.modulus)); - put_mp_ssh1(agentreq, s->key.exponent); - put_mp_ssh1(agentreq, s->key.modulus); - put_mp_ssh1(agentreq, s->challenge); - put_data(agentreq, s->session_id, 16); - put_uint32(agentreq, 1); /* response format */ - ssh1_login_agent_query(s, agentreq); - strbuf_free(agentreq); - crMaybeWaitUntilV(!s->auth_agent_query); - - ret = s->agent_response.ptr; - if (ret) { - if (s->agent_response.len >= 5+16 && - ret[4] == SSH1_AGENT_RSA_RESPONSE) { - ppl_logevent("Sending Pageant's response"); - pkt = ssh_bpp_new_pktout( - s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE); - put_data(pkt, ret + 5, 16); - pq_push(s->ppl.out_pq, pkt); - crMaybeWaitUntilV( - (pktin = ssh1_login_pop(s)) - != NULL); - if (pktin->type == SSH1_SMSG_SUCCESS) { - ppl_logevent("Pageant's response " - "accepted"); - if (flags & FLAG_VERBOSE) { - ptrlen comment = ptrlen_from_strbuf( - s->agent_comment); - ppl_printf("Authenticated using RSA " - "key \"%.*s\" from " - "agent\r\n", - PTRLEN_PRINTF(comment)); - } - s->authed = true; - } else - ppl_logevent("Pageant's response not " - "accepted"); - } else { - ppl_logevent("Pageant failed to answer " - "challenge"); - sfree((char *)ret); - } + strbuf *agentreq = strbuf_new_for_agent_query(); + put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE); + + rsa_ssh1_public_blob( + BinarySink_UPCAST(agentreq), + &s->agent_keys[s->agent_key_index].key, + RSA_SSH1_EXPONENT_FIRST); + + put_mp_ssh1(agentreq, challenge); + mp_free(challenge); + + put_data(agentreq, s->session_id, 16); + put_uint32(agentreq, 1); /* response format */ + ssh1_login_agent_query(s, agentreq); + strbuf_free(agentreq); + crMaybeWaitUntilV(!s->auth_agent_query); + } + + { + const unsigned char *ret = s->agent_response.ptr; + if (ret) { + if (s->agent_response.len >= 5+16 && + ret[4] == SSH1_AGENT_RSA_RESPONSE) { + ppl_logevent("Sending Pageant's response"); + pkt = ssh_bpp_new_pktout( + s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE); + put_data(pkt, ret + 5, 16); + pq_push(s->ppl.out_pq, pkt); + s->is_trivial_auth = false; + crMaybeWaitUntilV( + (pktin = ssh1_login_pop(s)) + != NULL); + if (pktin->type == SSH1_SMSG_SUCCESS) { + ppl_logevent("Pageant's response " + "accepted"); + if (seat_verbose(s->ppl.seat)) { + ptrlen comment = ptrlen_from_strbuf( + s->agent_keys[s->agent_key_index]. + comment); + ppl_printf("Authenticated using RSA " + "key \"%.*s\" from " + "agent\r\n", + PTRLEN_PRINTF(comment)); + } + s->authed = true; + } else + ppl_logevent("Pageant's response not " + "accepted"); } else { - ppl_logevent("No reply received from Pageant"); + ppl_logevent("Pageant failed to answer " + "challenge"); + sfree((char *)ret); } + } else { + ppl_logevent("No reply received from Pageant"); } - mp_free(s->key.exponent); - mp_free(s->key.modulus); - mp_free(s->challenge); - if (s->authed) - break; } - sfree(s->agent_response_to_free); - s->agent_response_to_free = NULL; - if (s->publickey_blob && !s->tried_publickey) - ppl_logevent("Configured key file not in Pageant"); - } else { - ppl_logevent("Failed to get reply from Pageant"); + if (s->authed) + break; } if (s->authed) break; @@ -630,7 +689,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) * key file. */ bool got_passphrase; /* need not be kept over crReturn */ - if (flags & FLAG_VERBOSE) + if (seat_verbose(s->ppl.seat)) ppl_printf("Trying public key authentication.\r\n"); ppl_logevent("Trying public key \"%s\"", filename_to_str(s->keyfile)); @@ -644,11 +703,11 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) char *passphrase = NULL; /* only written after crReturn */ const char *error; if (!s->privatekey_encrypted) { - if (flags & FLAG_VERBOSE) + if (seat_verbose(s->ppl.seat)) ppl_printf("No passphrase required.\r\n"); passphrase = NULL; } else { - s->cur_prompt = new_prompts(s->ppl.seat); + s->cur_prompt = new_prompts(); s->cur_prompt->to_server = false; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH key passphrase"); @@ -676,15 +735,14 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) "User aborted at passphrase prompt"); return; } - passphrase = dupstr(s->cur_prompt->prompts[0]->result); + passphrase = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); s->cur_prompt = NULL; } /* * Try decrypting key with passphrase. */ - retd = rsa_ssh1_loadkey( - s->keyfile, &s->key, passphrase, &error); + retd = rsa1_load_f(s->keyfile, &s->key, passphrase, &error); if (passphrase) { smemclr(passphrase, strlen(passphrase)); sfree(passphrase); @@ -702,7 +760,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) got_passphrase = false; /* and try again */ } else { - unreachable("unexpected return from rsa_ssh1_loadkey()"); + unreachable("unexpected return from rsa1_load_f()"); } } @@ -759,6 +817,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE); put_data(pkt, buffer, 16); pq_push(s->ppl.out_pq, pkt); + s->is_trivial_auth = false; mp_free(challenge); mp_free(response); @@ -767,7 +826,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { - if (flags & FLAG_VERBOSE) + if (seat_verbose(s->ppl.seat)) ppl_printf("Failed to authenticate with" " our public key.\r\n"); continue; /* go and try something else */ @@ -787,7 +846,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) /* * Otherwise, try various forms of password-like authentication. */ - s->cur_prompt = new_prompts(s->ppl.seat); + s->cur_prompt = new_prompts(); if (conf_get_bool(s->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) && @@ -800,7 +859,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { ppl_logevent("TIS authentication declined"); - if (flags & FLAG_INTERACTIVE) + if (seat_interactive(s->ppl.seat)) ppl_printf("TIS authentication refused.\r\n"); s->tis_auth_refused = true; continue; @@ -988,8 +1047,10 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) * we can use the primary defence. */ int bottom, top, pwlen, i; + const char *pw = prompt_get_result_ref( + s->cur_prompt->prompts[0]); - pwlen = strlen(s->cur_prompt->prompts[0]->result); + pwlen = strlen(pw); if (pwlen < 16) { bottom = 0; /* zero length passwords are OK! :-) */ top = 15; @@ -1003,7 +1064,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) for (i = bottom; i <= top; i++) { if (i == pwlen) { pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_stringz(pkt, s->cur_prompt->prompts[0]->result); + put_stringz(pkt, pw); pq_push(s->ppl.out_pq, pkt); } else { strbuf *random_data = strbuf_new_nm(); @@ -1026,7 +1087,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) ppl_logevent("Sending length-padded password"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_asciz(padded_pw, s->cur_prompt->prompts[0]->result); + put_asciz(padded_pw, prompt_get_result_ref( + s->cur_prompt->prompts[0])); size_t pad = 63 & -padded_pw->len; random_read(strbuf_append(padded_pw, pad), pad); put_stringsb(pkt, padded_pw); @@ -1038,20 +1100,22 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) */ ppl_logevent("Sending unpadded password"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_stringz(pkt, s->cur_prompt->prompts[0]->result); + put_stringz(pkt, prompt_get_result_ref( + s->cur_prompt->prompts[0])); pq_push(s->ppl.out_pq, pkt); } } else { pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); - put_stringz(pkt, s->cur_prompt->prompts[0]->result); + put_stringz(pkt, prompt_get_result_ref(s->cur_prompt->prompts[0])); pq_push(s->ppl.out_pq, pkt); } + s->is_trivial_auth = false; ppl_logevent("Sent password"); free_prompts(s->cur_prompt); s->cur_prompt = NULL; crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { - if (flags & FLAG_VERBOSE) + if (seat_verbose(s->ppl.seat)) ppl_printf("Access denied\r\n"); ppl_logevent("Authentication refused"); } else if (pktin->type != SSH1_SMSG_SUCCESS) { @@ -1062,6 +1126,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl) } } + if (conf_get_bool(s->conf, CONF_ssh_no_trivial_userauth) && + s->is_trivial_auth) { + ssh_proto_error(s->ppl.ssh, "Authentication was trivial! " + "Abandoning session as specified in configuration."); + return; + } + ppl_logevent("Authentication successful"); if (conf_get_bool(s->conf, CONF_compression)) { diff --git a/ssh2bpp-bare.c b/ssh2bpp-bare.c index 8dc3b11..90f196e 100644 --- a/ssh2bpp-bare.c +++ b/ssh2bpp-bare.c @@ -25,13 +25,15 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp); static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh2_bare_bpp_new_pktout(int type); -static const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = { - ssh2_bare_bpp_free, - ssh2_bare_bpp_handle_input, - ssh2_bare_bpp_handle_output, - ssh2_bare_bpp_new_pktout, - ssh2_bpp_queue_disconnect, /* in sshcommon.c */ - 0x4000, /* packet size limit, per protocol spec in sshshare.c comment */ +static const BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = { + .free = ssh2_bare_bpp_free, + .handle_input = ssh2_bare_bpp_handle_input, + .handle_output = ssh2_bare_bpp_handle_output, + .new_pktout = ssh2_bare_bpp_new_pktout, + .queue_disconnect = ssh2_bpp_queue_disconnect, /* in sshcommon.c */ + + /* packet size limit, per protocol spec in sshshare.c comment */ + .packet_size_limit = 0x4000, }; BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx) @@ -108,6 +110,18 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp) s->packetlen--; BinarySource_INIT(s->pktin, s->data, s->packetlen); + if (s->pktin->type == SSH2_MSG_EXT_INFO) { + /* + * Mild layer violation: EXT_INFO is not permitted in the + * bare ssh-connection protocol. Faulting it here means + * that ssh2_common_filter_queue doesn't receive it in the + * first place unless it's legal to have sent it. + */ + ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO " + "in bare connection protocol"); + return; + } + /* * Log incoming packet, possibly omitting sensitive fields. */ @@ -129,6 +143,7 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp) continue; } + s->pktin->qnode.formal_size = get_avail(s->pktin); pq_push(&s->bpp.in_pq, s->pktin); s->pktin = NULL; } diff --git a/ssh2bpp.c b/ssh2bpp.c index 79b97b3..09b23e5 100644 --- a/ssh2bpp.c +++ b/ssh2bpp.c @@ -37,6 +37,9 @@ struct ssh2_bpp_state { bool is_server; bool pending_newkeys; bool pending_compression, seen_userauth_success; + bool enforce_next_packet_is_userauth_success; + unsigned nnewkeys; + int prev_type; BinaryPacketProtocol bpp; }; @@ -46,13 +49,13 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp); static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh2_bpp_new_pktout(int type); -static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = { - ssh2_bpp_free, - ssh2_bpp_handle_input, - ssh2_bpp_handle_output, - ssh2_bpp_new_pktout, - ssh2_bpp_queue_disconnect, /* in sshcommon.c */ - 0xFFFFFFFF, /* no special packet size limit for this bpp */ +static const BinaryPacketProtocolVtable ssh2_bpp_vtable = { + .free = ssh2_bpp_free, + .handle_input = ssh2_bpp_handle_input, + .handle_output = ssh2_bpp_handle_output, + .new_pktout = ssh2_bpp_new_pktout, + .queue_disconnect = ssh2_bpp_queue_disconnect, /* in sshcommon.c */ + .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ }; BinaryPacketProtocol *ssh2_bpp_new( @@ -589,13 +592,30 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp) continue; } + s->pktin->qnode.formal_size = get_avail(s->pktin); pq_push(&s->bpp.in_pq, s->pktin); { int type = s->pktin->type; + int prev_type = s->prev_type; + s->prev_type = type; s->pktin = NULL; + if (s->enforce_next_packet_is_userauth_success) { + /* See EXT_INFO handler below */ + if (type != SSH2_MSG_USERAUTH_SUCCESS) { + ssh_proto_error(s->bpp.ssh, + "Remote side sent SSH2_MSG_EXT_INFO " + "not either preceded by NEWKEYS or " + "followed by USERAUTH_SUCCESS"); + return; + } + s->enforce_next_packet_is_userauth_success = false; + } + if (type == SSH2_MSG_NEWKEYS) { + if (s->nnewkeys < 2) + s->nnewkeys++; /* * Mild layer violation: in this situation we must * suspend processing of the input byte stream until @@ -626,6 +646,44 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp) s->seen_userauth_success = true; } + if (type == SSH2_MSG_EXT_INFO) { + /* + * And another: enforce that an incoming EXT_INFO is + * either the message immediately after the initial + * NEWKEYS, or (if we're the client) the one + * immediately before USERAUTH_SUCCESS. + */ + if (prev_type == SSH2_MSG_NEWKEYS && s->nnewkeys == 1) { + /* OK - this is right after the first NEWKEYS. */ + } else if (s->is_server) { + /* We're the server, so they're the client. + * Clients may not send EXT_INFO at _any_ other + * time. */ + ssh_proto_error(s->bpp.ssh, + "Remote side sent SSH2_MSG_EXT_INFO " + "that was not immediately after the " + "initial NEWKEYS"); + return; + } else if (s->nnewkeys > 0 && s->seen_userauth_success) { + /* We're the client, so they're the server. In + * that case they may also send EXT_INFO + * immediately before USERAUTH_SUCCESS. Error out + * immediately if this can't _possibly_ be that + * moment (because we haven't even seen NEWKEYS + * yet, or because we've already seen + * USERAUTH_SUCCESS). */ + ssh_proto_error(s->bpp.ssh, + "Remote side sent SSH2_MSG_EXT_INFO " + "after USERAUTH_SUCCESS"); + return; + } else { + /* This _could_ be OK, provided the next packet is + * USERAUTH_SUCCESS. Set a flag to remember to + * fault it if not. */ + s->enforce_next_packet_is_userauth_success = true; + } + } + if (s->pending_compression && userauth_range(type)) { /* * Receiving any userauth message at all indicates diff --git a/ssh2connection-client.c b/ssh2connection-client.c index d9e909d..0b13efe 100644 --- a/ssh2connection-client.c +++ b/ssh2connection-client.c @@ -89,13 +89,32 @@ static ChanopenResult chan_open_forwarded_tcpip( static ChanopenResult chan_open_auth_agent( struct ssh2_connection_state *s, SshChannel *sc) { - if (!s->agent_fwd_enabled) { + if (!ssh_agent_forwarding_permitted(&s->cl)) { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, ("Agent forwarding is not enabled")); } - CHANOPEN_RETURN_SUCCESS(agentf_new(sc)); + /* + * If possible, make a stream-oriented connection to the agent and + * set up an ordinary port-forwarding type channel over it. + */ + Plug *plug; + Channel *ch = portfwd_raw_new(&s->cl, &plug, true); + Socket *skt = agent_connect(plug); + + if (!sk_socket_error(skt)) { + portfwd_raw_setup(ch, skt, sc); + CHANOPEN_RETURN_SUCCESS(ch); + } else { + portfwd_raw_free(ch); + /* + * Otherwise, fall back to the old-fashioned system of parsing the + * forwarded data stream ourselves for message boundaries, and + * passing each individual message to the one-off agent_query(). + */ + CHANOPEN_RETURN_SUCCESS(agentf_new(sc)); + } } ChanopenResult ssh2_connection_parse_channel_open( @@ -315,7 +334,11 @@ SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan) static void ssh2_channel_response( struct ssh2_channel *c, PktIn *pkt, void *ctx) { - chan_request_response(c->chan, pkt->type == SSH2_MSG_CHANNEL_SUCCESS); + /* If pkt==NULL (because this handler has been called in response + * to CHANNEL_CLOSE arriving while the request was still + * outstanding), we treat that the same as CHANNEL_FAILURE. */ + chan_request_response(c->chan, + pkt && pkt->type == SSH2_MSG_CHANNEL_SUCCESS); } void ssh2channel_start_shell(SshChannel *sc, bool want_reply) @@ -477,5 +500,6 @@ void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h) bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s) { - return !seat_set_trust_status(s->ppl.seat, false); + bool success = seat_set_trust_status(s->ppl.seat, false); + return (!success && !ssh_is_bare(s->ppl.ssh)); } diff --git a/ssh2connection.c b/ssh2connection.c index 28054d4..ecb421e 100644 --- a/ssh2connection.c +++ b/ssh2connection.c @@ -22,15 +22,16 @@ static bool ssh2_connection_want_user_input(PacketProtocolLayer *ppl); static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl); static void ssh2_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf); -static const struct PacketProtocolLayerVtable ssh2_connection_vtable = { - ssh2_connection_free, - ssh2_connection_process_queue, - ssh2_connection_get_specials, - ssh2_connection_special_cmd, - ssh2_connection_want_user_input, - ssh2_connection_got_user_input, - ssh2_connection_reconfigure, - "ssh-connection", +static const PacketProtocolLayerVtable ssh2_connection_vtable = { + .free = ssh2_connection_free, + .process_queue = ssh2_connection_process_queue, + .get_specials = ssh2_connection_get_specials, + .special_cmd = ssh2_connection_special_cmd, + .want_user_input = ssh2_connection_want_user_input, + .got_user_input = ssh2_connection_got_user_input, + .reconfigure = ssh2_connection_reconfigure, + .queued_data_size = ssh_ppl_default_queued_data_size, + .name = "ssh-connection", }; static SshChannel *ssh2_lportfwd_open( @@ -61,34 +62,32 @@ static void ssh2_throttle_all_channels(ConnectionLayer *cl, bool throttled); static bool ssh2_ldisc_option(ConnectionLayer *cl, int option); static void ssh2_set_ldisc_option(ConnectionLayer *cl, int option, bool value); static void ssh2_enable_x_fwd(ConnectionLayer *cl); -static void ssh2_enable_agent_fwd(ConnectionLayer *cl); static void ssh2_set_wants_user_input(ConnectionLayer *cl, bool wanted); -static const struct ConnectionLayerVtable ssh2_connlayer_vtable = { - ssh2_rportfwd_alloc, - ssh2_rportfwd_remove, - ssh2_lportfwd_open, - ssh2_session_open, - ssh2_serverside_x11_open, - ssh2_serverside_agent_open, - ssh2_add_x11_display, - ssh2_add_sharing_x11_display, - ssh2_remove_sharing_x11_display, - ssh2_send_packet_from_downstream, - ssh2_alloc_sharing_channel, - ssh2_delete_sharing_channel, - ssh2_sharing_queue_global_request, - ssh2_sharing_no_more_downstreams, - ssh2_agent_forwarding_permitted, - ssh2_terminal_size, - ssh2_stdout_unthrottle, - ssh2_stdin_backlog, - ssh2_throttle_all_channels, - ssh2_ldisc_option, - ssh2_set_ldisc_option, - ssh2_enable_x_fwd, - ssh2_enable_agent_fwd, - ssh2_set_wants_user_input, +static const ConnectionLayerVtable ssh2_connlayer_vtable = { + .rportfwd_alloc = ssh2_rportfwd_alloc, + .rportfwd_remove = ssh2_rportfwd_remove, + .lportfwd_open = ssh2_lportfwd_open, + .session_open = ssh2_session_open, + .serverside_x11_open = ssh2_serverside_x11_open, + .serverside_agent_open = ssh2_serverside_agent_open, + .add_x11_display = ssh2_add_x11_display, + .add_sharing_x11_display = ssh2_add_sharing_x11_display, + .remove_sharing_x11_display = ssh2_remove_sharing_x11_display, + .send_packet_from_downstream = ssh2_send_packet_from_downstream, + .alloc_sharing_channel = ssh2_alloc_sharing_channel, + .delete_sharing_channel = ssh2_delete_sharing_channel, + .sharing_queue_global_request = ssh2_sharing_queue_global_request, + .sharing_no_more_downstreams = ssh2_sharing_no_more_downstreams, + .agent_forwarding_permitted = ssh2_agent_forwarding_permitted, + .terminal_size = ssh2_terminal_size, + .stdout_unthrottle = ssh2_stdout_unthrottle, + .stdin_backlog = ssh2_stdin_backlog, + .throttle_all_channels = ssh2_throttle_all_channels, + .ldisc_option = ssh2_ldisc_option, + .set_ldisc_option = ssh2_set_ldisc_option, + .enable_x_fwd = ssh2_enable_x_fwd, + .set_wants_user_input = ssh2_set_wants_user_input, }; static char *ssh2_channel_open_failure_error_text(PktIn *pktin) @@ -131,28 +130,28 @@ static void ssh2channel_x11_sharing_handover( int protomajor, int protominor, const void *initial_data, int initial_len); static void ssh2channel_hint_channel_is_simple(SshChannel *c); -static const struct SshChannelVtable ssh2channel_vtable = { - ssh2channel_write, - ssh2channel_write_eof, - ssh2channel_initiate_close, - ssh2channel_unthrottle, - ssh2channel_get_conf, - ssh2channel_window_override_removed, - ssh2channel_x11_sharing_handover, - ssh2channel_send_exit_status, - ssh2channel_send_exit_signal, - ssh2channel_send_exit_signal_numeric, - ssh2channel_request_x11_forwarding, - ssh2channel_request_agent_forwarding, - ssh2channel_request_pty, - ssh2channel_send_env_var, - ssh2channel_start_shell, - ssh2channel_start_command, - ssh2channel_start_subsystem, - ssh2channel_send_serial_break, - ssh2channel_send_signal, - ssh2channel_send_terminal_size_change, - ssh2channel_hint_channel_is_simple, +static const SshChannelVtable ssh2channel_vtable = { + .write = ssh2channel_write, + .write_eof = ssh2channel_write_eof, + .initiate_close = ssh2channel_initiate_close, + .unthrottle = ssh2channel_unthrottle, + .get_conf = ssh2channel_get_conf, + .window_override_removed = ssh2channel_window_override_removed, + .x11_sharing_handover = ssh2channel_x11_sharing_handover, + .send_exit_status = ssh2channel_send_exit_status, + .send_exit_signal = ssh2channel_send_exit_signal, + .send_exit_signal_numeric = ssh2channel_send_exit_signal_numeric, + .request_x11_forwarding = ssh2channel_request_x11_forwarding, + .request_agent_forwarding = ssh2channel_request_agent_forwarding, + .request_pty = ssh2channel_request_pty, + .send_env_var = ssh2channel_send_env_var, + .start_shell = ssh2channel_start_shell, + .start_command = ssh2channel_start_command, + .start_subsystem = ssh2channel_start_subsystem, + .send_serial_break = ssh2channel_send_serial_break, + .send_signal = ssh2channel_send_signal, + .send_terminal_size_change = ssh2channel_send_terminal_size_change, + .hint_channel_is_simple = ssh2channel_hint_channel_is_simple, }; static void ssh2_channel_check_close(struct ssh2_channel *c); @@ -515,19 +514,18 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s) ssh2_channel_try_eof(c); /* in case we had a pending EOF */ break; - case SSH2_MSG_CHANNEL_OPEN_FAILURE: + case SSH2_MSG_CHANNEL_OPEN_FAILURE: { assert(c->halfopen); - { - char *err = ssh2_channel_open_failure_error_text(pktin); - chan_open_failed(c->chan, err); - sfree(err); - } + char *err = ssh2_channel_open_failure_error_text(pktin); + chan_open_failed(c->chan, err); + sfree(err); del234(s->channels, c); ssh2_channel_free(c); break; + } case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: @@ -750,7 +748,8 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s) "Received %s for channel %d with no outstanding " "channel request", ssh2_pkt_type(s->ppl.bpp->pls->kctx, - s->ppl.bpp->pls->actx, pktin->type)); + s->ppl.bpp->pls->actx, pktin->type), + c->localid); return true; } ocr->handler(c, pktin, ocr->ctx); @@ -1021,6 +1020,7 @@ static void ssh2_connection_process_queue(PacketProtocolLayer *ppl) s->mainchan = mainchan_new( &s->ppl, &s->cl, s->conf, s->term_width, s->term_height, s->ssh_is_simple, &s->mainchan_sc); + s->started = true; /* * Transfer data! @@ -1249,6 +1249,15 @@ static void ssh2_check_termination(struct ssh2_connection_state *s) if (s->persistent) return; /* persistent mode: never proactively terminate */ + if (!s->started) { + /* At startup, we don't have any channels open because we + * haven't got round to opening the main one yet. In that + * situation, we don't want to terminate, even if a sharing + * connection opens and closes and causes a call to this + * function. */ + return; + } + if (count234(s->channels) == 0 && !(s->connshare && share_ndownstreams(s->connshare) > 0)) { /* @@ -1693,14 +1702,6 @@ static void ssh2_enable_x_fwd(ConnectionLayer *cl) s->X11_fwd_enabled = true; } -static void ssh2_enable_agent_fwd(ConnectionLayer *cl) -{ - struct ssh2_connection_state *s = - container_of(cl, struct ssh2_connection_state, cl); - - s->agent_fwd_enabled = true; -} - static void ssh2_set_wants_user_input(ConnectionLayer *cl, bool wanted) { struct ssh2_connection_state *s = diff --git a/ssh2connection.h b/ssh2connection.h index 69975d7..d3bb240 100644 --- a/ssh2connection.h +++ b/ssh2connection.h @@ -7,8 +7,6 @@ struct outstanding_global_request; struct ssh2_connection_state { int crState; - Ssh *ssh; - ssh_sharing_state *connshare; char *peer_verstring; @@ -21,6 +19,7 @@ struct ssh2_connection_state { bool ssh_is_simple; bool persistent; + bool started; Conf *conf; @@ -31,7 +30,6 @@ struct ssh2_connection_state { tree234 *x11authtree; bool got_pty; - bool agent_fwd_enabled; tree234 *rportfwds; PortFwdManager *portfwdmgr; diff --git a/ssh2kex-client.c b/ssh2kex-client.c index 64ec033..1dd960c 100644 --- a/ssh2kex-client.c +++ b/ssh2kex-client.c @@ -13,6 +13,12 @@ #include "ssh2transport.h" #include "mpint.h" +/* + * Another copy of the symbol defined in mpunsafe.c. See the comment + * there. + */ +const int deliberate_symbol_clash = 12345; + void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ @@ -250,7 +256,6 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) s->init_token_sent = false; s->complete_rcvd = false; s->hkey = NULL; - s->fingerprint = NULL; s->keystr = NULL; /* @@ -715,11 +720,12 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) * host key, store it. */ if (s->hkey) { - s->fingerprint = ssh2_fingerprint(s->hkey); + char *fingerprint = ssh2_fingerprint( + s->hkey, SSH_FPTYPE_DEFAULT); ppl_logevent("GSS kex provided fallback host key:"); - ppl_logevent("%s", s->fingerprint); - sfree(s->fingerprint); - s->fingerprint = NULL; + ppl_logevent("%s", fingerprint); + sfree(fingerprint); + ssh_transient_hostkey_cache_add(s->thc, s->hkey); } else if (!ssh_transient_hostkey_cache_non_empty(s->thc)) { /* @@ -773,25 +779,25 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) * triggered on purpose to populate the transient cache. */ assert(s->hkey); /* only KEXTYPE_GSS lets this be null */ - s->fingerprint = ssh2_fingerprint(s->hkey); + char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT); if (s->need_gss_transient_hostkey) { ppl_logevent("Post-GSS rekey provided fallback host key:"); - ppl_logevent("%s", s->fingerprint); + ppl_logevent("%s", fingerprint); ssh_transient_hostkey_cache_add(s->thc, s->hkey); s->need_gss_transient_hostkey = false; } else if (!ssh_transient_hostkey_cache_verify(s->thc, s->hkey)) { ppl_logevent("Non-GSS rekey after initial GSS kex " "used host key:"); - ppl_logevent("%s", s->fingerprint); + ppl_logevent("%s", fingerprint); + sfree(fingerprint); ssh_sw_abort(s->ppl.ssh, "Server's host key did not match any " "used in previous GSS kex"); *aborted = true; return; } - sfree(s->fingerprint); - s->fingerprint = NULL; + sfree(fingerprint); } } else #endif /* NO_GSSAPI */ @@ -837,22 +843,29 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) * Authenticate remote host: verify host key. (We've already * checked the signature of the exchange hash.) */ - s->fingerprint = ssh2_fingerprint(s->hkey); + char **fingerprints = ssh2_all_fingerprints(s->hkey); + FingerprintType fptype_default = + ssh2_pick_default_fingerprint(fingerprints); ppl_logevent("Host key fingerprint is:"); - ppl_logevent("%s", s->fingerprint); + ppl_logevent("%s", fingerprints[fptype_default]); /* First check against manually configured host keys. */ s->dlgret = verify_ssh_manual_host_key( - s->conf, s->fingerprint, s->hkey); + s->conf, fingerprints, s->hkey); if (s->dlgret == 0) { /* did not match */ + ssh2_free_all_fingerprints(fingerprints); ssh_sw_abort(s->ppl.ssh, "Host key did not appear in manually " "configured list"); *aborted = true; return; } else if (s->dlgret < 0) { /* none configured; use standard handling */ + ssh2_userkey uk = { .key = s->hkey, .comment = NULL }; + char *keydisp = ssh2_pubkey_openssh_str(&uk); s->dlgret = seat_verify_ssh_host_key( s->ppl.seat, s->savedhost, s->savedport, - ssh_key_cache_id(s->hkey), s->keystr, s->fingerprint, - ssh2_transport_dialog_callback, s); + ssh_key_cache_id(s->hkey), s->keystr, keydisp, + fingerprints, ssh2_transport_dialog_callback, s); + sfree(keydisp); + ssh2_free_all_fingerprints(fingerprints); #ifdef FUZZING s->dlgret = 1; #endif @@ -864,8 +877,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) return; } } - sfree(s->fingerprint); - s->fingerprint = NULL; + /* * Save this host key, to check against the one presented in * subsequent rekeys. @@ -876,11 +888,11 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) assert(s->hkey); assert(ssh_key_alg(s->hkey) == s->cross_certifying); - s->fingerprint = ssh2_fingerprint(s->hkey); + char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT); ppl_logevent("Storing additional host key for this host:"); - ppl_logevent("%s", s->fingerprint); - sfree(s->fingerprint); - s->fingerprint = NULL; + ppl_logevent("%s", fingerprint); + sfree(fingerprint); + store_host_key(s->savedhost, s->savedport, ssh_key_cache_id(s->hkey), s->keystr); /* diff --git a/ssh2kex-server.c b/ssh2kex-server.c index 4037715..1fbb588 100644 --- a/ssh2kex-server.c +++ b/ssh2kex-server.c @@ -10,6 +10,7 @@ #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" +#include "sshkeygen.h" #include "storage.h" #include "ssh2transport.h" #include "mpint.h" @@ -31,14 +32,10 @@ static strbuf *finalise_and_sign_exhash(struct ssh2_transport_state *s) sb = strbuf_new(); ssh_key_sign( s->hkey, make_ptrlen(s->exchange_hash, s->kex_alg->hash->hlen), - 0, BinarySink_UPCAST(sb)); + s->hkflags, BinarySink_UPCAST(sb)); return sb; } -static void no_progress(void *param, int action, int phase, int iprogress) -{ -} - void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ @@ -57,7 +54,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) assert(s->hkey); } - s->hostkeyblob->len = 0; + strbuf_clear(s->hostkeyblob); ssh_key_public_blob(s->hkey, BinarySink_UPCAST(s->hostkeyblob)); s->hostkeydata = ptrlen_from_strbuf(s->hostkeyblob); @@ -100,7 +97,13 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) * group! It's good enough for testing a client against, * but not for serious use. */ - s->p = primegen(s->pbits, 2, 2, NULL, 1, no_progress, NULL, 1); + PrimeGenerationContext *pgc = primegen_new_context( + &primegen_probabilistic); + ProgressReceiver null_progress; + null_progress.vt = &null_progress_vt; + s->p = primegen_generate(pgc, pcs_new(s->pbits), &null_progress); + primegen_free_context(pgc); + s->g = mp_from_integer(2); s->dh_ctx = dh_setup_gex(s->p, s->g); s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT; @@ -262,7 +265,15 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) ppl_logevent("Generating a %d-bit RSA key", extra->minklen); s->rsa_kex_key = snew(RSAKey); - rsa_generate(s->rsa_kex_key, extra->minklen, no_progress, NULL); + + PrimeGenerationContext *pgc = primegen_new_context( + &primegen_probabilistic); + ProgressReceiver null_progress; + null_progress.vt = &null_progress_vt; + rsa_generate(s->rsa_kex_key, extra->minklen, false, + pgc, &null_progress); + primegen_free_context(pgc); + s->rsa_kex_key->comment = NULL; s->rsa_kex_key_needs_freeing = true; } diff --git a/ssh2transport.c b/ssh2transport.c index e577c5a..4e1b443 100644 --- a/ssh2transport.c +++ b/ssh2transport.c @@ -52,11 +52,16 @@ static bool ssh_decomp_none_block(ssh_decompressor *handle, { return false; } -const static ssh_compression_alg ssh_comp_none = { - "none", NULL, - ssh_comp_none_init, ssh_comp_none_cleanup, ssh_comp_none_block, - ssh_decomp_none_init, ssh_decomp_none_cleanup, ssh_decomp_none_block, - NULL +static const ssh_compression_alg ssh_comp_none = { + .name = "none", + .delayed_name = NULL, + .compress_new = ssh_comp_none_init, + .compress_free = ssh_comp_none_cleanup, + .compress = ssh_comp_none_block, + .decompress_new = ssh_decomp_none_init, + .decompress_free = ssh_decomp_none_cleanup, + .decompress = ssh_decomp_none_block, + .text_name = NULL, }; const static ssh_compression_alg *const compressions[] = { &ssh_zlib, &ssh_comp_none @@ -71,20 +76,22 @@ static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl, static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl); static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl); static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf); +static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl); static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s); static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def); static void ssh2_transport_higher_layer_packet_callback(void *context); -static const struct PacketProtocolLayerVtable ssh2_transport_vtable = { - ssh2_transport_free, - ssh2_transport_process_queue, - ssh2_transport_get_specials, - ssh2_transport_special_cmd, - ssh2_transport_want_user_input, - ssh2_transport_got_user_input, - ssh2_transport_reconfigure, - NULL, /* no protocol name for this layer */ +static const PacketProtocolLayerVtable ssh2_transport_vtable = { + .free = ssh2_transport_free, + .process_queue = ssh2_transport_process_queue, + .get_specials = ssh2_transport_get_specials, + .special_cmd = ssh2_transport_special_cmd, + .want_user_input = ssh2_transport_want_user_input, + .got_user_input = ssh2_transport_got_user_input, + .reconfigure = ssh2_transport_reconfigure, + .queued_data_size = ssh2_transport_queued_data_size, + .name = NULL, /* no protocol name for this layer */ }; #ifndef NO_GSSAPI @@ -209,7 +216,6 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl) sfree(s->keystr); sfree(s->hostkey_str); strbuf_free(s->hostkeyblob); - sfree(s->fingerprint); if (s->hkey && !s->hostkeys) { ssh_key_free(s->hkey); s->hkey = NULL; @@ -265,7 +271,7 @@ static void ssh2_mkkey( */ keylen_padded = ((keylen + hlen - 1) / hlen) * hlen; - out->len = 0; + strbuf_clear(out); key = strbuf_append(out, keylen_padded); /* First hlen bytes. */ @@ -275,25 +281,25 @@ static void ssh2_mkkey( put_data(h, H, hlen); put_byte(h, chr); put_data(h, s->session_id, s->session_id_len); - ssh_hash_final(h, key); + ssh_hash_digest(h, key); /* Subsequent blocks of hlen bytes. */ if (keylen_padded > hlen) { int offset; - h = ssh_hash_new(s->kex_alg->hash); + ssh_hash_reset(h); if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY)) put_mp_ssh2(h, K); put_data(h, H, hlen); for (offset = hlen; offset < keylen_padded; offset += hlen) { put_data(h, key + offset - hlen, hlen); - ssh_hash *h2 = ssh_hash_copy(h); - ssh_hash_final(h2, key + offset); + ssh_hash_digest_nondestructive(h, key + offset); } - ssh_hash_free(h); } + + ssh_hash_free(h); } /* @@ -371,6 +377,64 @@ bool ssh2_common_filter_queue(PacketProtocolLayer *ppl) pq_pop(ppl->in_pq); break; + case SSH2_MSG_EXT_INFO: { + /* + * The BPP enforces that these turn up only at legal + * points in the protocol. In particular, it will not pass + * an EXT_INFO on to us if it arrives before encryption is + * enabled (which is when a MITM could inject one + * maliciously). + * + * However, one of the criteria for legality is that a + * server is permitted to send this message immediately + * _before_ USERAUTH_SUCCESS. So we may receive this + * message not yet knowing whether it's legal to have sent + * it - we won't know until the BPP processes the next + * packet. + * + * But that should be OK, because firstly, an + * out-of-sequence EXT_INFO that's still within the + * encrypted session is only a _protocol_ violation, not + * an attack; secondly, any data we set in response to + * such an illegal EXT_INFO won't have a chance to affect + * the session before the BPP aborts it anyway. + */ + uint32_t nexts = get_uint32(pktin); + for (uint32_t i = 0; i < nexts && !get_err(pktin); i++) { + ptrlen extname = get_string(pktin); + ptrlen extvalue = get_string(pktin); + if (ptrlen_eq_string(extname, "server-sig-algs")) { + /* + * Server has sent a list of signature algorithms + * it will potentially accept for user + * authentication keys. Check in particular + * whether the RFC 8332 improved versions of + * ssh-rsa are in the list, and set flags in the + * BPP if so. + * + * TODO: another thing we _could_ do here is to + * record a full list of the algorithm identifiers + * we've seen, whether we understand them + * ourselves or not. Then we could use that as a + * pre-filter during userauth, to skip keys in the + * SSH agent if we already know the server can't + * possibly accept them. (Even if the key + * algorithm is one that the agent and the server + * both understand but we do not.) + */ + ptrlen algname; + while (get_commasep_word(&extvalue, &algname)) { + if (ptrlen_eq_string(algname, "rsa-sha2-256")) + ppl->bpp->ext_info_rsa_sha256_ok = true; + if (ptrlen_eq_string(algname, "rsa-sha2-512")) + ppl->bpp->ext_info_rsa_sha512_ok = true; + } + } + } + pq_pop(ppl->in_pq); + break; + } + default: return false; } @@ -562,16 +626,34 @@ static void ssh2_write_kexinit_lists( * host keys we actually have. */ for (i = 0; i < our_nhostkeys; i++) { + const ssh_keyalg *keyalg = ssh_key_alg(our_hostkeys[i]); + alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], - ssh_key_alg(our_hostkeys[i])->ssh_id); - alg->u.hk.hostkey = ssh_key_alg(our_hostkeys[i]); + keyalg->ssh_id); + alg->u.hk.hostkey = keyalg; + alg->u.hk.hkflags = 0; alg->u.hk.warn = false; + + if (keyalg == &ssh_rsa) { + alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], + "rsa-sha2-256"); + alg->u.hk.hostkey = keyalg; + alg->u.hk.hkflags = SSH_AGENT_RSA_SHA2_256; + alg->u.hk.warn = false; + + alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], + "rsa-sha2-512"); + alg->u.hk.hostkey = keyalg; + alg->u.hk.hkflags = SSH_AGENT_RSA_SHA2_512; + alg->u.hk.warn = false; + } } } else if (first_time) { /* - * In the first key exchange, we list all the algorithms - * we're prepared to cope with, but prefer those algorithms - * for which we have a host key for this host. + * In the first key exchange, we list all the algorithms we're + * prepared to cope with, but (if configured to) we prefer + * those algorithms for which we have a host key for this + * host. * * If the host key algorithm is below the warning * threshold, we warn even if we did already have a key @@ -587,7 +669,8 @@ static void ssh2_write_kexinit_lists( for (j = 0; j < lenof(ssh2_hostkey_algs); j++) { if (ssh2_hostkey_algs[j].id != preferred_hk[i]) continue; - if (have_ssh_host_key(hk_host, hk_port, + if (conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys) && + have_ssh_host_key(hk_host, hk_port, ssh2_hostkey_algs[j].alg->cache_id)) { alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], ssh2_hostkey_algs[j].alg->ssh_id); @@ -753,6 +836,12 @@ static void ssh2_write_kexinit_lists( add_to_commasep(list, kexlists[i][j].name); } } + if (i == KEXLIST_KEX && first_time) { + if (our_hostkeys) /* we're the server */ + add_to_commasep(list, "ext-info-s"); + else /* we're the client */ + add_to_commasep(list, "ext-info-c"); + } put_stringsb(pktout, list); } /* List client->server languages. Empty list. */ @@ -768,7 +857,8 @@ static bool ssh2_scan_kexinits( transport_direction *cs, transport_direction *sc, bool *warn_kex, bool *warn_hk, bool *warn_cscipher, bool *warn_sccipher, Ssh *ssh, bool *ignore_guess_cs_packet, bool *ignore_guess_sc_packet, - int *n_server_hostkeys, int server_hostkeys[MAXKEXLIST]) + int *n_server_hostkeys, int server_hostkeys[MAXKEXLIST], unsigned *hkflags, + bool *can_send_ext_info) { BinarySource client[1], server[1]; int i; @@ -929,6 +1019,7 @@ static bool ssh2_scan_kexinits( continue; *hostkey_alg = alg->u.hk.hostkey; + *hkflags = alg->u.hk.hkflags; *warn_hk = alg->u.hk.warn; break; @@ -967,6 +1058,20 @@ static bool ssh2_scan_kexinits( } } + /* + * Check whether the other side advertised support for EXT_INFO. + */ + { + ptrlen extinfo_advert = + (server_hostkeys ? PTRLEN_LITERAL("ext-info-c") : + PTRLEN_LITERAL("ext-info-s")); + ptrlen list = (server_hostkeys ? clists[KEXLIST_KEX] : + slists[KEXLIST_KEX]); + for (ptrlen word; get_commasep_word(&list, &word) ;) + if (ptrlen_eq_ptrlen(word, extinfo_advert)) + *can_send_ext_info = true; + } + if (server_hostkeys) { /* * Finally, make an auxiliary pass over the server's host key @@ -1083,7 +1188,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) * Construct our KEXINIT packet, in a strbuf so we can refer to it * later. */ - s->client_kexinit->len = 0; + strbuf_clear(s->client_kexinit); put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT); random_read(strbuf_append(s->outgoing_kexinit, 16), 16); ssh2_write_kexinit_lists( @@ -1121,7 +1226,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) s->ppl.bpp->pls->actx, pktin->type)); return; } - s->incoming_kexinit->len = 0; + strbuf_clear(s->incoming_kexinit); put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT); put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin)); @@ -1137,7 +1242,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) ptrlen_from_strbuf(s->server_kexinit), s->kexlists, &s->kex_alg, &s->hostkey_alg, s->cstrans, s->sctrans, &s->warn_kex, &s->warn_hk, &s->warn_cscipher, - &s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks)) + &s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks, + &s->hkflags, &s->can_send_ext_info)) return; /* false means a fatal error function was called */ /* @@ -1200,9 +1306,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) if (better) { if (betteralgs) { char *old_ba = betteralgs; - betteralgs = dupcat(betteralgs, ",", - hktype->alg->ssh_id, - (const char *)NULL); + betteralgs = dupcat(betteralgs, ",", hktype->alg->ssh_id); sfree(old_ba); } else { betteralgs = dupstr(hktype->alg->ssh_id); @@ -1338,6 +1442,42 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) strbuf_free(mac_key); } + /* + * If that was our first key exchange, this is the moment to send + * our EXT_INFO, if we're sending one. + */ + if (!s->post_newkeys_ext_info) { + s->post_newkeys_ext_info = true; /* never do this again */ + if (s->can_send_ext_info) { + strbuf *extinfo = strbuf_new(); + uint32_t n_exts = 0; + + if (s->ssc) { + /* Server->client EXT_INFO lists our supported user + * key algorithms. */ + n_exts++; + put_stringz(extinfo, "server-sig-algs"); + strbuf *list = strbuf_new(); + for (size_t i = 0; i < n_keyalgs; i++) + add_to_commasep(list, all_keyalgs[i]->ssh_id); + put_stringsb(extinfo, list); + } else { + /* Client->server EXT_INFO is currently not sent, but here's + * where we should put things in it if we ever want to. */ + } + + /* Only send EXT_INFO if it's non-empty */ + if (n_exts) { + pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_EXT_INFO); + put_uint32(pktout, n_exts); + put_datapl(pktout, ptrlen_from_strbuf(extinfo)); + pq_push(s->ppl.out_pq, pktout); + } + + strbuf_free(extinfo); + } + } + /* * Now our end of the key exchange is complete, we can send all * our queued higher-layer packets. Transfer the whole of the next @@ -2030,3 +2170,12 @@ static int ssh2_transport_confirm_weak_crypto_primitive( return seat_confirm_weak_crypto_primitive( s->ppl.seat, type, name, ssh2_transport_dialog_callback, s); } + +static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl) +{ + struct ssh2_transport_state *s = + container_of(ppl, struct ssh2_transport_state, ppl); + + return (ssh_ppl_default_queued_data_size(ppl) + + ssh_ppl_queued_data_size(s->higher_layer)); +} diff --git a/ssh2transport.h b/ssh2transport.h index 8dc2330..349c06f 100644 --- a/ssh2transport.h +++ b/ssh2transport.h @@ -28,6 +28,7 @@ struct kexinit_algorithm { } kex; struct { const ssh_keyalg *hostkey; + unsigned hkflags; bool warn; } hk; struct { @@ -47,10 +48,13 @@ struct kexinit_algorithm { #define HOSTKEY_ALGORITHMS(X) \ X(HK_ED25519, ssh_ecdsa_ed25519) \ + X(HK_ED448, ssh_ecdsa_ed448) \ X(HK_ECDSA, ssh_ecdsa_nistp256) \ X(HK_ECDSA, ssh_ecdsa_nistp384) \ X(HK_ECDSA, ssh_ecdsa_nistp521) \ X(HK_DSA, ssh_dss) \ + X(HK_RSA, ssh_rsa_sha512) \ + X(HK_RSA, ssh_rsa_sha256) \ X(HK_RSA, ssh_rsa) \ /* end of list */ #define COUNT_HOSTKEY_ALGORITHM(type, alg) +1 @@ -170,8 +174,9 @@ struct ssh2_transport_state { transport_direction in, out, *cstrans, *sctrans; ptrlen hostkeydata, sigdata; strbuf *hostkeyblob; - char *keystr, *fingerprint; + char *keystr; ssh_key *hkey; /* actual host key */ + unsigned hkflags; /* signing flags, used in server */ RSAKey *rsa_kex_key; /* for RSA kex */ bool rsa_kex_key_needs_freeing; ecdh_key *ecdh_key; /* for ECDH kex */ @@ -180,6 +185,7 @@ struct ssh2_transport_state { bool need_gss_transient_hostkey; bool warned_about_no_gss_transient_hostkey; bool got_session_id; + bool can_send_ext_info, post_newkeys_ext_info; int dlgret; bool guessok; bool ignorepkt; diff --git a/ssh2userauth-server.c b/ssh2userauth-server.c index 3775395..cacddee 100644 --- a/ssh2userauth-server.c +++ b/ssh2userauth-server.c @@ -38,15 +38,12 @@ struct ssh2_userauth_server_state { static void ssh2_userauth_server_free(PacketProtocolLayer *); static void ssh2_userauth_server_process_queue(PacketProtocolLayer *); -static const struct PacketProtocolLayerVtable ssh2_userauth_server_vtable = { - ssh2_userauth_server_free, - ssh2_userauth_server_process_queue, - NULL /* get_specials */, - NULL /* special_cmd */, - NULL /* want_user_input */, - NULL /* got_user_input */, - NULL /* reconfigure */, - "ssh-userauth", +static const PacketProtocolLayerVtable ssh2_userauth_server_vtable = { + .free = ssh2_userauth_server_free, + .process_queue = ssh2_userauth_server_process_queue, + .queued_data_size = ssh_ppl_default_queued_data_size, + .name = "ssh-userauth", + /* other methods are NULL */ }; static void free_auth_kbdint(AuthKbdInt *aki) @@ -202,7 +199,7 @@ static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl) goto failure; } } else if (ptrlen_eq_string(s->method, "publickey")) { - bool has_signature, success; + bool has_signature, success, send_pk_ok, key_really_ok; ptrlen algorithm, blob, signature; const ssh_keyalg *keyalg; ssh_key *key; @@ -216,18 +213,14 @@ static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl) algorithm = get_string(pktin); blob = get_string(pktin); - if (!auth_publickey(s->authpolicy, s->username, blob)) - goto failure; - - keyalg = find_pubkey_alg_len(algorithm); - if (!keyalg) - goto failure; - key = ssh_key_new_pub(keyalg, blob); - if (!key) - goto failure; + key_really_ok = auth_publickey(s->authpolicy, s->username, blob); + send_pk_ok = key_really_ok || + s->ssc->stunt_pretend_to_accept_any_pubkey; if (!has_signature) { - ssh_key_free(key); + if (!send_pk_ok) + goto failure; + pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_PK_OK); put_stringpl(pktout, algorithm); @@ -236,6 +229,16 @@ static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl) continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */ } + if (!key_really_ok) + goto failure; + + keyalg = find_pubkey_alg_len(algorithm); + if (!keyalg) + goto failure; + key = ssh_key_new_pub(keyalg, blob); + if (!key) + goto failure; + sigdata = strbuf_new(); ssh2_userauth_server_add_session_id(s, sigdata); put_byte(sigdata, SSH2_MSG_USERAUTH_REQUEST); diff --git a/ssh2userauth.c b/ssh2userauth.c index 833fde8..451d5ab 100644 --- a/ssh2userauth.c +++ b/ssh2userauth.c @@ -18,12 +18,17 @@ #define BANNER_LIMIT 131072 +typedef struct agent_key { + strbuf *blob, *comment; + ptrlen algorithm; +} agent_key; + struct ssh2_userauth_state { int crState; PacketProtocolLayer *transport_layer, *successor_layer; Filename *keyfile; - bool show_banner, tryagent, change_username; + bool show_banner, tryagent, notrivialauth, change_username; char *hostname, *fullhostname; char *default_username; bool try_ki_auth, try_gssapi_auth, try_gssapi_kex_auth, gssapi_fwd; @@ -69,12 +74,15 @@ struct ssh2_userauth_state { void *agent_response_to_free; ptrlen agent_response; BinarySource asrc[1]; /* for reading SSH agent response */ - size_t pkblob_pos_in_agent; - int keyi, nkeys; - ptrlen pk, alg, comment; + size_t agent_keys_len; + agent_key *agent_keys; + size_t agent_key_index, agent_key_limit; + ptrlen agent_keyalg; + unsigned signflags; int len; PktOut *pktout; bool want_user_input; + bool is_trivial_auth; agent_pending_query *auth_agent_query; bufchain banner; @@ -112,21 +120,22 @@ static PktOut *ssh2_userauth_gss_packet( static void ssh2_userauth_antispoof_msg( struct ssh2_userauth_state *s, const char *msg); -static const struct PacketProtocolLayerVtable ssh2_userauth_vtable = { - ssh2_userauth_free, - ssh2_userauth_process_queue, - ssh2_userauth_get_specials, - ssh2_userauth_special_cmd, - ssh2_userauth_want_user_input, - ssh2_userauth_got_user_input, - ssh2_userauth_reconfigure, - "ssh-userauth", +static const PacketProtocolLayerVtable ssh2_userauth_vtable = { + .free = ssh2_userauth_free, + .process_queue = ssh2_userauth_process_queue, + .get_specials = ssh2_userauth_get_specials, + .special_cmd = ssh2_userauth_special_cmd, + .want_user_input = ssh2_userauth_want_user_input, + .got_user_input = ssh2_userauth_got_user_input, + .reconfigure = ssh2_userauth_reconfigure, + .queued_data_size = ssh_ppl_default_queued_data_size, + .name = "ssh-userauth", }; PacketProtocolLayer *ssh2_userauth_new( PacketProtocolLayer *successor_layer, const char *hostname, const char *fullhostname, - Filename *keyfile, bool show_banner, bool tryagent, + Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth, const char *default_username, bool change_username, bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth, bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss) @@ -141,6 +150,7 @@ PacketProtocolLayer *ssh2_userauth_new( s->keyfile = filename_copy(keyfile); s->show_banner = show_banner; s->tryagent = tryagent; + s->notrivialauth = notrivialauth; s->default_username = dupstr(default_username); s->change_username = change_username; s->try_ki_auth = try_ki_auth; @@ -149,6 +159,7 @@ PacketProtocolLayer *ssh2_userauth_new( s->gssapi_fwd = gssapi_fwd; s->shgss = shgss; s->last_methods_string = strbuf_new(); + s->is_trivial_auth = true; bufchain_init(&s->banner); bufchain_sink_init(&s->banner_bs, &s->banner); @@ -172,6 +183,13 @@ static void ssh2_userauth_free(PacketProtocolLayer *ppl) if (s->successor_layer) ssh_ppl_free(s->successor_layer); + if (s->agent_keys) { + for (size_t i = 0; i < s->agent_keys_len; i++) { + strbuf_free(s->agent_keys[i].blob); + strbuf_free(s->agent_keys[i].comment); + } + sfree(s->agent_keys); + } sfree(s->agent_response_to_free); if (s->auth_agent_query) agent_cancel_query(s->auth_agent_query); @@ -269,15 +287,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { const char *error; s->publickey_blob = strbuf_new(); - if (ssh2_userkey_loadpub(s->keyfile, - &s->publickey_algorithm, - BinarySink_UPCAST(s->publickey_blob), - &s->publickey_comment, &error)) { + if (ppk_loadpub_f(s->keyfile, &s->publickey_algorithm, + BinarySink_UPCAST(s->publickey_blob), + &s->publickey_comment, &error)) { s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2); if (!s->privatekey_available) ppl_logevent("Key file contains public key only"); - s->privatekey_encrypted = - ssh2_userkey_encrypted(s->keyfile, NULL); + s->privatekey_encrypted = ppk_encrypted_f(s->keyfile, NULL); } else { ppl_logevent("Unable to load key (%s)", error); ppl_printf("Unable to load key file \"%s\" (%s)\r\n", @@ -299,8 +315,6 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Find out about any keys Pageant has (but if there's a public * key configured, filter out all others). */ - s->nkeys = 0; - s->pkblob_pos_in_agent = 0; if (s->tryagent && agent_exists()) { ppl_logevent("Pageant is running. Requesting keys."); @@ -316,48 +330,75 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) get_uint32(s->asrc); /* skip length field */ if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) { - int keyi; + size_t nkeys = get_uint32(s->asrc); + size_t origpos = s->asrc->pos; - s->nkeys = toint(get_uint32(s->asrc)); + /* + * Check that the agent response is well formed. + */ + for (size_t i = 0; i < nkeys; i++) { + get_string(s->asrc); /* blob */ + get_string(s->asrc); /* comment */ + if (get_err(s->asrc)) { + ppl_logevent("Pageant's response was truncated"); + goto done_agent_query; + } + } /* - * Vet the Pageant response to ensure that the key count - * and blob lengths make sense. + * Copy the list of public-key blobs out of the Pageant + * response. */ - if (s->nkeys < 0) { - ppl_logevent("Pageant response contained a negative" - " key count %d", s->nkeys); - s->nkeys = 0; - goto done_agent_query; - } else { - ppl_logevent("Pageant has %d SSH-2 keys", s->nkeys); - - /* See if configured key is in agent. */ - for (keyi = 0; keyi < s->nkeys; keyi++) { - size_t pos = s->asrc->pos; - ptrlen blob = get_string(s->asrc); - get_string(s->asrc); /* skip comment */ - if (get_err(s->asrc)) { - ppl_logevent("Pageant response was truncated"); - s->nkeys = 0; - goto done_agent_query; - } + BinarySource_REWIND_TO(s->asrc, origpos); + s->agent_keys_len = nkeys; + s->agent_keys = snewn(s->agent_keys_len, agent_key); + for (size_t i = 0; i < nkeys; i++) { + s->agent_keys[i].blob = strbuf_new(); + put_datapl(s->agent_keys[i].blob, get_string(s->asrc)); + s->agent_keys[i].comment = strbuf_new(); + put_datapl(s->agent_keys[i].comment, get_string(s->asrc)); + + /* Also, extract the algorithm string from the start + * of the public-key blob. */ + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf( + s->agent_keys[i].blob)); + s->agent_keys[i].algorithm = get_string(src); + } + + ppl_logevent("Pageant has %"SIZEu" SSH-2 keys", nkeys); - if (s->publickey_blob && - blob.len == s->publickey_blob->len && - !memcmp(blob.ptr, s->publickey_blob->s, - s->publickey_blob->len)) { - ppl_logevent("Pageant key #%d matches " - "configured key file", keyi); - s->keyi = keyi; - s->pkblob_pos_in_agent = pos; + if (s->publickey_blob) { + /* + * If we've been given a specific public key blob, + * filter the list of keys to try from the agent down + * to only that one, or none if it's not there. + */ + ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob); + size_t i; + + for (i = 0; i < nkeys; i++) { + if (ptrlen_eq_ptrlen(our_blob, ptrlen_from_strbuf( + s->agent_keys[i].blob))) break; - } } - if (s->publickey_blob && !s->pkblob_pos_in_agent) { + + if (i < nkeys) { + ppl_logevent("Pageant key #%"SIZEu" matches " + "configured key file", i); + s->agent_key_index = i; + s->agent_key_limit = i+1; + } else { ppl_logevent("Configured key file not in Pageant"); - s->nkeys = 0; + s->agent_key_index = 0; + s->agent_key_limit = 0; } + } else { + /* + * Otherwise, try them all. + */ + s->agent_key_index = 0; + s->agent_key_limit = nkeys; } } else { ppl_logevent("Failed to get reply from Pageant"); @@ -432,10 +473,10 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } sfree(s->locally_allocated_username); /* for change_username */ s->username = s->locally_allocated_username = - dupstr(s->cur_prompt->prompts[0]->result); + prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); } else { - if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) + if (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat)) ppl_printf("Using username \"%s\".\r\n", s->username); } s->got_username = true; @@ -456,17 +497,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) s->tried_pubkey_config = false; s->kbd_inter_refused = false; - - /* Reset agent request state. */ s->done_agent = false; - if (s->agent_response.ptr) { - if (s->pkblob_pos_in_agent) { - s->asrc->pos = s->pkblob_pos_in_agent; - } else { - s->asrc->pos = 9; /* skip length + type + key count */ - s->keyi = 0; - } - } while (1) { /* @@ -498,7 +529,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * anti-spoofing header lines. */ if (bufchain_size(&s->banner) && - (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE))) { + (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat))) { if (s->banner_scc) { ssh2_userauth_antispoof_msg( s, "Pre-authentication banner message from server:"); @@ -509,9 +540,9 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) while (bufchain_size(&s->banner) > 0) { ptrlen data = bufchain_prefix(&s->banner); seat_stderr_pl(s->ppl.seat, data); - bufchain_consume(&s->banner, data.len); mid_line = (((const char *)data.ptr)[data.len-1] != '\n'); + bufchain_consume(&s->banner, data.len); } bufchain_clear(&s->banner); @@ -620,7 +651,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) /* * Save the methods string for use in error messages. */ - s->last_methods_string->len = 0; + strbuf_clear(s->last_methods_string); put_datapl(s->last_methods_string, methods); /* @@ -687,24 +718,29 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } else #endif /* NO_GSSAPI */ - if (s->can_pubkey && !s->done_agent && s->nkeys) { + if (s->can_pubkey && !s->done_agent && + s->agent_key_index < s->agent_key_limit) { /* * Attempt public-key authentication using a key from Pageant. */ + s->agent_keyalg = s->agent_keys[s->agent_key_index].algorithm; + s->signflags = 0; + if (ptrlen_eq_string(s->agent_keyalg, "ssh-rsa")) { + /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family, + * if the server has announced support for them. */ + if (s->ppl.bpp->ext_info_rsa_sha512_ok) { + s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-512"); + s->signflags = SSH_AGENT_RSA_SHA2_512; + } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) { + s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-256"); + s->signflags = SSH_AGENT_RSA_SHA2_256; + } + } s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY; - ppl_logevent("Trying Pageant key #%d", s->keyi); - - /* Unpack key from agent response */ - s->pk = get_string(s->asrc); - s->comment = get_string(s->asrc); - { - BinarySource src[1]; - BinarySource_BARE_INIT_PL(src, s->pk); - s->alg = get_string(src); - } + ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index); /* See if server will accept it */ s->pktout = ssh_bpp_new_pktout( @@ -714,8 +750,9 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, false); /* no signature included */ - put_stringpl(s->pktout, s->alg); - put_stringpl(s->pktout, s->pk); + put_stringpl(s->pktout, s->agent_keyalg); + put_stringpl(s->pktout, ptrlen_from_strbuf( + s->agent_keys[s->agent_key_index].blob)); pq_push(s->ppl.out_pq, s->pktout); s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET; @@ -728,11 +765,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } else { strbuf *agentreq, *sigdata; + ptrlen comment = ptrlen_from_strbuf( + s->agent_keys[s->agent_key_index].comment); - if (flags & FLAG_VERBOSE) + if (seat_verbose(s->ppl.seat)) ppl_printf("Authenticating with public key " "\"%.*s\" from agent\r\n", - PTRLEN_PRINTF(s->comment)); + PTRLEN_PRINTF(comment)); /* * Server is willing to accept the key. @@ -745,21 +784,23 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, true); /* signature included */ - put_stringpl(s->pktout, s->alg); - put_stringpl(s->pktout, s->pk); + put_stringpl(s->pktout, s->agent_keyalg); + put_stringpl(s->pktout, ptrlen_from_strbuf( + s->agent_keys[s->agent_key_index].blob)); /* Ask agent for signature. */ agentreq = strbuf_new_for_agent_query(); put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST); - put_stringpl(agentreq, s->pk); + put_stringpl(agentreq, ptrlen_from_strbuf( + s->agent_keys[s->agent_key_index].blob)); /* Now the data to be signed... */ sigdata = strbuf_new(); ssh2_userauth_add_session_id(s, sigdata); put_data(sigdata, s->pktout->data + 5, s->pktout->length - 5); put_stringsb(agentreq, sigdata); - /* And finally the (zero) flags word. */ - put_uint32(agentreq, 0); + /* And finally the flags word. */ + put_uint32(agentreq, s->signflags); ssh2_userauth_agent_query(s, agentreq); strbuf_free(agentreq); crWaitUntilV(!s->auth_agent_query); @@ -773,28 +814,34 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE && (sigblob = get_string(src), !get_err(src))) { ppl_logevent("Sending Pageant's response"); - ssh2_userauth_add_sigblob(s, s->pktout, - s->pk, sigblob); + ssh2_userauth_add_sigblob( + s, s->pktout, + ptrlen_from_strbuf( + s->agent_keys[s->agent_key_index].blob), + sigblob); pq_push(s->ppl.out_pq, s->pktout); s->type = AUTH_TYPE_PUBLICKEY; + s->is_trivial_auth = false; } else { ppl_logevent("Pageant refused signing request"); ppl_printf("Pageant failed to " "provide a signature\r\n"); s->suppress_wait_for_response_packet = true; + ssh_free_pktout(s->pktout); } + } else { + ppl_logevent("Pageant failed to respond to " + "signing request"); + ppl_printf("Pageant failed to " + "respond to signing request\r\n"); + s->suppress_wait_for_response_packet = true; + ssh_free_pktout(s->pktout); } } /* Do we have any keys left to try? */ - if (s->pkblob_pos_in_agent) { + if (++s->agent_key_index >= s->agent_key_limit) s->done_agent = true; - s->tried_pubkey_config = true; - } else { - s->keyi++; - if (s->keyi >= s->nkeys) - s->done_agent = true; - } } else if (s->can_pubkey && s->publickey_blob && s->privatekey_available && !s->tried_pubkey_config) { @@ -809,8 +856,25 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) /* * Try the public key supplied in the configuration. * - * First, offer the public blob to see if the server is - * willing to accept it. + * First, try to upgrade its algorithm. + */ + if (!strcmp(s->publickey_algorithm, "ssh-rsa")) { + /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family, + * if the server has announced support for them. */ + if (s->ppl.bpp->ext_info_rsa_sha512_ok) { + sfree(s->publickey_algorithm); + s->publickey_algorithm = dupstr("rsa-sha2-512"); + s->signflags = SSH_AGENT_RSA_SHA2_512; + } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) { + sfree(s->publickey_algorithm); + s->publickey_algorithm = dupstr("rsa-sha2-256"); + s->signflags = SSH_AGENT_RSA_SHA2_256; + } + } + + /* + * Offer the public blob to see if the server is willing to + * accept it. */ s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); @@ -838,7 +902,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Actually attempt a serious authentication using * the key. */ - if (flags & FLAG_VERBOSE) + if (seat_verbose(s->ppl.seat)) ppl_printf("Authenticating with public key \"%s\"\r\n", s->publickey_comment); @@ -884,7 +948,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) return; } passphrase = - dupstr(s->cur_prompt->prompts[0]->result); + prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); } else { passphrase = NULL; /* no passphrase needed */ @@ -893,7 +957,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) /* * Try decrypting the key. */ - key = ssh2_load_userkey(s->keyfile, passphrase, &error); + key = ppk_load_f(s->keyfile, passphrase, &error); if (passphrase) { /* burn the evidence */ smemclr(passphrase, strlen(passphrase)); @@ -945,7 +1009,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, true); /* signature follows */ - put_stringz(s->pktout, ssh_key_ssh_id(key->key)); + put_stringz(s->pktout, s->publickey_algorithm); pkblob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(pkblob)); put_string(s->pktout, pkblob->s, pkblob->len); @@ -963,8 +1027,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) put_data(sigdata, s->pktout->data + 5, s->pktout->length - 5); sigblob = strbuf_new(); - ssh_key_sign(key->key, ptrlen_from_strbuf(sigdata), 0, - BinarySink_UPCAST(sigblob)); + ssh_key_sign(key->key, ptrlen_from_strbuf(sigdata), + s->signflags, BinarySink_UPCAST(sigblob)); strbuf_free(sigdata); ssh2_userauth_add_sigblob( s, s->pktout, ptrlen_from_strbuf(pkblob), @@ -978,6 +1042,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) ssh_key_free(key->key); sfree(key->comment); sfree(key); + s->is_trivial_auth = false; } #ifndef NO_GSSAPI @@ -1109,6 +1174,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * no longer says CONTINUE_NEEDED */ if (s->gss_sndtok.length != 0) { + s->is_trivial_auth = false; s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); @@ -1228,7 +1294,6 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Loop while the server continues to send INFO_REQUESTs. */ while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) { - ptrlen name, inst; strbuf *sb; @@ -1248,6 +1313,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) */ s->num_prompts = get_uint32(pktin); for (uint32_t i = 0; i < s->num_prompts; i++) { + s->is_trivial_auth = false; ptrlen prompt = get_string(pktin); bool echo = get_bool(pktin); @@ -1334,6 +1400,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } if (sb->len) s->cur_prompt->instruction = strbuf_to_str(sb); + else + strbuf_free(sb); /* * Our prompts_t is fully constructed now. Get the @@ -1374,8 +1442,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE); put_uint32(s->pktout, s->num_prompts); for (uint32_t i = 0; i < s->num_prompts; i++) { - put_stringz(s->pktout, - s->cur_prompt->prompts[i]->result); + put_stringz(s->pktout, prompt_get_result_ref( + s->cur_prompt->prompts[i])); } s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); @@ -1410,7 +1478,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) pq_push_front(s->ppl.in_pq, pktin); } else if (s->can_passwd) { - + s->is_trivial_auth = false; /* * Plain old password authentication. */ @@ -1457,7 +1525,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * Squirrel away the password. (We may need it later if * asked to change it.) */ - s->password = dupstr(s->cur_prompt->prompts[0]->result); + s->password = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); /* @@ -1583,20 +1651,20 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) * (A side effect is that the user doesn't have to * re-enter it if they louse up the new password.) */ - if (s->cur_prompt->prompts[0]->result[0]) { + if (s->cur_prompt->prompts[0]->result->s[0]) { smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); - s->password = - dupstr(s->cur_prompt->prompts[0]->result); + s->password = prompt_get_result( + s->cur_prompt->prompts[0]); } /* * Check the two new passwords match. */ - got_new = (strcmp(s->cur_prompt->prompts[1]->result, - s->cur_prompt->prompts[2]->result) - == 0); + got_new = !strcmp( + prompt_get_result_ref(s->cur_prompt->prompts[1]), + prompt_get_result_ref(s->cur_prompt->prompts[2])); if (!got_new) /* They don't. Silly user. */ ppl_printf("Passwords do not match\r\n"); @@ -1614,8 +1682,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) put_stringz(s->pktout, "password"); put_bool(s->pktout, true); put_stringz(s->pktout, s->password); - put_stringz(s->pktout, - s->cur_prompt->prompts[1]->result); + put_stringz(s->pktout, prompt_get_result_ref( + s->cur_prompt->prompts[1])); free_prompts(s->cur_prompt); s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); @@ -1669,6 +1737,12 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) } userauth_success: + if (s->notrivialauth && s->is_trivial_auth) { + ssh_proto_error(s->ppl.ssh, "Authentication was trivial! " + "Abandoning session as specified in configuration."); + return; + } + /* * We've just received USERAUTH_SUCCESS, and we haven't sent * any packets since. Signal the transport layer to consider @@ -1777,7 +1851,7 @@ static void ssh2_userauth_add_sigblob( /* debug("modulus length is %d\n", len); */ /* debug("signature length is %d\n", siglen); */ - if (mod_mp.len != sig_mp.len) { + if (mod_mp.len > sig_mp.len) { strbuf *substr = strbuf_new(); put_data(substr, sigblob.ptr, sig_prefix_len); put_uint32(substr, mod_mp.len); diff --git a/sshaes.c b/sshaes.c index a04ea58..6671879 100644 --- a/sshaes.c +++ b/sshaes.c @@ -85,7 +85,7 @@ * vtables: one for the pure software implementation, one using * hardware acceleration (if available), and a top-level one which is * never actually instantiated, and only contains a new() method whose - * job is to decide whihc of the other two to return an actual + * job is to decide which of the other two to return an actual * instance of. */ @@ -106,30 +106,54 @@ struct aes_extra { }; #define VTABLES_INNER(cid, pid, bits, name, encsuffix, \ - decsuffix, setiv, flags) \ + decsuffix, setivsuffix, flagsval) \ static void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len); \ static void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len); \ const ssh_cipheralg ssh_##cid##_sw = { \ - aes_sw_new, aes_sw_free, aes_sw_##setiv, aes_sw_setkey, \ - cid##_sw##encsuffix, cid##_sw##decsuffix, NULL, NULL, \ - pid, 16, bits, bits/8, flags, name " (unaccelerated)", \ - NULL, NULL }; \ + .new = aes_sw_new, \ + .free = aes_sw_free, \ + .setiv = aes_sw_##setivsuffix, \ + .setkey = aes_sw_setkey, \ + .encrypt = cid##_sw##encsuffix, \ + .decrypt = cid##_sw##decsuffix, \ + .ssh2_id = pid, \ + .blksize = 16, \ + .real_keybits = bits, \ + .padded_keybytes = bits/8, \ + .flags = flagsval, \ + .text_name = name " (unaccelerated)", \ + }; \ \ static void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len); \ static void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len); \ const ssh_cipheralg ssh_##cid##_hw = { \ - aes_hw_new, aes_hw_free, aes_hw_##setiv, aes_hw_setkey, \ - cid##_hw##encsuffix, cid##_hw##decsuffix, NULL, NULL, \ - pid, 16, bits, bits/8, flags, name HW_NAME_SUFFIX, \ - NULL, NULL }; \ + .new = aes_hw_new, \ + .free = aes_hw_free, \ + .setiv = aes_hw_##setivsuffix, \ + .setkey = aes_hw_setkey, \ + .encrypt = cid##_hw##encsuffix, \ + .decrypt = cid##_hw##decsuffix, \ + .ssh2_id = pid, \ + .blksize = 16, \ + .real_keybits = bits, \ + .padded_keybytes = bits/8, \ + .flags = flagsval, \ + .text_name = name HW_NAME_SUFFIX, \ + }; \ \ - const struct aes_extra extra_##cid = { \ + static const struct aes_extra extra_##cid = { \ &ssh_##cid##_sw, &ssh_##cid##_hw }; \ \ const ssh_cipheralg ssh_##cid = { \ - aes_select, NULL, NULL, NULL, NULL, NULL, NULL, NULL, \ - pid, 16, bits, bits/8, flags, name " (dummy selector vtable)", \ - NULL, &extra_##cid }; \ + .new = aes_select, \ + .ssh2_id = pid, \ + .blksize = 16, \ + .real_keybits = bits, \ + .padded_keybytes = bits/8, \ + .flags = flagsval, \ + .text_name = name " (dummy selector vtable)", \ + .extra = &extra_##cid \ + }; \ #define VTABLES(keylen) \ VTABLES_INNER(aes ## keylen ## _cbc, "aes" #keylen "-cbc", \ @@ -144,9 +168,14 @@ VTABLES(256) static const ssh_cipheralg ssh_rijndael_lysator = { /* Same as aes256_cbc, but with a different protocol ID */ - aes_select, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - "rijndael-cbc@lysator.liu.se", 16, 256, 256/8, 0, - "AES-256 CBC (dummy selector vtable)", NULL, &extra_aes256_cbc + .new = aes_select, + .ssh2_id = "rijndael-cbc@lysator.liu.se", + .blksize = 16, + .real_keybits = 256, + .padded_keybytes = 256/8, + .flags = 0, + .text_name = "AES-256 CBC (dummy selector vtable)", + .extra = &extra_aes256_cbc, }; static const ssh_cipheralg *const aes_list[] = { @@ -1551,6 +1580,7 @@ NI_ENC_DEC(256) */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 +#define __ARM_FEATURE_AES 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ diff --git a/ssharcf.c b/ssharcf.c index dd163d4..5382147 100644 --- a/ssharcf.c +++ b/ssharcf.c @@ -104,19 +104,33 @@ static void arcfour_ssh2_block(ssh_cipher *cipher, void *blk, int len) } const ssh_cipheralg ssh_arcfour128_ssh2 = { - arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey, - arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL, - "arcfour128", - 1, 128, 16, 0, "Arcfour-128", - NULL + .new = arcfour_new, + .free = arcfour_free, + .setiv = arcfour_ssh2_setiv, + .setkey = arcfour_ssh2_setkey, + .encrypt = arcfour_ssh2_block, + .decrypt = arcfour_ssh2_block, + .ssh2_id = "arcfour128", + .blksize = 1, + .real_keybits = 128, + .padded_keybytes = 16, + .flags = 0, + .text_name = "Arcfour-128", }; const ssh_cipheralg ssh_arcfour256_ssh2 = { - arcfour_new, arcfour_free, arcfour_ssh2_setiv, arcfour_ssh2_setkey, - arcfour_ssh2_block, arcfour_ssh2_block, NULL, NULL, - "arcfour256", - 1, 256, 32, 0, "Arcfour-256", - NULL + .new = arcfour_new, + .free = arcfour_free, + .setiv = arcfour_ssh2_setiv, + .setkey = arcfour_ssh2_setkey, + .encrypt = arcfour_ssh2_block, + .decrypt = arcfour_ssh2_block, + .ssh2_id = "arcfour256", + .blksize = 1, + .real_keybits = 256, + .padded_keybytes = 32, + .flags = 0, + .text_name = "Arcfour-256", }; static const ssh_cipheralg *const arcfour_list[] = { diff --git a/sshargon2.c b/sshargon2.c new file mode 100644 index 0000000..25385d7 --- /dev/null +++ b/sshargon2.c @@ -0,0 +1,565 @@ +/* + * Implementation of the Argon2 password hash function. + * + * My sources for the algorithm description and test vectors (the latter in + * test/cryptsuite.py) were the reference implementation on Github, and also + * the Internet-Draft description: + * + * https://github.com/P-H-C/phc-winner-argon2 + * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-argon2-13 + */ + +#include + +#include "putty.h" +#include "ssh.h" +#include "marshal.h" + +/* ---------------------------------------------------------------------- + * Argon2 uses data marshalling rules similar to SSH but with 32-bit integers + * stored little-endian. Start with some local BinarySink routines for storing + * a uint32 and a string in that fashion. + */ + +static void BinarySink_put_uint32_le(BinarySink *bs, unsigned long val) +{ + unsigned char data[4]; + PUT_32BIT_LSB_FIRST(data, val); + bs->write(bs, data, sizeof(data)); +} + +static void BinarySink_put_stringpl_le(BinarySink *bs, ptrlen pl) +{ + /* Check that the string length fits in a uint32, without doing a + * potentially implementation-defined shift of more than 31 bits */ + assert((pl.len >> 31) < 2); + + BinarySink_put_uint32_le(bs, pl.len); + bs->write(bs, pl.ptr, pl.len); +} + +#define put_uint32_le(bs, val) \ + BinarySink_put_uint32_le(BinarySink_UPCAST(bs), val) +#define put_stringpl_le(bs, val) \ + BinarySink_put_stringpl_le(BinarySink_UPCAST(bs), val) + +/* ---------------------------------------------------------------------- + * Argon2 defines a hash-function family that's an extension of BLAKE2b to + * generate longer output digests, by repeatedly outputting half of a BLAKE2 + * hash output and then re-hashing the whole thing until there are 64 or fewer + * bytes left to output. The spec calls this H' (a variant of the original + * hash it calls H, which is the unmodified BLAKE2b). + */ + +static ssh_hash *hprime_new(unsigned length) +{ + ssh_hash *h = blake2b_new_general(length > 64 ? 64 : length); + put_uint32_le(h, length); + return h; +} + +static void hprime_final(ssh_hash *h, unsigned length, void *vout) +{ + uint8_t *out = (uint8_t *)vout; + + while (length > 64) { + uint8_t hashbuf[64]; + ssh_hash_final(h, hashbuf); + + memcpy(out, hashbuf, 32); + out += 32; + length -= 32; + + h = blake2b_new_general(length > 64 ? 64 : length); + put_data(h, hashbuf, 64); + + smemclr(hashbuf, sizeof(hashbuf)); + } + + ssh_hash_final(h, out); +} + +/* Externally visible entry point for the long hash function. This is only + * used by testcrypt, so it would be overkill to set it up like a proper + * ssh_hash. */ +strbuf *argon2_long_hash(unsigned length, ptrlen data) +{ + ssh_hash *h = hprime_new(length); + put_datapl(h, data); + strbuf *out = strbuf_new(); + hprime_final(h, length, strbuf_append(out, length)); + return out; +} + +/* ---------------------------------------------------------------------- + * Argon2's own mixing function G, which operates on 1Kb blocks of data. + * + * The definition of G in the spec takes two 1Kb blocks as input and produces + * a 1Kb output block. The first thing that happens to the input blocks is + * that they get XORed together, and then only the XOR output is used, so you + * could perfectly well regard G as a 1Kb->1Kb function. + */ + +static inline uint64_t ror(uint64_t x, unsigned rotation) +{ + unsigned lshift = 63 & -rotation, rshift = 63 & rotation; + return (x << lshift) | (x >> rshift); +} + +static inline uint64_t trunc32(uint64_t x) +{ + return x & 0xFFFFFFFF; +} + +/* Internal function similar to the BLAKE2b round, which mixes up four 64-bit + * words */ +static inline void GB(uint64_t *a, uint64_t *b, uint64_t *c, uint64_t *d) +{ + *a += *b + 2 * trunc32(*a) * trunc32(*b); + *d = ror(*d ^ *a, 32); + *c += *d + 2 * trunc32(*c) * trunc32(*d); + *b = ror(*b ^ *c, 24); + *a += *b + 2 * trunc32(*a) * trunc32(*b); + *d = ror(*d ^ *a, 16); + *c += *d + 2 * trunc32(*c) * trunc32(*d); + *b = ror(*b ^ *c, 63); +} + +/* Higher-level internal function which mixes up sixteen 64-bit words. This is + * applied to different subsets of the 128 words in a kilobyte block, and the + * API here is designed to make it easy to apply in the circumstances the spec + * requires. In every call, the sixteen words form eight pairs adjacent in + * memory, whose addresses are in arithmetic progression. So the 16 input + * words are in[0], in[1], in[instep], in[instep+1], ..., in[7*instep], + * in[7*instep+1], and the 16 output words similarly. */ +static inline void P(uint64_t *out, unsigned outstep, + uint64_t *in, unsigned instep) +{ + for (unsigned i = 0; i < 8; i++) { + out[i*outstep] = in[i*instep]; + out[i*outstep+1] = in[i*instep+1]; + } + + GB(out+0*outstep+0, out+2*outstep+0, out+4*outstep+0, out+6*outstep+0); + GB(out+0*outstep+1, out+2*outstep+1, out+4*outstep+1, out+6*outstep+1); + GB(out+1*outstep+0, out+3*outstep+0, out+5*outstep+0, out+7*outstep+0); + GB(out+1*outstep+1, out+3*outstep+1, out+5*outstep+1, out+7*outstep+1); + + GB(out+0*outstep+0, out+2*outstep+1, out+5*outstep+0, out+7*outstep+1); + GB(out+0*outstep+1, out+3*outstep+0, out+5*outstep+1, out+6*outstep+0); + GB(out+1*outstep+0, out+3*outstep+1, out+4*outstep+0, out+6*outstep+1); + GB(out+1*outstep+1, out+2*outstep+0, out+4*outstep+1, out+7*outstep+0); +} + +/* The full G function, taking input blocks X and Y. The result of G is most + * often XORed into an existing output block, so this API is designed with + * that in mind: the mixing function's output is always XORed into whatever + * 1Kb of data is already at 'out'. */ +static void G_xor(uint8_t *out, const uint8_t *X, const uint8_t *Y) +{ + uint64_t R[128], Q[128], Z[128]; + + for (unsigned i = 0; i < 128; i++) + R[i] = GET_64BIT_LSB_FIRST(X + 8*i) ^ GET_64BIT_LSB_FIRST(Y + 8*i); + + for (unsigned i = 0; i < 8; i++) + P(Q+16*i, 2, R+16*i, 2); + + for (unsigned i = 0; i < 8; i++) + P(Z+2*i, 16, Q+2*i, 16); + + for (unsigned i = 0; i < 128; i++) + PUT_64BIT_LSB_FIRST(out + 8*i, + GET_64BIT_LSB_FIRST(out + 8*i) ^ R[i] ^ Z[i]); + + smemclr(R, sizeof(R)); + smemclr(Q, sizeof(Q)); + smemclr(Z, sizeof(Z)); +} + +/* ---------------------------------------------------------------------- + * The main Argon2 function. + */ + +static void argon2_internal(uint32_t p, uint32_t T, uint32_t m, uint32_t t, + uint32_t y, ptrlen P, ptrlen S, ptrlen K, ptrlen X, + uint8_t *out) +{ + /* + * Start by hashing all the input data together: the four string arguments + * (password P, salt S, optional secret key K, optional associated data + * X), plus all the parameters for the function's memory and time usage. + * + * The output of this hash is the sole input to the subsequent mixing + * step: Argon2 does not preserve any more entropy from the inputs, it + * just makes it extra painful to get the final answer. + */ + uint8_t h0[64]; + { + ssh_hash *h = blake2b_new_general(64); + put_uint32_le(h, p); + put_uint32_le(h, T); + put_uint32_le(h, m); + put_uint32_le(h, t); + put_uint32_le(h, 0x13); /* hash function version number */ + put_uint32_le(h, y); + put_stringpl_le(h, P); + put_stringpl_le(h, S); + put_stringpl_le(h, K); + put_stringpl_le(h, X); + ssh_hash_final(h, h0); + } + + struct blk { uint8_t data[1024]; }; + + /* + * Array of 1Kb blocks. The total size is (approximately) m, the + * caller-specified parameter for how much memory to use; the blocks are + * regarded as a rectangular array of p rows ('lanes') by q columns, where + * p is the 'parallelism' input parameter (the lanes can be processed + * concurrently up to a point) and q is whatever makes the product pq come + * to m. + * + * Additionally, each row is divided into four equal 'segments', which are + * important to the way the algorithm decides which blocks to use as input + * to each step of the function. + * + * The term 'slice' refers to a whole set of vertically aligned segments, + * i.e. slice 0 is the whole left quarter of the array, and slice 3 the + * whole right quarter. + */ + size_t SL = m / (4*p); /* segment length: # of 1Kb blocks in a segment */ + size_t q = 4 * SL; /* width of the array: 4 segments times SL */ + size_t mprime = q * p; /* total size of the array, approximately m */ + + /* Allocate the memory. */ + struct blk *B = snewn(mprime, struct blk); + memset(B, 0, mprime * sizeof(struct blk)); + + /* + * Initial setup: fill the first two full columns of the array with data + * expanded from the starting hash h0. Each block is the result of using + * the long-output hash function H' to hash h0 itself plus the block's + * coordinates in the array. + */ + for (size_t i = 0; i < p; i++) { + ssh_hash *h = hprime_new(1024); + put_data(h, h0, 64); + put_uint32_le(h, 0); + put_uint32_le(h, i); + hprime_final(h, 1024, B[i].data); + } + for (size_t i = 0; i < p; i++) { + ssh_hash *h = hprime_new(1024); + put_data(h, h0, 64); + put_uint32_le(h, 1); + put_uint32_le(h, i); + hprime_final(h, 1024, B[i+p].data); + } + + /* + * Declarations for the main loop. + * + * The basic structure of the main loop is going to involve processing the + * array one whole slice (vertically divided quarter) at a time. Usually + * we'll write a new value into every single block in the slice, except + * that in the initial slice on the first pass, we've already written + * values into the first two columns during the initial setup above. So + * 'jstart' indicates the starting index in each segment we process; it + * starts off as 2 so that we don't overwrite the inital setup, and then + * after the first slice is done, we set it to 0, and it stays there. + * + * d_mode indicates whether we're being data-dependent (true) or + * data-independent (false). In the hybrid Argon2id mode, we start off + * independent, and then once we've mixed things up enough, switch over to + * dependent mode to force long serial chains of computation. + */ + size_t jstart = 2; + bool d_mode = (y == 0); + struct blk out2i, tmp2i, in2i; + + /* Outermost loop: t whole passes from left to right over the array */ + for (size_t pass = 0; pass < t; pass++) { + + /* Within that, we process the array in its four main slices */ + for (unsigned slice = 0; slice < 4; slice++) { + + /* In Argon2id mode, if we're half way through the first pass, + * this is the moment to switch d_mode from false to true */ + if (pass == 0 && slice == 2 && y == 2) + d_mode = true; + + /* Loop over every segment in the slice (i.e. every row). So i is + * the y-coordinate of each block we process. */ + for (size_t i = 0; i < p; i++) { + + /* And within that segment, process the blocks from left to + * right, starting at 'jstart' (usually 0, but 2 in the first + * slice). */ + for (size_t jpre = jstart; jpre < SL; jpre++) { + + /* j is the x-coordinate of each block we process, made up + * of the slice number and the index 'jpre' within the + * segment. */ + size_t j = slice * SL + jpre; + + /* jm1 is j-1 (mod q) */ + uint32_t jm1 = (j == 0 ? q-1 : j-1); + + /* + * Construct two 32-bit pseudorandom integers J1 and J2. + * This is the part of the algorithm that varies between + * the data-dependent and independent modes. + */ + uint32_t J1, J2; + if (d_mode) { + /* + * Data-dependent: grab the first 64 bits of the block + * to the left of this one. + */ + J1 = GET_32BIT_LSB_FIRST(B[i + p * jm1].data); + J2 = GET_32BIT_LSB_FIRST(B[i + p * jm1].data + 4); + } else { + /* + * Data-independent: generate pseudorandom data by + * hashing a sequence of preimage blocks that include + * all our input parameters, plus the coordinates of + * this point in the algorithm (array position and + * pass number) to make all the hash outputs distinct. + * + * The hash we use is G itself, applied twice. So we + * generate 1Kb of data at a time, which is enough for + * 128 (J1,J2) pairs. Hence we only need to do the + * hashing if our index within the segment is a + * multiple of 128, or if we're at the very start of + * the algorithm (in which case we started at 2 rather + * than 0). After that we can just keep picking data + * out of our most recent hash output. + */ + if (jpre == jstart || jpre % 128 == 0) { + /* + * Hash preimage is mostly zeroes, with a + * collection of assorted integer values we had + * anyway. + */ + memset(in2i.data, 0, sizeof(in2i.data)); + PUT_64BIT_LSB_FIRST(in2i.data + 0, pass); + PUT_64BIT_LSB_FIRST(in2i.data + 8, i); + PUT_64BIT_LSB_FIRST(in2i.data + 16, slice); + PUT_64BIT_LSB_FIRST(in2i.data + 24, mprime); + PUT_64BIT_LSB_FIRST(in2i.data + 32, t); + PUT_64BIT_LSB_FIRST(in2i.data + 40, y); + PUT_64BIT_LSB_FIRST(in2i.data + 48, jpre / 128 + 1); + + /* + * Now apply G twice to generate the hash output + * in out2i. + */ + memset(tmp2i.data, 0, sizeof(tmp2i.data)); + G_xor(tmp2i.data, tmp2i.data, in2i.data); + memset(out2i.data, 0, sizeof(out2i.data)); + G_xor(out2i.data, out2i.data, tmp2i.data); + } + + /* + * Extract J1 and J2 from the most recent hash output + * (whether we've just computed it or not). + */ + J1 = GET_32BIT_LSB_FIRST( + out2i.data + 8 * (jpre % 128)); + J2 = GET_32BIT_LSB_FIRST( + out2i.data + 8 * (jpre % 128) + 4); + } + + /* + * Now convert J1 and J2 into the index of an existing + * block of the array to use as input to this step. This + * is fairly fiddly. + * + * The easy part: the y-coordinate of the input block is + * obtained by reducing J2 mod p, except that at the very + * start of the algorithm (processing the first slice on + * the first pass) we simply use the same y-coordinate as + * our output block. + * + * Note that it's safe to use the ordinary % operator + * here, without any concern for timing side channels: in + * data-independent mode J2 is not correlated to any + * secrets, and in data-dependent mode we're going to be + * giving away side-channel data _anyway_ when we use it + * as an array index (and by assumption we don't care, + * because it's already massively randomised from the real + * inputs). + */ + uint32_t index_l = (pass == 0 && slice == 0) ? i : J2 % p; + + /* + * The hard part: which block in this array row do we use? + * + * First, we decide what the possible candidates are. This + * requires some case analysis, and depends on whether the + * array row is the same one we're writing into or not. + * + * If it's not the same row: we can't use any block from + * the current slice (because the segments within a slice + * have to be processable in parallel, so in a concurrent + * implementation those blocks are potentially in the + * process of being overwritten by other threads). But the + * other three slices are fair game, except that in the + * first pass, slices to the right of us won't have had + * any values written into them yet at all. + * + * If it is the same row, we _are_ allowed to use blocks + * from the current slice, but only the ones before our + * current position. + * + * In both cases, we also exclude the individual _column_ + * just to the left of the current one. (The block + * immediately to our left is going to be the _other_ + * input to G, but the spec also says that we avoid that + * column even in a different row.) + * + * All of this means that we end up choosing from a + * cyclically contiguous interval of blocks within this + * lane, but the start and end points require some thought + * to get them right. + */ + + /* Start position is the beginning of the _next_ slice + * (containing data from the previous pass), unless we're + * on pass 0, where the start position has to be 0. */ + uint32_t Wstart = (pass == 0 ? 0 : (slice + 1) % 4 * SL); + + /* End position splits up by cases. */ + uint32_t Wend; + if (index_l == i) { + /* Same lane as output: we can use anything up to (but + * not including) the block immediately left of us. */ + Wend = jm1; + } else { + /* Different lane from output: we can use anything up + * to the previous slice boundary, or one less than + * that if we're at the very left edge of our slice + * right now. */ + Wend = SL * slice; + if (jpre == 0) + Wend = (Wend + q-1) % q; + } + + /* Total number of blocks available to choose from */ + uint32_t Wsize = (Wend + q - Wstart) % q; + + /* Fiddly computation from the spec that chooses from the + * available blocks, in a deliberately non-uniform + * fashion, using J1 as pseudorandom input data. Output is + * zz which is the index within our contiguous interval. */ + uint32_t x = ((uint64_t)J1 * J1) >> 32; + uint32_t y = ((uint64_t)Wsize * x) >> 32; + uint32_t zz = Wsize - 1 - y; + + /* And index_z is the actual x coordinate of the block we + * want. */ + uint32_t index_z = (Wstart + zz) % q; + + /* Phew! Combine that block with the one immediately to + * our left, and XOR over the top of whatever is already + * in our current output block. */ + G_xor(B[i + p * j].data, B[i + p * jm1].data, + B[index_l + p * index_z].data); + } + } + + /* We've finished processing a slice. Reset jstart to 0. It will + * onily _not_ have been 0 if this was pass 0 slice 0, in which + * case it still had its initial value of 2 to avoid the starting + * data. */ + jstart = 0; + } + } + + /* + * The main output is all done. Final output works by taking the XOR of + * all the blocks in the rightmost column of the array, and then using + * that as input to our long hash H'. The output of _that_ is what we + * deliver to the caller. + */ + + struct blk C = B[p * (q-1)]; + for (size_t i = 1; i < p; i++) + memxor(C.data, C.data, B[i + p * (q-1)].data, 1024); + + { + ssh_hash *h = hprime_new(T); + put_data(h, C.data, 1024); + hprime_final(h, T, out); + } + + /* + * Clean up. + */ + smemclr(out2i.data, sizeof(out2i.data)); + smemclr(tmp2i.data, sizeof(tmp2i.data)); + smemclr(in2i.data, sizeof(in2i.data)); + smemclr(C.data, sizeof(C.data)); + smemclr(B, mprime * sizeof(struct blk)); + sfree(B); +} + +/* + * Wrapper function that appends to a strbuf (which sshpubk.c will want). + */ +void argon2(Argon2Flavour flavour, uint32_t mem, uint32_t passes, + uint32_t parallel, uint32_t taglen, + ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out) +{ + argon2_internal(parallel, taglen, mem, passes, flavour, + P, S, K, X, strbuf_append(out, taglen)); +} + +/* + * Wrapper function which dynamically chooses the number of passes to run in + * order to hit an approximate total amount of CPU time. Writes the result + * into 'passes'. + */ +void argon2_choose_passes( + Argon2Flavour flavour, uint32_t mem, + uint32_t milliseconds, uint32_t *passes, + uint32_t parallel, uint32_t taglen, + ptrlen P, ptrlen S, ptrlen K, ptrlen X, + strbuf *out) +{ + unsigned long desired_time = (TICKSPERSEC * milliseconds) / 1000; + + /* + * We only need the time taken to be approximately right, so we + * scale up the number of passes geometrically, which avoids + * taking O(t^2) time to find a pass count taking time t. + * + * Using the Fibonacci numbers is slightly nicer than the obvious + * approach of powers of 2, because it's still very easy to + * compute, and grows less fast (powers of 1.6 instead of 2), so + * you get just a touch more precision. + */ + uint32_t a = 1, b = 1; + + while (true) { + unsigned long start_time = GETTICKCOUNT(); + argon2(flavour, mem, b, parallel, taglen, P, S, K, X, out); + unsigned long ticks = GETTICKCOUNT() - start_time; + + /* But just in case computers get _too_ fast, we have to cap + * the growth before it gets past the uint32_t upper bound! So + * if computing a+b would overflow, stop here. */ + + if (ticks >= desired_time || a > (uint32_t)~b) { + *passes = b; + return; + } else { + strbuf_clear(out); + + /* Next Fibonacci number: replace (a, b) with (b, a+b) */ + b += a; + a = b - a; + } + } +} diff --git a/sshauxcrypt.c b/sshauxcrypt.c index 1367d69..7f64b27 100644 --- a/sshauxcrypt.c +++ b/sshauxcrypt.c @@ -11,30 +11,28 @@ #include "ssh.h" -static ssh_cipher *aes256_pubkey_cipher(const void *key) +static ssh_cipher *aes256_pubkey_cipher(const void *key, const void *iv) { /* * PuTTY's own .PPK format for SSH-2 private key files is * encrypted with 256-bit AES in CBC mode. */ - char iv[16]; - memset(iv, 0, 16); ssh_cipher *cipher = ssh_cipher_new(&ssh_aes256_cbc); ssh_cipher_setkey(cipher, key); ssh_cipher_setiv(cipher, iv); return cipher; } -void aes256_encrypt_pubkey(const void *key, void *blk, int len) +void aes256_encrypt_pubkey(const void *key, const void *iv, void *blk, int len) { - ssh_cipher *c = aes256_pubkey_cipher(key); + ssh_cipher *c = aes256_pubkey_cipher(key, iv); ssh_cipher_encrypt(c, blk, len); ssh_cipher_free(c); } -void aes256_decrypt_pubkey(const void *key, void *blk, int len) +void aes256_decrypt_pubkey(const void *key, const void *iv, void *blk, int len) { - ssh_cipher *c = aes256_pubkey_cipher(key); + ssh_cipher *c = aes256_pubkey_cipher(key, iv); ssh_cipher_decrypt(c, blk, len); ssh_cipher_free(c); } diff --git a/sshblake2.c b/sshblake2.c new file mode 100644 index 0000000..a4d42f2 --- /dev/null +++ b/sshblake2.c @@ -0,0 +1,223 @@ +/* + * BLAKE2 (RFC 7693) implementation for PuTTY. + * + * The BLAKE2 hash family includes BLAKE2s, in which the hash state is + * operated on as a collection of 32-bit integers, and BLAKE2b, based + * on 64-bit integers. At present this code implements BLAKE2b only. + */ + +#include +#include "ssh.h" + +static inline uint64_t ror(uint64_t x, unsigned rotation) +{ + unsigned lshift = 63 & -rotation, rshift = 63 & rotation; + return (x << lshift) | (x >> rshift); +} + +/* RFC 7963 section 2.1 */ +enum { R1 = 32, R2 = 24, R3 = 16, R4 = 63 }; + +/* RFC 7693 section 2.6 */ +static const uint64_t iv[] = { + 0x6a09e667f3bcc908, /* floor(2^64 * frac(sqrt(2))) */ + 0xbb67ae8584caa73b, /* floor(2^64 * frac(sqrt(3))) */ + 0x3c6ef372fe94f82b, /* floor(2^64 * frac(sqrt(5))) */ + 0xa54ff53a5f1d36f1, /* floor(2^64 * frac(sqrt(7))) */ + 0x510e527fade682d1, /* floor(2^64 * frac(sqrt(11))) */ + 0x9b05688c2b3e6c1f, /* floor(2^64 * frac(sqrt(13))) */ + 0x1f83d9abfb41bd6b, /* floor(2^64 * frac(sqrt(17))) */ + 0x5be0cd19137e2179, /* floor(2^64 * frac(sqrt(19))) */ +}; + +/* RFC 7693 section 2.7 */ +static const unsigned char sigma[][16] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, + {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, + { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, + { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, + { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, + {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, + {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, + { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, + {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, + /* This array recycles if you have more than 10 rounds. BLAKE2b + * has 12, so we repeat the first two rows again. */ + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, + {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, +}; + +static inline void g_half(uint64_t v[16], unsigned a, unsigned b, unsigned c, + unsigned d, uint64_t x, unsigned r1, unsigned r2) +{ + v[a] += v[b] + x; + v[d] ^= v[a]; + v[d] = ror(v[d], r1); + v[c] += v[d]; + v[b] ^= v[c]; + v[b] = ror(v[b], r2); +} + +static inline void g(uint64_t v[16], unsigned a, unsigned b, unsigned c, + unsigned d, uint64_t x, uint64_t y) +{ + g_half(v, a, b, c, d, x, R1, R2); + g_half(v, a, b, c, d, y, R3, R4); +} + +static inline void f(uint64_t h[8], uint64_t m[16], uint64_t offset_hi, + uint64_t offset_lo, unsigned final) +{ + uint64_t v[16]; + memcpy(v, h, 8 * sizeof(*v)); + memcpy(v + 8, iv, 8 * sizeof(*v)); + v[12] ^= offset_lo; + v[13] ^= offset_hi; + v[14] ^= -(uint64_t)final; + for (unsigned round = 0; round < 12; round++) { + const unsigned char *s = sigma[round]; + g(v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]]); + g(v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]]); + g(v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]]); + g(v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]]); + g(v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]]); + g(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + for (unsigned i = 0; i < 8; i++) + h[i] ^= v[i] ^ v[i+8]; + smemclr(v, sizeof(v)); +} + +static inline void f_outer(uint64_t h[8], uint8_t blk[128], uint64_t offset_hi, + uint64_t offset_lo, unsigned final) +{ + uint64_t m[16]; + for (unsigned i = 0; i < 16; i++) + m[i] = GET_64BIT_LSB_FIRST(blk + 8*i); + f(h, m, offset_hi, offset_lo, final); + smemclr(m, sizeof(m)); +} + +typedef struct blake2b { + uint64_t h[8]; + unsigned hashlen; + + uint8_t block[128]; + size_t used; + uint64_t lenhi, lenlo; + + BinarySink_IMPLEMENTATION; + ssh_hash hash; +} blake2b; + +static void blake2b_write(BinarySink *bs, const void *vp, size_t len); + +static ssh_hash *blake2b_new_inner(unsigned hashlen) +{ + assert(hashlen <= ssh_blake2b.hlen); + + blake2b *s = snew(blake2b); + s->hash.vt = &ssh_blake2b; + s->hashlen = hashlen; + BinarySink_INIT(s, blake2b_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; +} + +static ssh_hash *blake2b_new(const ssh_hashalg *alg) +{ + return blake2b_new_inner(alg->hlen); +} + +ssh_hash *blake2b_new_general(unsigned hashlen) +{ + ssh_hash *h = blake2b_new_inner(hashlen); + ssh_hash_reset(h); + return h; +} + +static void blake2b_reset(ssh_hash *hash) +{ + blake2b *s = container_of(hash, blake2b, hash); + + /* Initialise the hash to the standard IV */ + memcpy(s->h, iv, sizeof(s->h)); + + /* XOR in the parameters: secret key length (here always 0) in + * byte 1, and hash length in byte 0. */ + s->h[0] ^= 0x01010000 ^ s->hashlen; + + s->used = 0; + s->lenhi = s->lenlo = 0; +} + +static void blake2b_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + blake2b *copy = container_of(hcopy, blake2b, hash); + blake2b *orig = container_of(horig, blake2b, hash); + + memcpy(copy, orig, sizeof(*copy)); + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); +} + +static void blake2b_free(ssh_hash *hash) +{ + blake2b *s = container_of(hash, blake2b, hash); + + smemclr(s, sizeof(*s)); + sfree(s); +} + +static void blake2b_write(BinarySink *bs, const void *vp, size_t len) +{ + blake2b *s = BinarySink_DOWNCAST(bs, blake2b); + const uint8_t *p = vp; + + while (len > 0) { + if (s->used == sizeof(s->block)) { + f_outer(s->h, s->block, s->lenhi, s->lenlo, 0); + s->used = 0; + } + + size_t chunk = sizeof(s->block) - s->used; + if (chunk > len) + chunk = len; + + memcpy(s->block + s->used, p, chunk); + s->used += chunk; + p += chunk; + len -= chunk; + + s->lenlo += chunk; + s->lenhi += (s->lenlo < chunk); + } +} + +static void blake2b_digest(ssh_hash *hash, uint8_t *digest) +{ + blake2b *s = container_of(hash, blake2b, hash); + + memset(s->block + s->used, 0, sizeof(s->block) - s->used); + f_outer(s->h, s->block, s->lenhi, s->lenlo, 1); + + uint8_t hash_pre[128]; + for (unsigned i = 0; i < 8; i++) + PUT_64BIT_LSB_FIRST(hash_pre + 8*i, s->h[i]); + memcpy(digest, hash_pre, s->hashlen); + smemclr(hash_pre, sizeof(hash_pre)); +} + +const ssh_hashalg ssh_blake2b = { + .new = blake2b_new, + .reset = blake2b_reset, + .copyfrom = blake2b_copyfrom, + .digest = blake2b_digest, + .free = blake2b_free, + .hlen = 64, + .blocklen = 128, + HASHALG_NAMES_BARE("BLAKE2b-64"), +}; diff --git a/sshblowf.c b/sshblowf.c index a48401b..c74f06c 100644 --- a/sshblowf.c +++ b/sshblowf.c @@ -648,30 +648,47 @@ static void blowfish_ssh2_sdctr(ssh_cipher *cipher, void *blk, int len) } const ssh_cipheralg ssh_blowfish_ssh1 = { - blowfish_new, blowfish_free, - blowfish_ssh1_setiv, blowfish_ssh_setkey, - blowfish_ssh1_encrypt_blk, blowfish_ssh1_decrypt_blk, - NULL, NULL, NULL, - 8, 128, SSH1_SESSION_KEY_LENGTH, SSH_CIPHER_IS_CBC, "Blowfish-256 CBC", - NULL + .new = blowfish_new, + .free = blowfish_free, + .setiv = blowfish_ssh1_setiv, + .setkey = blowfish_ssh_setkey, + .encrypt = blowfish_ssh1_encrypt_blk, + .decrypt = blowfish_ssh1_decrypt_blk, + .blksize = 8, + .real_keybits = 128, + .padded_keybytes = SSH1_SESSION_KEY_LENGTH, + .flags = SSH_CIPHER_IS_CBC, + .text_name = "Blowfish-256 CBC", }; const ssh_cipheralg ssh_blowfish_ssh2 = { - blowfish_new, blowfish_free, - blowfish_ssh2_setiv, blowfish_ssh_setkey, - blowfish_ssh2_encrypt_blk, blowfish_ssh2_decrypt_blk, NULL, NULL, - "blowfish-cbc", - 8, 128, 16, SSH_CIPHER_IS_CBC, "Blowfish-128 CBC", - NULL + .new = blowfish_new, + .free = blowfish_free, + .setiv = blowfish_ssh2_setiv, + .setkey = blowfish_ssh_setkey, + .encrypt = blowfish_ssh2_encrypt_blk, + .decrypt = blowfish_ssh2_decrypt_blk, + .ssh2_id = "blowfish-cbc", + .blksize = 8, + .real_keybits = 128, + .padded_keybytes = 16, + .flags = SSH_CIPHER_IS_CBC, + .text_name = "Blowfish-128 CBC", }; const ssh_cipheralg ssh_blowfish_ssh2_ctr = { - blowfish_new, blowfish_free, - blowfish_ssh2_setiv, blowfish_ssh_setkey, - blowfish_ssh2_sdctr, blowfish_ssh2_sdctr, NULL, NULL, - "blowfish-ctr", - 8, 256, 32, 0, "Blowfish-256 SDCTR", - NULL + .new = blowfish_new, + .free = blowfish_free, + .setiv = blowfish_ssh2_setiv, + .setkey = blowfish_ssh_setkey, + .encrypt = blowfish_ssh2_sdctr, + .decrypt = blowfish_ssh2_sdctr, + .ssh2_id = "blowfish-ctr", + .blksize = 8, + .real_keybits = 256, + .padded_keybytes = 32, + .flags = 0, + .text_name = "Blowfish-256 SDCTR", }; static const ssh_cipheralg *const blowfish_list[] = { diff --git a/sshbpp.h b/sshbpp.h index 8192d4e..87e7d7e 100644 --- a/sshbpp.h +++ b/sshbpp.h @@ -5,6 +5,8 @@ #ifndef PUTTY_SSHBPP_H #define PUTTY_SSHBPP_H +typedef struct BinaryPacketProtocolVtable BinaryPacketProtocolVtable; + struct BinaryPacketProtocolVtable { void (*free)(BinaryPacketProtocol *); void (*handle_input)(BinaryPacketProtocol *); @@ -34,7 +36,10 @@ struct BinaryPacketProtocol { * the callback on out_pq. */ IdempotentCallback ic_out_pq; + /* Information that all packet layers sharing this BPP will + * potentially be interested in. */ int remote_bugs; + bool ext_info_rsa_sha256_ok, ext_info_rsa_sha512_ok; /* Set this if remote connection closure should not generate an * error message (either because it's not to be treated as an diff --git a/sshccp.c b/sshccp.c index 7812933..dd25b99 100644 --- a/sshccp.c +++ b/sshccp.c @@ -944,11 +944,17 @@ static const char *poly_text_name(ssh2_mac *mac) } const ssh2_macalg ssh2_poly1305 = { - poly_ssh2_new, poly_ssh2_free, poly_setkey, - poly_start, poly_genresult, poly_text_name, - - "", "", /* Not selectable individually, just part of ChaCha20-Poly1305 */ - 16, 0, + .new = poly_ssh2_new, + .free = poly_ssh2_free, + .setkey = poly_setkey, + .start = poly_start, + .genresult = poly_genresult, + .text_name = poly_text_name, + .name = "", + .etm_name = "", /* Not selectable individually, just part of + * ChaCha20-Poly1305 */ + .len = 16, + .keylen = 0, }; static ssh_cipher *ccp_new(const ssh_cipheralg *alg) @@ -1032,20 +1038,21 @@ static void ccp_decrypt_length(ssh_cipher *cipher, void *blk, int len, } const ssh_cipheralg ssh2_chacha20_poly1305 = { - - ccp_new, - ccp_free, - ccp_iv, - ccp_key, - ccp_encrypt, - ccp_decrypt, - ccp_encrypt_length, - ccp_decrypt_length, - - "chacha20-poly1305@openssh.com", - 1, 512, 64, SSH_CIPHER_SEPARATE_LENGTH, "ChaCha20", - - &ssh2_poly1305 + .new = ccp_new, + .free = ccp_free, + .setiv = ccp_iv, + .setkey = ccp_key, + .encrypt = ccp_encrypt, + .decrypt = ccp_decrypt, + .encrypt_length = ccp_encrypt_length, + .decrypt_length = ccp_decrypt_length, + .ssh2_id = "chacha20-poly1305@openssh.com", + .blksize = 1, + .real_keybits = 512, + .padded_keybytes = 64, + .flags = SSH_CIPHER_SEPARATE_LENGTH, + .text_name = "ChaCha20", + .required_mac = &ssh2_poly1305, }; static const ssh_cipheralg *const ccp_list[] = { diff --git a/sshchan.h b/sshchan.h index cedf357..14dfccb 100644 --- a/sshchan.h +++ b/sshchan.h @@ -6,6 +6,8 @@ #ifndef PUTTY_SSHCHAN_H #define PUTTY_SSHCHAN_H +typedef struct ChannelVtable ChannelVtable; + struct ChannelVtable { void (*free)(Channel *); @@ -169,6 +171,8 @@ Channel *zombiechan_new(void); * implementation to talk back to. */ +typedef struct SshChannelVtable SshChannelVtable; + struct SshChannelVtable { size_t (*write)(SshChannel *c, bool is_stderr, const void *, size_t); void (*write_eof)(SshChannel *c); diff --git a/sshcommon.c b/sshcommon.c index 2607384..5485e33 100644 --- a/sshcommon.c +++ b/sshcommon.c @@ -35,6 +35,7 @@ void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node) node->prev = pqb->end.prev; node->next->prev = node; node->prev->next = node; + pqb->total_size += node->formal_size; if (pqb->ic) queue_idempotent_callback(pqb->ic); @@ -47,6 +48,7 @@ void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node) node->next = pqb->end.next; node->next->prev = node; node->prev->next = node; + pqb->total_size += node->formal_size; if (pqb->ic) queue_idempotent_callback(pqb->ic); @@ -72,6 +74,23 @@ static IdempotentCallback ic_pktin_free = { pktin_free_queue_callback, NULL, false }; +static inline void pq_unlink_common(PacketQueueBase *pqb, + PacketQueueNode *node) +{ + node->next->prev = node->prev; + node->prev->next = node->next; + + /* Check total_size doesn't drift out of sync downwards, by + * ensuring it doesn't underflow when we do this subtraction */ + assert(pqb->total_size >= node->formal_size); + pqb->total_size -= node->formal_size; + + /* Check total_size doesn't drift out of sync upwards, by checking + * that it's returned to exactly zero whenever a queue is + * emptied */ + assert(pqb->end.next != &pqb->end || pqb->total_size == 0); +} + static PktIn *pq_in_after(PacketQueueBase *pqb, PacketQueueNode *prev, bool pop) { @@ -80,14 +99,14 @@ static PktIn *pq_in_after(PacketQueueBase *pqb, return NULL; if (pop) { - node->next->prev = node->prev; - node->prev->next = node->next; + pq_unlink_common(pqb, node); node->prev = pktin_freeq_head.prev; node->next = &pktin_freeq_head; node->next->prev = node; node->prev->next = node; node->on_free_queue = true; + queue_idempotent_callback(&ic_pktin_free); } @@ -102,8 +121,8 @@ static PktOut *pq_out_after(PacketQueueBase *pqb, return NULL; if (pop) { - node->next->prev = node->prev; - node->prev->next = node->next; + pq_unlink_common(pqb, node); + node->prev = node->next = NULL; } @@ -115,6 +134,7 @@ void pq_in_init(PktInQueue *pq) pq->pqb.ic = NULL; pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end; pq->after = pq_in_after; + pq->pqb.total_size = 0; } void pq_out_init(PktOutQueue *pq) @@ -122,6 +142,7 @@ void pq_out_init(PktOutQueue *pq) pq->pqb.ic = NULL; pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end; pq->after = pq_out_after; + pq->pqb.total_size = 0; } void pq_in_clear(PktInQueue *pq) @@ -153,6 +174,8 @@ void pq_base_concatenate(PacketQueueBase *qdest, { struct PacketQueueNode *head1, *tail1, *head2, *tail2; + size_t total_size = q1->total_size + q2->total_size; + /* * Extract the contents from both input queues, and empty them. */ @@ -164,6 +187,7 @@ void pq_base_concatenate(PacketQueueBase *qdest, q1->end.next = q1->end.prev = &q1->end; q2->end.next = q2->end.prev = &q2->end; + q1->total_size = q2->total_size = 0; /* * Link the two lists together, handling the case where one or @@ -206,6 +230,8 @@ void pq_base_concatenate(PacketQueueBase *qdest, if (qdest->ic) queue_idempotent_callback(qdest->ic); } + + qdest->total_size = total_size; } /* ---------------------------------------------------------------------- @@ -235,6 +261,7 @@ static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len) sgrowarrayn_nm(pkt->data, pkt->maxlen, pkt->length, len); memcpy(pkt->data + pkt->length, data, len); pkt->length += len; + pkt->qnode.formal_size = pkt->length; } static void ssh_pkt_BinarySink_write(BinarySink *bs, @@ -263,29 +290,29 @@ static void zombiechan_open_failure(Channel *chan, const char *); static bool zombiechan_want_close(Channel *chan, bool sent_eof, bool rcvd_eof); static char *zombiechan_log_close_msg(Channel *chan) { return NULL; } -static const struct ChannelVtable zombiechan_channelvt = { - zombiechan_free, - zombiechan_do_nothing, /* open_confirmation */ - zombiechan_open_failure, - zombiechan_send, - zombiechan_do_nothing, /* send_eof */ - zombiechan_set_input_wanted, - zombiechan_log_close_msg, - zombiechan_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - chan_no_request_response, +static const ChannelVtable zombiechan_channelvt = { + .free = zombiechan_free, + .open_confirmation = zombiechan_do_nothing, + .open_failed = zombiechan_open_failure, + .send = zombiechan_send, + .send_eof = zombiechan_do_nothing, + .set_input_wanted = zombiechan_set_input_wanted, + .log_close_msg = zombiechan_log_close_msg, + .want_close = zombiechan_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, }; Channel *zombiechan_new(void) @@ -329,109 +356,6 @@ static bool zombiechan_want_close(Channel *chan, bool sent_eof, bool rcvd_eof) return true; } -/* ---------------------------------------------------------------------- - * Centralised standard methods for other channel implementations to - * borrow. - */ - -void chan_remotely_opened_confirmation(Channel *chan) -{ - unreachable("this channel type should never receive OPEN_CONFIRMATION"); -} - -void chan_remotely_opened_failure(Channel *chan, const char *errtext) -{ - unreachable("this channel type should never receive OPEN_FAILURE"); -} - -bool chan_default_want_close( - Channel *chan, bool sent_local_eof, bool rcvd_remote_eof) -{ - /* - * Default close policy: we start initiating the CHANNEL_CLOSE - * procedure as soon as both sides of the channel have seen EOF. - */ - return sent_local_eof && rcvd_remote_eof; -} - -bool chan_no_exit_status(Channel *chan, int status) -{ - return false; -} - -bool chan_no_exit_signal( - Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg) -{ - return false; -} - -bool chan_no_exit_signal_numeric( - Channel *chan, int signum, bool core_dumped, ptrlen msg) -{ - return false; -} - -bool chan_no_run_shell(Channel *chan) -{ - return false; -} - -bool chan_no_run_command(Channel *chan, ptrlen command) -{ - return false; -} - -bool chan_no_run_subsystem(Channel *chan, ptrlen subsys) -{ - return false; -} - -bool chan_no_enable_x11_forwarding( - Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, - unsigned screen_number) -{ - return false; -} - -bool chan_no_enable_agent_forwarding(Channel *chan) -{ - return false; -} - -bool chan_no_allocate_pty( - Channel *chan, ptrlen termtype, unsigned width, unsigned height, - unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) -{ - return false; -} - -bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value) -{ - return false; -} - -bool chan_no_send_break(Channel *chan, unsigned length) -{ - return false; -} - -bool chan_no_send_signal(Channel *chan, ptrlen signame) -{ - return false; -} - -bool chan_no_change_window_size( - Channel *chan, unsigned width, unsigned height, - unsigned pixwidth, unsigned pixheight) -{ - return false; -} - -void chan_no_request_response(Channel *chan, bool success) -{ - unreachable("this channel type should never send a want-reply request"); -} - /* ---------------------------------------------------------------------- * Common routines for handling SSH tty modes. */ @@ -808,6 +732,11 @@ void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text) sfree(text); } +size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl) +{ + return ppl->out_pq->pqb.total_size; +} + /* ---------------------------------------------------------------------- * Common helper functions for clients and implementations of * BinaryPacketProtocol. @@ -897,28 +826,28 @@ bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin) * Function to check a host key against any manually configured in Conf. */ -int verify_ssh_manual_host_key( - Conf *conf, const char *fingerprint, ssh_key *key) +int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key) { if (!conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, 0)) return -1; /* no manual keys configured */ - if (fingerprint) { - /* - * The fingerprint string we've been given will have things - * like 'ssh-rsa 2048' at the front of it. Strip those off and - * narrow down to just the colon-separated hex block at the - * end of the string. - */ - const char *p = strrchr(fingerprint, ' '); - fingerprint = p ? p+1 : fingerprint; - /* Quick sanity checks, including making sure it's in lowercase */ - assert(strlen(fingerprint) == 16*3 - 1); - assert(fingerprint[2] == ':'); - assert(fingerprint[strspn(fingerprint, "0123456789abcdef:")] == 0); - - if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, fingerprint)) - return 1; /* success */ + if (fingerprints) { + for (size_t i = 0; i < SSH_N_FPTYPES; i++) { + /* + * Each fingerprint string we've been given will have + * things like 'ssh-rsa 2048' at the front of it. Strip + * those off and narrow down to just the hash at the end + * of the string. + */ + const char *fingerprint = fingerprints[i]; + if (!fingerprint) + continue; + const char *p = strrchr(fingerprint, ' '); + fingerprint = p ? p+1 : fingerprint; + if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, + fingerprint)) + return 1; /* success */ + } } if (key) { @@ -1015,17 +944,3 @@ void ssh1_compute_session_id( put_data(hash, cookie, 8); ssh_hash_final(hash, session_id); } - -/* ---------------------------------------------------------------------- - * Other miscellaneous utility functions. - */ - -void free_rportfwd(struct ssh_rportfwd *rpf) -{ - if (rpf) { - sfree(rpf->log_description); - sfree(rpf->shost); - sfree(rpf->dhost); - sfree(rpf); - } -} diff --git a/sshcr.h b/sshcr.h index 8406ef3..e87ce86 100644 --- a/sshcr.h +++ b/sshcr.h @@ -25,17 +25,19 @@ * Database for Edit and Continue'. */ -#define crBegin(v) { int *crLine = &v; switch(v) { case 0:; +#define crBegin(v) do { int *crLine = &v; switch(v) { case 0: #define crBeginState crBegin(s->crLine) #define crStateP(t, v) \ struct t *s; \ if (!(v)) { s = (v) = snew(struct t); s->crLine = 0; } \ s = (v); #define crState(t) crStateP(t, ssh->t) -#define crFinish(z) } *crLine = 0; return (z); } -#define crFinishV } *crLine = 0; return; } -#define crFinishFree(z) } sfree(s); return (z); } -#define crFinishFreeV } sfree(s); return; } +#define crFinish(z) } *crLine = 0; return (z); } while (0) +#define crFinishV } *crLine = 0; return; } while (0) +#define crFinishFreed(z) } return (z); } while (0) +#define crFinishFreedV } return; } while (0) +#define crFinishFree(z) } sfree(s); return (z); } while (0) +#define crFinishFreeV } sfree(s); return; } while (0) #define crReturn(z) \ do {\ *crLine =__LINE__; return (z); case __LINE__:;\ diff --git a/sshcrcda.c b/sshcrcda.c index 33dedad..1804475 100644 --- a/sshcrcda.c +++ b/sshcrcda.c @@ -44,8 +44,8 @@ #define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE)) -uint8_t ONE[4] = { 1, 0, 0, 0 }; -uint8_t ZERO[4] = { 0, 0, 0, 0 }; +static const uint8_t ONE[4] = { 1, 0, 0, 0 }; +static const uint8_t ZERO[4] = { 0, 0, 0, 0 }; struct crcda_ctx { uint16_t *h; diff --git a/sshdes.c b/sshdes.c index 8d4b939..d2ba982 100644 --- a/sshdes.c +++ b/sshdes.c @@ -430,7 +430,7 @@ static inline uint64_t bitsel( return ret; } -void des_key_setup(uint64_t key, des_keysched *sched) +static void des_key_setup(uint64_t key, des_keysched *sched) { static const int8_t PC1[] = { 7, 15, 23, 31, 39, 47, 55, 63, 6, 14, 22, 30, 38, 46, @@ -683,16 +683,34 @@ static void des_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len) } const ssh_cipheralg ssh_des = { - des_cbc_new, des_cbc_free, des_cbc_setiv, des_cbc_setkey, - des_cbc_encrypt, des_cbc_decrypt, NULL, NULL, "des-cbc", - 8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC", NULL + .new = des_cbc_new, + .free = des_cbc_free, + .setiv = des_cbc_setiv, + .setkey = des_cbc_setkey, + .encrypt = des_cbc_encrypt, + .decrypt = des_cbc_decrypt, + .ssh2_id = "des-cbc", + .blksize = 8, + .real_keybits = 56, + .padded_keybytes = 8, + .flags = SSH_CIPHER_IS_CBC, + .text_name = "single-DES CBC", }; const ssh_cipheralg ssh_des_sshcom_ssh2 = { /* Same as ssh_des_cbc, but with a different SSH-2 ID */ - des_cbc_new, des_cbc_free, des_cbc_setiv, des_cbc_setkey, - des_cbc_encrypt, des_cbc_decrypt, NULL, NULL, "des-cbc@ssh.com", - 8, 56, 8, SSH_CIPHER_IS_CBC, "single-DES CBC", NULL + .new = des_cbc_new, + .free = des_cbc_free, + .setiv = des_cbc_setiv, + .setkey = des_cbc_setkey, + .encrypt = des_cbc_encrypt, + .decrypt = des_cbc_decrypt, + .ssh2_id = "des-cbc@ssh.com", + .blksize = 8, + .real_keybits = 56, + .padded_keybytes = 8, + .flags = SSH_CIPHER_IS_CBC, + .text_name = "single-DES CBC", }; static const ssh_cipheralg *const des_list[] = { @@ -784,9 +802,18 @@ static void des3_cbc1_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len) } const ssh_cipheralg ssh_3des_ssh2 = { - des3_cbc1_new, des3_cbc1_free, des3_cbc1_setiv, des3_cbc1_setkey, - des3_cbc1_cbc_encrypt, des3_cbc1_cbc_decrypt, NULL, NULL, "3des-cbc", - 8, 168, 24, SSH_CIPHER_IS_CBC, "triple-DES CBC", NULL + .new = des3_cbc1_new, + .free = des3_cbc1_free, + .setiv = des3_cbc1_setiv, + .setkey = des3_cbc1_setkey, + .encrypt = des3_cbc1_cbc_encrypt, + .decrypt = des3_cbc1_cbc_decrypt, + .ssh2_id = "3des-cbc", + .blksize = 8, + .real_keybits = 168, + .padded_keybytes = 24, + .flags = SSH_CIPHER_IS_CBC, + .text_name = "triple-DES CBC", }; /* ---------------------------------------------------------------------- @@ -872,9 +899,18 @@ static void des3_sdctr_encrypt_decrypt(ssh_cipher *ciph, void *vdata, int len) } const ssh_cipheralg ssh_3des_ssh2_ctr = { - des3_sdctr_new, des3_sdctr_free, des3_sdctr_setiv, des3_sdctr_setkey, - des3_sdctr_encrypt_decrypt, des3_sdctr_encrypt_decrypt, - NULL, NULL, "3des-ctr", 8, 168, 24, 0, "triple-DES SDCTR", NULL + .new = des3_sdctr_new, + .free = des3_sdctr_free, + .setiv = des3_sdctr_setiv, + .setkey = des3_sdctr_setkey, + .encrypt = des3_sdctr_encrypt_decrypt, + .decrypt = des3_sdctr_encrypt_decrypt, + .ssh2_id = "3des-ctr", + .blksize = 8, + .real_keybits = 168, + .padded_keybytes = 24, + .flags = 0, + .text_name = "triple-DES SDCTR", }; static const ssh_cipheralg *const des3_list[] = { @@ -998,7 +1034,15 @@ static void des3_cbc3_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len) } const ssh_cipheralg ssh_3des_ssh1 = { - des3_cbc3_new, des3_cbc3_free, des3_cbc3_setiv, des3_cbc3_setkey, - des3_cbc3_cbc_encrypt, des3_cbc3_cbc_decrypt, NULL, NULL, NULL, - 8, 168, 24, SSH_CIPHER_IS_CBC, "triple-DES inner-CBC", NULL + .new = des3_cbc3_new, + .free = des3_cbc3_free, + .setiv = des3_cbc3_setiv, + .setkey = des3_cbc3_setkey, + .encrypt = des3_cbc3_cbc_encrypt, + .decrypt = des3_cbc3_cbc_decrypt, + .blksize = 8, + .real_keybits = 168, + .padded_keybytes = 24, + .flags = SSH_CIPHER_IS_CBC, + .text_name = "triple-DES inner-CBC", }; diff --git a/sshdss.c b/sshdss.c index 2bf26d9..3e0c761 100644 --- a/sshdss.c +++ b/sshdss.c @@ -72,8 +72,10 @@ static char *dss_cache_str(ssh_key *key) struct dss_key *dss = container_of(key, struct dss_key, sshk); strbuf *sb = strbuf_new(); - if (!dss->p) + if (!dss->p) { + strbuf_free(sb); return NULL; + } append_hex_to_strbuf(sb, dss->p); append_hex_to_strbuf(sb, dss->q); @@ -83,6 +85,23 @@ static char *dss_cache_str(ssh_key *key) return strbuf_to_str(sb); } +static key_components *dss_components(ssh_key *key) +{ + struct dss_key *dss = container_of(key, struct dss_key, sshk); + key_components *kc = key_components_new(); + + key_components_add_text(kc, "key_type", "DSA"); + assert(dss->p); + key_components_add_mp(kc, "p", dss->p); + key_components_add_mp(kc, "q", dss->q); + key_components_add_mp(kc, "g", dss->g); + key_components_add_mp(kc, "public_y", dss->y); + if (dss->x) + key_components_add_mp(kc, "private_x", dss->x); + + return kc; +} + static char *dss_invalid(ssh_key *key, unsigned flags) { /* No validity criterion will stop us from using a DSA key at all */ @@ -399,12 +418,12 @@ mp_int *dss_gen_k(const char *id_string, mp_int *modulus, h = ssh_hash_new(&ssh_sha512); put_asciz(h, id_string); put_mp_ssh2(h, private_key); - ssh_hash_final(h, digest512); + ssh_hash_digest(h, digest512); /* * Now hash that digest plus the message hash. */ - h = ssh_hash_new(&ssh_sha512); + ssh_hash_reset(h); put_data(h, digest512, sizeof(digest512)); put_data(h, digest, digest_len); ssh_hash_final(h, digest512); @@ -466,23 +485,19 @@ static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) } const ssh_keyalg ssh_dss = { - dss_new_pub, - dss_new_priv, - dss_new_priv_openssh, - - dss_freekey, - dss_invalid, - dss_sign, - dss_verify, - dss_public_blob, - dss_private_blob, - dss_openssh_blob, - dss_cache_str, - - dss_pubkey_bits, - - "ssh-dss", - "dss", - NULL, - 0, /* no supported flags */ + .new_pub = dss_new_pub, + .new_priv = dss_new_priv, + .new_priv_openssh = dss_new_priv_openssh, + .freekey = dss_freekey, + .invalid = dss_invalid, + .sign = dss_sign, + .verify = dss_verify, + .public_blob = dss_public_blob, + .private_blob = dss_private_blob, + .openssh_blob = dss_openssh_blob, + .cache_str = dss_cache_str, + .components = dss_components, + .pubkey_bits = dss_pubkey_bits, + .ssh_id = "ssh-dss", + .cache_id = "dss", }; diff --git a/sshdssg.c b/sshdssg.c index 2d95773..3b52725 100644 --- a/sshdssg.c +++ b/sshdssg.c @@ -4,69 +4,58 @@ #include "misc.h" #include "ssh.h" +#include "sshkeygen.h" #include "mpint.h" -int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, - void *pfnparam) +int dsa_generate(struct dss_key *key, int bits, PrimeGenerationContext *pgc, + ProgressReceiver *prog) { /* - * Set up the phase limits for the progress report. We do this - * by passing minus the phase number. + * Progress-reporting setup. * - * For prime generation: our initial filter finds things - * coprime to everything below 2^16. Computing the product of - * (p-1)/p for all prime p below 2^16 gives about 20.33; so - * among B-bit integers, one in every 20.33 will get through - * the initial filter to be a candidate prime. + * DSA generation involves three potentially long jobs: inventing + * the small prime q, the large prime p, and finding an order-q + * element of the multiplicative group of p. * - * Meanwhile, we are searching for primes in the region of 2^B; - * since pi(x) ~ x/log(x), when x is in the region of 2^B, the - * prime density will be d/dx pi(x) ~ 1/log(B), i.e. about - * 1/0.6931B. So the chance of any given candidate being prime - * is 20.33/0.6931B, which is roughly 29.34 divided by B. + * The latter is done by finding an element whose order is + * _divisible_ by q and raising it to the power of (p-1)/q. Every + * element whose order is not divisible by q is a qth power of q + * distinct elements whose order _is_ divisible by q, so the + * probability of not finding a suitable element on the first try + * is in the region of 1/q, i.e. at most 2^-159. * - * So now we have this probability P, we're looking at an - * exponential distribution with parameter P: we will manage in - * one attempt with probability P, in two with probability - * P(1-P), in three with probability P(1-P)^2, etc. The - * probability that we have still not managed to find a prime - * after N attempts is (1-P)^N. - * - * We therefore inform the progress indicator of the number B - * (29.34/B), so that it knows how much to increment by each - * time. We do this in 16-bit fixed point, so 29.34 becomes - * 0x1D.57C4. + * (So the probability of success will end up indistinguishable + * from 1 in IEEE standard floating point! But what can you do.) */ - pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x2800); - pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / 160); - pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x40 * bits); - pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / bits); + ProgressPhase phase_q = primegen_add_progress_phase(pgc, prog, 160); + ProgressPhase phase_p = primegen_add_progress_phase(pgc, prog, bits); + double g_failure_probability = 1.0 + / (double)(1ULL << 53) + / (double)(1ULL << 53) + / (double)(1ULL << 53); + ProgressPhase phase_g = progress_add_probabilistic( + prog, estimate_modexp_cost(bits), 1.0 - g_failure_probability); + progress_ready(prog); - /* - * In phase three we are finding an order-q element of the - * multiplicative group of p, by finding an element whose order - * is _divisible_ by q and raising it to the power of (p-1)/q. - * _Most_ elements will have order divisible by q, since for a - * start phi(p) of them will be primitive roots. So - * realistically we don't need to set this much below 1 (64K). - * Still, we'll set it to 1/2 (32K) to be on the safe side. - */ - pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x2000); - pfn(pfnparam, PROGFN_EXP_PHASE, 3, -32768); + PrimeCandidateSource *pcs; - pfn(pfnparam, PROGFN_READY, 0, 0); - - unsigned pfirst, qfirst; - invent_firstbits(&pfirst, &qfirst, 0); /* * Generate q: a prime of length 160. */ - mp_int *q = primegen(160, 2, 2, NULL, 1, pfn, pfnparam, qfirst); + progress_start_phase(prog, phase_q); + pcs = pcs_new(160); + mp_int *q = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + /* * Now generate p: a prime of length `bits', such that p-1 is * divisible by q. */ - mp_int *p = primegen(bits-160, 2, 2, q, 2, pfn, pfnparam, pfirst); + progress_start_phase(prog, phase_p); + pcs = pcs_new(bits); + pcs_require_residue_1_mod_prime(pcs, q); + mp_int *p = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); /* * Next we need g. Raise 2 to the power (p-1)/q modulo p, and @@ -74,12 +63,12 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, * soon as we hit a non-unit (and non-zero!) one, that'll do * for g. */ + progress_start_phase(prog, phase_g); mp_int *power = mp_div(p, q); /* this is floor(p/q) == (p-1)/q */ - mp_int *h = mp_from_integer(1); - int progress = 0; + mp_int *h = mp_from_integer(2); mp_int *g; while (1) { - pfn(pfnparam, PROGFN_PROGRESS, 3, ++progress); + progress_report_attempt(prog); g = mp_modpow(h, power, p); if (mp_hs_integer(g, 2)) break; /* got one */ @@ -88,6 +77,7 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn, } mp_free(h); mp_free(power); + progress_report_phase_complete(prog); /* * Now we're nearly done. All we need now is our private key x, diff --git a/sshecc.c b/sshecc.c index dd9a7e3..ba580db 100644 --- a/sshecc.c +++ b/sshecc.c @@ -7,7 +7,7 @@ * * Montgomery form curves are supported for DH. (Curve25519) * - * Edwards form curves are supported for DSA. (Ed25519) + * Edwards form curves are supported for DSA. (Ed25519, Ed448) */ /* @@ -44,19 +44,20 @@ */ static void initialise_common( - struct ec_curve *curve, EllipticCurveType type, mp_int *p) + struct ec_curve *curve, EllipticCurveType type, mp_int *p, + unsigned extrabits) { curve->type = type; curve->p = mp_copy(p); curve->fieldBits = mp_get_nbits(p); - curve->fieldBytes = (curve->fieldBits + 7) / 8; + curve->fieldBytes = (curve->fieldBits + extrabits + 7) / 8; } static void initialise_wcurve( struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order) { - initialise_common(curve, EC_WEIERSTRASS, p); + initialise_common(curve, EC_WEIERSTRASS, p, 0); curve->w.wc = ecc_weierstrass_curve(p, a, b, nonsquare); @@ -68,7 +69,7 @@ static void initialise_mcurve( struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b, mp_int *G_x, unsigned log2_cofactor) { - initialise_common(curve, EC_MONTGOMERY, p); + initialise_common(curve, EC_MONTGOMERY, p, 0); curve->m.mc = ecc_montgomery_curve(p, a, b); curve->m.log2_cofactor = log2_cofactor; @@ -78,11 +79,15 @@ static void initialise_mcurve( static void initialise_ecurve( struct ec_curve *curve, mp_int *p, mp_int *d, mp_int *a, - mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order) + mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order, + unsigned log2_cofactor) { - initialise_common(curve, EC_EDWARDS, p); + /* Ensure curve->fieldBytes is long enough to store an extra bit + * for a compressed point */ + initialise_common(curve, EC_EDWARDS, p, 1); curve->e.ec = ecc_edwards_curve(p, d, a, nonsquare); + curve->e.log2_cofactor = log2_cofactor; curve->e.G = ecc_edwards_point_new(curve->e.ec, G_x, G_y); curve->e.G_order = mp_copy(G_order); @@ -213,6 +218,35 @@ static struct ec_curve *ec_curve25519(void) return &curve; } +static struct ec_curve *ec_curve448(void) +{ + static struct ec_curve curve = { 0 }; + static bool initialised = false; + + if (!initialised) + { + mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + mp_int *a = MP_LITERAL(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000262a6); + mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); + mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005); + initialise_mcurve(&curve, p, a, b, G_x, 2); + mp_free(p); + mp_free(a); + mp_free(b); + mp_free(G_x); + + /* This curve doesn't need a name, because it's never used in + * any format that embeds the curve name */ + curve.name = NULL; + curve.textname = "Curve448"; + + /* Now initialised, no need to do it again */ + initialised = true; + } + + return &curve; +} + static struct ec_curve *ec_ed25519(void) { static struct ec_curve curve = { 0 }; @@ -227,7 +261,8 @@ static struct ec_curve *ec_ed25519(void) mp_int *G_y = MP_LITERAL(0x6666666666666666666666666666666666666666666666666666666666666658); mp_int *G_order = MP_LITERAL(0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed); mp_int *nonsquare_mod_p = mp_from_integer(2); - initialise_ecurve(&curve, p, d, a, nonsquare_mod_p, G_x, G_y, G_order); + initialise_ecurve(&curve, p, d, a, nonsquare_mod_p, + G_x, G_y, G_order, 3); mp_free(p); mp_free(d); mp_free(a); @@ -249,6 +284,43 @@ static struct ec_curve *ec_ed25519(void) return &curve; } +static struct ec_curve *ec_ed448(void) +{ + static struct ec_curve curve = { 0 }; + static bool initialised = false; + + if (!initialised) + { + mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + mp_int *d = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756); /* = p - 39081 */ + mp_int *a = MP_LITERAL(0x1); + mp_int *G_x = MP_LITERAL(0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e); + mp_int *G_y = MP_LITERAL(0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14); + mp_int *G_order = MP_LITERAL(0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3); + mp_int *nonsquare_mod_p = mp_from_integer(7); + initialise_ecurve(&curve, p, d, a, nonsquare_mod_p, + G_x, G_y, G_order, 2); + mp_free(p); + mp_free(d); + mp_free(a); + mp_free(G_x); + mp_free(G_y); + mp_free(G_order); + mp_free(nonsquare_mod_p); + + /* This curve doesn't need a name, because it's never used in + * any format that embeds the curve name */ + curve.name = NULL; + + curve.textname = "Ed448"; + + /* Now initialised, no need to do it again */ + initialised = true; + } + + return &curve; +} + /* ---------------------------------------------------------------------- * Public point from private */ @@ -260,6 +332,10 @@ struct ecsign_extra { /* These fields are used by the OpenSSH PEM format importer/exporter */ const unsigned char *oid; int oidlen; + + /* Some EdDSA instances prefix a string to all hash preimages, to + * disambiguate which signature variant they're being used with */ + ptrlen hash_prefix; }; WeierstrassPoint *ecdsa_public(mp_int *private_key, const ssh_keyalg *alg) @@ -293,9 +369,9 @@ static mp_int *eddsa_exponent_from_hash( mp_reduce_mod_2to(e, curve->fieldBits); /* - * Clear exactly three low bits. + * Clear a curve-specific number of low bits. */ - for (size_t bit = 0; bit < 3; bit++) + for (unsigned bit = 0; bit < curve->e.log2_cofactor; bit++) mp_set_bit(e, bit, 0); return e; @@ -334,16 +410,15 @@ static mp_int *BinarySource_get_mp_le(BinarySource *src) } #define get_mp_le(src) BinarySource_get_mp_le(BinarySource_UPCAST(src)) -static void BinarySink_put_mp_le_unsigned(BinarySink *bs, mp_int *x) +static void BinarySink_put_mp_le_fixedlen(BinarySink *bs, mp_int *x, + size_t bytes) { - size_t bytes = (mp_get_nbits(x) + 7) / 8; - put_uint32(bs, bytes); for (size_t i = 0; i < bytes; ++i) put_byte(bs, mp_get_byte(x, i)); } -#define put_mp_le_unsigned(bs, x) \ - BinarySink_put_mp_le_unsigned(BinarySink_UPCAST(bs), x) +#define put_mp_le_fixedlen(bs, x, bytes) \ + BinarySink_put_mp_le_fixedlen(BinarySink_UPCAST(bs), x, bytes) static WeierstrassPoint *ecdsa_decode( ptrlen encoded, const struct ec_curve *curve) @@ -462,20 +537,20 @@ static void BinarySink_put_wpoint( static EdwardsPoint *eddsa_decode(ptrlen encoded, const struct ec_curve *curve) { assert(curve->type == EC_EDWARDS); - assert(curve->fieldBits % 8 == 7); mp_int *y = mp_from_bytes_le(encoded); - if (mp_get_nbits(y) > curve->fieldBits+1) { + /* The topmost bit of the encoding isn't part of y, so it stores + * the bottom bit of x. Extract it, and zero that bit in y. */ + unsigned desired_x_parity = mp_get_bit(y, curve->fieldBytes * 8 - 1); + mp_set_bit(y, curve->fieldBytes * 8 - 1, 0); + + /* What's left should now be within the range of the curve's modulus */ + if (mp_cmp_hs(y, curve->p)) { mp_free(y); return NULL; } - /* The topmost bit of the encoding isn't part of y, so it stores - * the bottom bit of x. Extract it, and zero that bit in y. */ - unsigned desired_x_parity = mp_get_bit(y, curve->fieldBits); - mp_set_bit(y, curve->fieldBits, 0); - EdwardsPoint *P = ecc_edwards_point_new_from_y( curve->e.ec, y, desired_x_parity); mp_free(y); @@ -640,6 +715,27 @@ static char *ecdsa_cache_str(ssh_key *key) return toret; } +static key_components *ecdsa_components(ssh_key *key) +{ + struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); + key_components *kc = key_components_new(); + + key_components_add_text(kc, "key_type", "ECDSA"); + key_components_add_text(kc, "curve_name", ek->curve->textname); + + mp_int *x, *y; + ecc_weierstrass_get_affine(ek->publicKey, &x, &y); + key_components_add_mp(kc, "public_affine_x", x); + key_components_add_mp(kc, "public_affine_y", y); + mp_free(x); + mp_free(y); + + if (ek->privateKey) + key_components_add_mp(kc, "private_exponent", ek->privateKey); + + return kc; +} + static char *eddsa_cache_str(ssh_key *key) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); @@ -652,6 +748,27 @@ static char *eddsa_cache_str(ssh_key *key) return toret; } +static key_components *eddsa_components(ssh_key *key) +{ + struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); + key_components *kc = key_components_new(); + + key_components_add_text(kc, "key_type", "EdDSA"); + key_components_add_text(kc, "curve_name", ek->curve->textname); + + mp_int *x, *y; + ecc_edwards_get_affine(ek->publicKey, &x, &y); + key_components_add_mp(kc, "public_affine_x", x); + key_components_add_mp(kc, "public_affine_y", y); + mp_free(x); + mp_free(y); + + if (ek->privateKey) + key_components_add_mp(kc, "private_exponent", ek->privateKey); + + return kc; +} + static void ecdsa_public_blob(ssh_key *key, BinarySink *bs) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); @@ -684,7 +801,7 @@ static void eddsa_private_blob(ssh_key *key, BinarySink *bs) /* EdDSA stores the private key integer little-endian and unsigned */ assert(ek->privateKey); - put_mp_le_unsigned(bs, ek->privateKey); + put_mp_le_fixedlen(bs, ek->privateKey, ek->curve->fieldBytes); } static ssh_key *ecdsa_new_priv(const ssh_keyalg *alg, ptrlen pub, ptrlen priv) @@ -735,6 +852,10 @@ static ssh_key *eddsa_new_priv_openssh( * correct as well, otherwise the key we think we've imported * won't behave identically to the way OpenSSH would have treated * it. + * + * We assume that Ed448 will work the same way, as and when + * OpenSSH implements it, which at the time of writing this they + * had not. */ BinarySource subsrc[1]; BinarySource_BARE_INIT_PL(subsrc, privkey_extended_pl); @@ -772,7 +893,7 @@ static void eddsa_openssh_blob(ssh_key *key, BinarySink *bs) ptrlen pub = make_ptrlen(pub_sb->s + 4, pub_sb->len - 4); strbuf *priv_sb = strbuf_new_nm(); - put_mp_le_unsigned(priv_sb, ek->privateKey); + put_mp_le_fixedlen(priv_sb, ek->privateKey, ek->curve->fieldBytes); ptrlen priv = make_ptrlen(priv_sb->s + 4, priv_sb->len - 4); put_stringpl(bs, pub); @@ -929,6 +1050,7 @@ static mp_int *eddsa_signing_exponent_from_data( /* Hash (r || public key || message) */ unsigned char hash[MAX_HASH_LEN]; ssh_hash *h = ssh_hash_new(extra->hash); + put_datapl(h, extra->hash_prefix); put_datapl(h, r_encoded); put_epoint(h, ek->publicKey, ek->curve, true); /* omit string header */ put_datapl(h, data); @@ -1081,6 +1203,7 @@ static void eddsa_sign(ssh_key *key, ptrlen data, * generate the signature point r. */ h = ssh_hash_new(extra->hash); + put_datapl(h, extra->hash_prefix); put_data(h, hash + ek->curve->fieldBytes, extra->hash->hlen - ek->curve->fieldBytes); put_datapl(h, data); @@ -1124,120 +1247,131 @@ static void eddsa_sign(ssh_key *key, ptrlen data, mp_free(s); } -const struct ecsign_extra sign_extra_ed25519 = { +static const struct ecsign_extra sign_extra_ed25519 = { ec_ed25519, &ssh_sha512, - NULL, 0, + NULL, 0, PTRLEN_DECL_LITERAL(""), }; const ssh_keyalg ssh_ecdsa_ed25519 = { - eddsa_new_pub, - eddsa_new_priv, - eddsa_new_priv_openssh, - - eddsa_freekey, - ec_signkey_invalid, - eddsa_sign, - eddsa_verify, - eddsa_public_blob, - eddsa_private_blob, - eddsa_openssh_blob, - eddsa_cache_str, - - ec_shared_pubkey_bits, - - "ssh-ed25519", - "ssh-ed25519", - &sign_extra_ed25519, - 0, /* no supported flags */ + .new_pub = eddsa_new_pub, + .new_priv = eddsa_new_priv, + .new_priv_openssh = eddsa_new_priv_openssh, + .freekey = eddsa_freekey, + .invalid = ec_signkey_invalid, + .sign = eddsa_sign, + .verify = eddsa_verify, + .public_blob = eddsa_public_blob, + .private_blob = eddsa_private_blob, + .openssh_blob = eddsa_openssh_blob, + .cache_str = eddsa_cache_str, + .components = eddsa_components, + .pubkey_bits = ec_shared_pubkey_bits, + .ssh_id = "ssh-ed25519", + .cache_id = "ssh-ed25519", + .extra = &sign_extra_ed25519, +}; + +static const struct ecsign_extra sign_extra_ed448 = { + ec_ed448, &ssh_shake256_114bytes, + NULL, 0, PTRLEN_DECL_LITERAL("SigEd448\0\0"), +}; +const ssh_keyalg ssh_ecdsa_ed448 = { + .new_pub = eddsa_new_pub, + .new_priv = eddsa_new_priv, + .new_priv_openssh = eddsa_new_priv_openssh, + .freekey = eddsa_freekey, + .invalid = ec_signkey_invalid, + .sign = eddsa_sign, + .verify = eddsa_verify, + .public_blob = eddsa_public_blob, + .private_blob = eddsa_private_blob, + .openssh_blob = eddsa_openssh_blob, + .cache_str = eddsa_cache_str, + .components = eddsa_components, + .pubkey_bits = ec_shared_pubkey_bits, + .ssh_id = "ssh-ed448", + .cache_id = "ssh-ed448", + .extra = &sign_extra_ed448, }; /* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */ static const unsigned char nistp256_oid[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }; -const struct ecsign_extra sign_extra_nistp256 = { +static const struct ecsign_extra sign_extra_nistp256 = { ec_p256, &ssh_sha256, nistp256_oid, lenof(nistp256_oid), }; const ssh_keyalg ssh_ecdsa_nistp256 = { - ecdsa_new_pub, - ecdsa_new_priv, - ecdsa_new_priv_openssh, - - ecdsa_freekey, - ec_signkey_invalid, - ecdsa_sign, - ecdsa_verify, - ecdsa_public_blob, - ecdsa_private_blob, - ecdsa_openssh_blob, - ecdsa_cache_str, - - ec_shared_pubkey_bits, - - "ecdsa-sha2-nistp256", - "ecdsa-sha2-nistp256", - &sign_extra_nistp256, - 0, /* no supported flags */ + .new_pub = ecdsa_new_pub, + .new_priv = ecdsa_new_priv, + .new_priv_openssh = ecdsa_new_priv_openssh, + .freekey = ecdsa_freekey, + .invalid = ec_signkey_invalid, + .sign = ecdsa_sign, + .verify = ecdsa_verify, + .public_blob = ecdsa_public_blob, + .private_blob = ecdsa_private_blob, + .openssh_blob = ecdsa_openssh_blob, + .cache_str = ecdsa_cache_str, + .components = ecdsa_components, + .pubkey_bits = ec_shared_pubkey_bits, + .ssh_id = "ecdsa-sha2-nistp256", + .cache_id = "ecdsa-sha2-nistp256", + .extra = &sign_extra_nistp256, }; /* OID: 1.3.132.0.34 (secp384r1) */ static const unsigned char nistp384_oid[] = { 0x2b, 0x81, 0x04, 0x00, 0x22 }; -const struct ecsign_extra sign_extra_nistp384 = { +static const struct ecsign_extra sign_extra_nistp384 = { ec_p384, &ssh_sha384, nistp384_oid, lenof(nistp384_oid), }; const ssh_keyalg ssh_ecdsa_nistp384 = { - ecdsa_new_pub, - ecdsa_new_priv, - ecdsa_new_priv_openssh, - - ecdsa_freekey, - ec_signkey_invalid, - ecdsa_sign, - ecdsa_verify, - ecdsa_public_blob, - ecdsa_private_blob, - ecdsa_openssh_blob, - ecdsa_cache_str, - - ec_shared_pubkey_bits, - - "ecdsa-sha2-nistp384", - "ecdsa-sha2-nistp384", - &sign_extra_nistp384, - 0, /* no supported flags */ + .new_pub = ecdsa_new_pub, + .new_priv = ecdsa_new_priv, + .new_priv_openssh = ecdsa_new_priv_openssh, + .freekey = ecdsa_freekey, + .invalid = ec_signkey_invalid, + .sign = ecdsa_sign, + .verify = ecdsa_verify, + .public_blob = ecdsa_public_blob, + .private_blob = ecdsa_private_blob, + .openssh_blob = ecdsa_openssh_blob, + .cache_str = ecdsa_cache_str, + .components = ecdsa_components, + .pubkey_bits = ec_shared_pubkey_bits, + .ssh_id = "ecdsa-sha2-nistp384", + .cache_id = "ecdsa-sha2-nistp384", + .extra = &sign_extra_nistp384, }; /* OID: 1.3.132.0.35 (secp521r1) */ static const unsigned char nistp521_oid[] = { 0x2b, 0x81, 0x04, 0x00, 0x23 }; -const struct ecsign_extra sign_extra_nistp521 = { +static const struct ecsign_extra sign_extra_nistp521 = { ec_p521, &ssh_sha512, nistp521_oid, lenof(nistp521_oid), }; const ssh_keyalg ssh_ecdsa_nistp521 = { - ecdsa_new_pub, - ecdsa_new_priv, - ecdsa_new_priv_openssh, - - ecdsa_freekey, - ec_signkey_invalid, - ecdsa_sign, - ecdsa_verify, - ecdsa_public_blob, - ecdsa_private_blob, - ecdsa_openssh_blob, - ecdsa_cache_str, - - ec_shared_pubkey_bits, - - "ecdsa-sha2-nistp521", - "ecdsa-sha2-nistp521", - &sign_extra_nistp521, - 0, /* no supported flags */ + .new_pub = ecdsa_new_pub, + .new_priv = ecdsa_new_priv, + .new_priv_openssh = ecdsa_new_priv_openssh, + .freekey = ecdsa_freekey, + .invalid = ec_signkey_invalid, + .sign = ecdsa_sign, + .verify = ecdsa_verify, + .public_blob = ecdsa_public_blob, + .private_blob = ecdsa_private_blob, + .openssh_blob = ecdsa_openssh_blob, + .cache_str = ecdsa_cache_str, + .components = ecdsa_components, + .pubkey_bits = ec_shared_pubkey_bits, + .ssh_id = "ecdsa-sha2-nistp521", + .cache_id = "ecdsa-sha2-nistp521", + .extra = &sign_extra_nistp521, }; /* ---------------------------------------------------------------------- @@ -1365,26 +1499,18 @@ static mp_int *ssh_ecdhkex_m_getkey(ecdh_key *dh, ptrlen remoteKey) * will be reduced mod p. */ mp_reduce_mod_2to(remote_x, dh->curve->fieldBits); - if (mp_eq_integer(remote_x, 0)) { - /* - * The libssh spec for Curve25519 key exchange says that - * 'every possible public key maps to a valid ECC Point' and - * therefore no validation needs to be done on the server's - * provided x-coordinate. However, I don't believe it: an - * x-coordinate of zero doesn't work sensibly, because you end - * up dividing by zero in the doubling formula - * (x+1)^2(x-1)^2/(4(x^3+ax^2+x)). (Put another way, although - * that point P is not the _identity_ of the curve, it is a - * torsion point such that 2P is the identity.) - */ - mp_free(remote_x); - return NULL; - } MontgomeryPoint *remote_p = ecc_montgomery_point_new( dh->curve->m.mc, remote_x); mp_free(remote_x); MontgomeryPoint *p = ecc_montgomery_multiply(remote_p, dh->private); + + if (ecc_montgomery_is_identity(p)) { + ecc_montgomery_point_free(remote_p); + ecc_montgomery_point_free(p); + return NULL; + } + mp_int *x; ecc_montgomery_get_affine(p, &x); @@ -1445,11 +1571,28 @@ static const struct eckex_extra kex_extra_curve25519 = { ssh_ecdhkex_m_getkey, }; const ssh_kex ssh_ec_kex_curve25519 = { + "curve25519-sha256", NULL, KEXTYPE_ECDH, + &ssh_sha256, &kex_extra_curve25519, +}; +/* Pre-RFC alias */ +const ssh_kex ssh_ec_kex_curve25519_libssh = { "curve25519-sha256@libssh.org", NULL, KEXTYPE_ECDH, &ssh_sha256, &kex_extra_curve25519, }; -const struct eckex_extra kex_extra_nistp256 = { +static const struct eckex_extra kex_extra_curve448 = { + ec_curve448, + ssh_ecdhkex_m_setup, + ssh_ecdhkex_m_cleanup, + ssh_ecdhkex_m_getpublic, + ssh_ecdhkex_m_getkey, +}; +const ssh_kex ssh_ec_kex_curve448 = { + "curve448-sha512", NULL, KEXTYPE_ECDH, + &ssh_sha512, &kex_extra_curve448, +}; + +static const struct eckex_extra kex_extra_nistp256 = { ec_p256, ssh_ecdhkex_w_setup, ssh_ecdhkex_w_cleanup, @@ -1461,7 +1604,7 @@ const ssh_kex ssh_ec_kex_nistp256 = { &ssh_sha256, &kex_extra_nistp256, }; -const struct eckex_extra kex_extra_nistp384 = { +static const struct eckex_extra kex_extra_nistp384 = { ec_p384, ssh_ecdhkex_w_setup, ssh_ecdhkex_w_cleanup, @@ -1473,7 +1616,7 @@ const ssh_kex ssh_ec_kex_nistp384 = { &ssh_sha384, &kex_extra_nistp384, }; -const struct eckex_extra kex_extra_nistp521 = { +static const struct eckex_extra kex_extra_nistp521 = { ec_p521, ssh_ecdhkex_w_setup, ssh_ecdhkex_w_cleanup, @@ -1486,7 +1629,9 @@ const ssh_kex ssh_ec_kex_nistp521 = { }; static const ssh_kex *const ec_kex_list[] = { + &ssh_ec_kex_curve448, &ssh_ec_kex_curve25519, + &ssh_ec_kex_curve25519_libssh, &ssh_ec_kex_nistp256, &ssh_ec_kex_nistp384, &ssh_ec_kex_nistp521, @@ -1532,6 +1677,9 @@ const unsigned char *ec_alg_oid(const ssh_keyalg *alg, const int ec_nist_curve_lengths[] = { 256, 384, 521 }; const int n_ec_nist_curve_lengths = lenof(ec_nist_curve_lengths); +const int ec_ed_curve_lengths[] = { 255, 448 }; +const int n_ec_ed_curve_lengths = lenof(ec_ed_curve_lengths); + bool ec_nist_alg_and_curve_by_bits( int bits, const struct ec_curve **curve, const ssh_keyalg **alg) { @@ -1549,7 +1697,8 @@ bool ec_ed_alg_and_curve_by_bits( int bits, const struct ec_curve **curve, const ssh_keyalg **alg) { switch (bits) { - case 256: *alg = &ssh_ecdsa_ed25519; break; + case 255: case 256: *alg = &ssh_ecdsa_ed25519; break; + case 448: *alg = &ssh_ecdsa_ed448; break; default: return false; } *curve = ((struct ecsign_extra *)(*alg)->extra)->curve(); diff --git a/sshecdsag.c b/sshecdsag.c index 37048ea..28a723b 100644 --- a/sshecdsag.c +++ b/sshecdsag.c @@ -3,10 +3,10 @@ */ #include "ssh.h" +#include "sshkeygen.h" #include "mpint.h" -int ecdsa_generate(struct ecdsa_key *ek, int bits, - progfn_t pfn, void *pfnparam) +int ecdsa_generate(struct ecdsa_key *ek, int bits) { if (!ec_nist_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt)) return 0; @@ -20,8 +20,7 @@ int ecdsa_generate(struct ecdsa_key *ek, int bits, return 1; } -int eddsa_generate(struct eddsa_key *ek, int bits, - progfn_t pfn, void *pfnparam) +int eddsa_generate(struct eddsa_key *ek, int bits) { if (!ec_ed_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt)) return 0; diff --git a/sshgssc.c b/sshgssc.c index adbb437..d9f62c3 100644 --- a/sshgssc.c +++ b/sshgssc.c @@ -25,7 +25,7 @@ static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib, gss_buffer_desc host_buf; char *pStr; - pStr = dupcat("host@", host, NULL); + pStr = dupcat("host@", host); host_buf.value = pStr; host_buf.length = strlen(pStr); diff --git a/sshhmac.c b/sshhmac.c index 232d768..8f18eb2 100644 --- a/sshhmac.c +++ b/sshhmac.c @@ -8,7 +8,6 @@ struct hmac { const ssh_hashalg *hashalg; ssh_hash *h_outer, *h_inner, *h_live; - bool keyed; uint8_t *digest; strbuf *text_name; ssh2_mac mac; @@ -30,7 +29,6 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) ctx->hashalg = ssh_hash_alg(ctx->h_outer); ctx->h_inner = ssh_hash_new(ctx->hashalg); ctx->h_live = ssh_hash_new(ctx->hashalg); - ctx->keyed = false; /* * HMAC is not well defined as a wrapper on an absolutely general @@ -44,7 +42,7 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) ctx->digest = snewn(ctx->hashalg->hlen, uint8_t); ctx->text_name = strbuf_new(); - strbuf_catf(ctx->text_name, "HMAC-%s", + strbuf_catf(ctx->text_name, "HMAC-%s%s", ctx->hashalg->text_basename, extra->suffix); if (extra->annotation || ctx->hashalg->annotation) { strbuf_catf(ctx->text_name, " ("); @@ -92,18 +90,6 @@ static void hmac_key(ssh2_mac *mac, ptrlen key) size_t klen; strbuf *sb = NULL; - if (ctx->keyed) { - /* - * If we've already been keyed, throw away the existing hash - * objects and make a fresh pair to put the new key in. - */ - ssh_hash_free(ctx->h_outer); - ssh_hash_free(ctx->h_inner); - ctx->h_outer = ssh_hash_new(ctx->hashalg); - ctx->h_inner = ssh_hash_new(ctx->hashalg); - } - ctx->keyed = true; - if (key.len > ctx->hashalg->blocklen) { /* * RFC 2104 section 2: if the key exceeds the block length of @@ -112,11 +98,7 @@ static void hmac_key(ssh2_mac *mac, ptrlen key) */ sb = strbuf_new_nm(); strbuf_append(sb, ctx->hashalg->hlen); - - ssh_hash *htmp = ssh_hash_new(ctx->hashalg); - put_datapl(htmp, key); - ssh_hash_final(htmp, sb->u); - + hash_simple(ctx->hashalg, key, sb->u); kp = sb->u; klen = sb->len; } else { @@ -127,18 +109,13 @@ static void hmac_key(ssh2_mac *mac, ptrlen key) klen = key.len; } - if (ctx->h_outer) - ssh_hash_free(ctx->h_outer); - if (ctx->h_inner) - ssh_hash_free(ctx->h_inner); - - ctx->h_outer = ssh_hash_new(ctx->hashalg); + ssh_hash_reset(ctx->h_outer); for (size_t i = 0; i < klen; i++) put_byte(ctx->h_outer, PAD_OUTER ^ kp[i]); for (size_t i = klen; i < ctx->hashalg->blocklen; i++) put_byte(ctx->h_outer, PAD_OUTER); - ctx->h_inner = ssh_hash_new(ctx->hashalg); + ssh_hash_reset(ctx->h_inner); for (size_t i = 0; i < klen; i++) put_byte(ctx->h_inner, PAD_INNER ^ kp[i]); for (size_t i = klen; i < ctx->hashalg->blocklen; i++) @@ -151,10 +128,7 @@ static void hmac_key(ssh2_mac *mac, ptrlen key) static void hmac_start(ssh2_mac *mac) { struct hmac *ctx = container_of(mac, struct hmac, mac); - - ssh_hash_free(ctx->h_live); - ctx->h_live = ssh_hash_copy(ctx->h_inner); - BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live); + ssh_hash_copyfrom(ctx->h_live, ctx->h_inner); } static void hmac_genresult(ssh2_mac *mac, unsigned char *output) @@ -162,11 +136,10 @@ static void hmac_genresult(ssh2_mac *mac, unsigned char *output) struct hmac *ctx = container_of(mac, struct hmac, mac); ssh_hash *htmp; - /* Leave h_live in place, so that the SSH-2 BPP can continue - * regenerating test results from different-length prefixes of the - * packet */ - htmp = ssh_hash_copy(ctx->h_live); - ssh_hash_final(htmp, ctx->digest); + /* Leave h_live and h_outer in place, so that the SSH-2 BPP can + * continue regenerating test results from different-length + * prefixes of the packet */ + ssh_hash_digest_nondestructive(ctx->h_live, ctx->digest); htmp = ssh_hash_copy(ctx->h_outer); put_data(htmp, ctx->digest, ctx->hashalg->hlen); @@ -187,58 +160,98 @@ static const char *hmac_text_name(ssh2_mac *mac) return ctx->text_name->s; } -const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" }; +static const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" }; const ssh2_macalg ssh_hmac_sha256 = { - hmac_new, hmac_free, hmac_key, - hmac_start, hmac_genresult, hmac_text_name, - "hmac-sha2-256", "hmac-sha2-256-etm@openssh.com", - 32, 32, &ssh_hmac_sha256_extra, + .new = hmac_new, + .free = hmac_free, + .setkey = hmac_key, + .start = hmac_start, + .genresult = hmac_genresult, + .text_name = hmac_text_name, + .name = "hmac-sha2-256", + .etm_name = "hmac-sha2-256-etm@openssh.com", + .len = 32, + .keylen = 32, + .extra = &ssh_hmac_sha256_extra, }; -const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" }; +static const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" }; const ssh2_macalg ssh_hmac_md5 = { - hmac_new, hmac_free, hmac_key, - hmac_start, hmac_genresult, hmac_text_name, - "hmac-md5", "hmac-md5-etm@openssh.com", - 16, 16, &ssh_hmac_md5_extra, + .new = hmac_new, + .free = hmac_free, + .setkey = hmac_key, + .start = hmac_start, + .genresult = hmac_genresult, + .text_name = hmac_text_name, + .name = "hmac-md5", + .etm_name = "hmac-md5-etm@openssh.com", + .len = 16, + .keylen = 16, + .extra = &ssh_hmac_md5_extra, }; -const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" }; +static const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" }; const ssh2_macalg ssh_hmac_sha1 = { - hmac_new, hmac_free, hmac_key, - hmac_start, hmac_genresult, hmac_text_name, - "hmac-sha1", "hmac-sha1-etm@openssh.com", - 20, 20, &ssh_hmac_sha1_extra, + .new = hmac_new, + .free = hmac_free, + .setkey = hmac_key, + .start = hmac_start, + .genresult = hmac_genresult, + .text_name = hmac_text_name, + .name = "hmac-sha1", + .etm_name = "hmac-sha1-etm@openssh.com", + .len = 20, + .keylen = 20, + .extra = &ssh_hmac_sha1_extra, }; -const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" }; +static const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" }; const ssh2_macalg ssh_hmac_sha1_96 = { - hmac_new, hmac_free, hmac_key, - hmac_start, hmac_genresult, hmac_text_name, - "hmac-sha1-96", "hmac-sha1-96-etm@openssh.com", - 12, 20, &ssh_hmac_sha1_96_extra, + .new = hmac_new, + .free = hmac_free, + .setkey = hmac_key, + .start = hmac_start, + .genresult = hmac_genresult, + .text_name = hmac_text_name, + .name = "hmac-sha1-96", + .etm_name = "hmac-sha1-96-etm@openssh.com", + .len = 12, + .keylen = 20, + .extra = &ssh_hmac_sha1_96_extra, }; -const struct hmac_extra ssh_hmac_sha1_buggy_extra = { - &ssh_sha1, " (bug-compatible)" +static const struct hmac_extra ssh_hmac_sha1_buggy_extra = { + &ssh_sha1, "", "bug-compatible" }; const ssh2_macalg ssh_hmac_sha1_buggy = { - hmac_new, hmac_free, hmac_key, - hmac_start, hmac_genresult, hmac_text_name, - "hmac-sha1", NULL, - 20, 16, &ssh_hmac_sha1_buggy_extra, + .new = hmac_new, + .free = hmac_free, + .setkey = hmac_key, + .start = hmac_start, + .genresult = hmac_genresult, + .text_name = hmac_text_name, + .name = "hmac-sha1", + .len = 20, + .keylen = 16, + .extra = &ssh_hmac_sha1_buggy_extra, }; -const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = { - &ssh_sha1, "-96 (bug-compatible)" +static const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = { + &ssh_sha1, "-96", "bug-compatible" }; const ssh2_macalg ssh_hmac_sha1_96_buggy = { - hmac_new, hmac_free, hmac_key, - hmac_start, hmac_genresult, hmac_text_name, - "hmac-sha1-96", NULL, - 12, 16, &ssh_hmac_sha1_96_buggy_extra, + .new = hmac_new, + .free = hmac_free, + .setkey = hmac_key, + .start = hmac_start, + .genresult = hmac_genresult, + .text_name = hmac_text_name, + .name = "hmac-sha1-96", + .len = 12, + .keylen = 16, + .extra = &ssh_hmac_sha1_96_buggy_extra, }; diff --git a/sshkeygen.h b/sshkeygen.h new file mode 100644 index 0000000..971a363 --- /dev/null +++ b/sshkeygen.h @@ -0,0 +1,292 @@ +/* + * sshkeygen.h: routines used internally to key generation. + */ + +/* ---------------------------------------------------------------------- + * A table of all the primes that fit in a 16-bit integer. Call + * init_primes_array to make sure it's been initialised. + */ + +#define NSMALLPRIMES 6542 /* number of primes < 65536 */ +extern const unsigned short *const smallprimes; +void init_smallprimes(void); + +/* ---------------------------------------------------------------------- + * A system for making up random candidate integers during prime + * generation. This unconditionally ensures that the numbers have the + * right number of bits and are not divisible by any prime in the + * smallprimes[] array above. It can also impose further constraints, + * as documented below. + */ +typedef struct PrimeCandidateSource PrimeCandidateSource; + +/* + * pcs_new: you say how many bits you want the prime to have (with the + * usual semantics that an n-bit number is in the range [2^{n-1},2^n)) + * and also optionally specify what you want its topmost 'nfirst' bits + * to be. + * + * (The 'first' system is used for RSA keys, where you need to arrange + * that the product of your two primes is in a more tightly + * constrained range than the factor of 4 you'd get by just generating + * two (n/2)-bit primes and multiplying them.) + */ +PrimeCandidateSource *pcs_new(unsigned bits); +PrimeCandidateSource *pcs_new_with_firstbits(unsigned bits, + unsigned first, unsigned nfirst); + +/* Insist that generated numbers must be congruent to 'res' mod 'mod' */ +void pcs_require_residue(PrimeCandidateSource *s, mp_int *mod, mp_int *res); + +/* Convenience wrapper for the common case where res = 1 */ +void pcs_require_residue_1(PrimeCandidateSource *s, mp_int *mod); + +/* Same as pcs_require_residue_1, but also records that the modulus is + * known to be prime */ +void pcs_require_residue_1_mod_prime(PrimeCandidateSource *s, mp_int *mod); + +/* Insist that generated numbers must _not_ be congruent to 'res' mod + * 'mod'. This is used to avoid being 1 mod the RSA public exponent, + * which is small, so it only needs ordinary integer parameters. */ +void pcs_avoid_residue_small(PrimeCandidateSource *s, + unsigned mod, unsigned res); + +/* Exclude any prime that has no chance of being a Sophie Germain prime. */ +void pcs_try_sophie_germain(PrimeCandidateSource *s); + +/* Mark a PrimeCandidateSource as one-shot, so that the prime generation + * function will return NULL if an attempt fails, rather than looping. */ +void pcs_set_oneshot(PrimeCandidateSource *s); + +/* Prepare a PrimeCandidateSource to actually generate numbers. This + * function does last-minute computation that has to be delayed until + * all constraints have been input. */ +void pcs_ready(PrimeCandidateSource *s); + +/* Actually generate a candidate integer. You must free the result, of + * course. */ +mp_int *pcs_generate(PrimeCandidateSource *s); + +/* Free a PrimeCandidateSource. */ +void pcs_free(PrimeCandidateSource *s); + +/* Return some internal fields of the PCS. Used by testcrypt for + * unit-testing this system. */ +void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out, + mp_int **factor_out, mp_int **addend_out); + +/* Query functions for primegen to use */ +unsigned pcs_get_bits(PrimeCandidateSource *pcs); +unsigned pcs_get_bits_remaining(PrimeCandidateSource *pcs); +mp_int *pcs_get_upper_bound(PrimeCandidateSource *pcs); +mp_int **pcs_get_known_prime_factors(PrimeCandidateSource *pcs, size_t *nout); + +/* ---------------------------------------------------------------------- + * A system for doing Miller-Rabin probabilistic primality tests. + * These benefit from having set up some context beforehand, if you're + * going to do more than one of them on the same candidate prime, so + * we declare an object type here to store that context. + */ + +typedef struct MillerRabin MillerRabin; + +/* Make and free a Miller-Rabin context. */ +MillerRabin *miller_rabin_new(mp_int *p); +void miller_rabin_free(MillerRabin *mr); + +/* Perform a single Miller-Rabin test, using a random witness value. */ +bool miller_rabin_test_random(MillerRabin *mr); + +/* Suggest how many tests are needed to make it sufficiently unlikely + * that a composite number will pass them all */ +unsigned miller_rabin_checks_needed(unsigned bits); + +/* An extension to the M-R test, which iterates until it either finds + * a witness value that is potentially a primitive root, or one + * that proves the number to be composite. */ +mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr); + +/* ---------------------------------------------------------------------- + * A system for proving numbers to be prime, using the Pocklington + * test, which requires knowing a partial factorisation of p-1 + * (specifically, factors whose product is at least cbrt(p)) and a + * primitive root. + * + * The API consists of instantiating a 'Pockle' object, which + * internally stores a list of numbers you've already convinced it is + * prime, and can accept further primes if you give a satisfactory + * certificate of their primality based on primes it already knows + * about. + */ + +typedef struct Pockle Pockle; + +/* In real use, you only really need to know whether the Pockle + * successfully accepted your prime. But for testcrypt, it's useful to + * expose many different failure modes so we can try to provoke them + * all in unit tests and check they're working. */ +#define POCKLE_STATUSES(X) \ + X(POCKLE_OK) \ + X(POCKLE_SMALL_PRIME_NOT_SMALL) \ + X(POCKLE_SMALL_PRIME_NOT_PRIME) \ + X(POCKLE_PRIME_SMALLER_THAN_2) \ + X(POCKLE_FACTOR_NOT_KNOWN_PRIME) \ + X(POCKLE_FACTOR_NOT_A_FACTOR) \ + X(POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL) \ + X(POCKLE_FERMAT_TEST_FAILED) \ + X(POCKLE_DISCRIMINANT_IS_SQUARE) \ + X(POCKLE_WITNESS_POWER_IS_1) \ + X(POCKLE_WITNESS_POWER_NOT_COPRIME) \ + /* end of list */ + +#define DEFINE_ENUM(id) id, +typedef enum PockleStatus { POCKLE_STATUSES(DEFINE_ENUM) } PockleStatus; +#undef DEFINE_ENUM + +/* Make a new empty Pockle, containing no primes. */ +Pockle *pockle_new(void); + +/* Insert a prime below 2^32 into the Pockle. No evidence is required: + * Pockle will check it itself. */ +PockleStatus pockle_add_small_prime(Pockle *pockle, mp_int *p); + +/* Insert a general prime into the Pockle. You must provide a list of + * prime factors of p-1, whose product exceeds the cube root of p, and + * also a primitive root mod p. */ +PockleStatus pockle_add_prime(Pockle *pockle, mp_int *p, + mp_int **factors, size_t nfactors, + mp_int *primitive_root); + +/* If you call pockle_mark, and later pass the returned value to + * pockle_release, it will free all the primes that were added to the + * Pockle between those two calls. Useful in recursive algorithms, to + * stop the Pockle growing unboundedly if the recursion keeps having + * to backtrack. */ +size_t pockle_mark(Pockle *pockle); +void pockle_release(Pockle *pockle, size_t mark); + +/* Free a Pockle. */ +void pockle_free(Pockle *pockle); + +/* Generate a certificate of primality for a prime already known to + * the Pockle, in a format acceptable to Math::Prime::Util. */ +strbuf *pockle_mpu(Pockle *pockle, mp_int *p); + +/* ---------------------------------------------------------------------- + * Callback API that allows key generation to report progress to its + * caller. + */ + +typedef struct ProgressReceiverVtable ProgressReceiverVtable; +typedef struct ProgressReceiver ProgressReceiver; +typedef union ProgressPhase ProgressPhase; + +union ProgressPhase { + int n; + void *p; +}; + +struct ProgressReceiver { + const ProgressReceiverVtable *vt; +}; + +struct ProgressReceiverVtable { + ProgressPhase (*add_linear)(ProgressReceiver *prog, double overall_cost); + ProgressPhase (*add_probabilistic)(ProgressReceiver *prog, + double cost_per_attempt, + double attempt_probability); + void (*ready)(ProgressReceiver *prog); + void (*start_phase)(ProgressReceiver *prog, ProgressPhase phase); + void (*report)(ProgressReceiver *prog, double progress); + void (*report_attempt)(ProgressReceiver *prog); + void (*report_phase_complete)(ProgressReceiver *prog); +}; + +static inline ProgressPhase progress_add_linear(ProgressReceiver *prog, + double c) +{ return prog->vt->add_linear(prog, c); } +static inline ProgressPhase progress_add_probabilistic(ProgressReceiver *prog, + double c, double p) +{ return prog->vt->add_probabilistic(prog, c, p); } +static inline void progress_ready(ProgressReceiver *prog) +{ prog->vt->ready(prog); } +static inline void progress_start_phase( + ProgressReceiver *prog, ProgressPhase phase) +{ prog->vt->start_phase(prog, phase); } +static inline void progress_report(ProgressReceiver *prog, double progress) +{ prog->vt->report(prog, progress); } +static inline void progress_report_attempt(ProgressReceiver *prog) +{ prog->vt->report_attempt(prog); } +static inline void progress_report_phase_complete(ProgressReceiver *prog) +{ prog->vt->report_phase_complete(prog); } + +ProgressPhase null_progress_add_linear( + ProgressReceiver *prog, double c); +ProgressPhase null_progress_add_probabilistic( + ProgressReceiver *prog, double c, double p); +void null_progress_ready(ProgressReceiver *prog); +void null_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase); +void null_progress_report(ProgressReceiver *prog, double progress); +void null_progress_report_attempt(ProgressReceiver *prog); +void null_progress_report_phase_complete(ProgressReceiver *prog); +extern const ProgressReceiverVtable null_progress_vt; + +/* A helper function for dreaming up progress cost estimates. */ +double estimate_modexp_cost(unsigned bits); + +/* ---------------------------------------------------------------------- + * The top-level API for generating primes. + */ + +typedef struct PrimeGenerationPolicy PrimeGenerationPolicy; +typedef struct PrimeGenerationContext PrimeGenerationContext; + +struct PrimeGenerationContext { + const PrimeGenerationPolicy *vt; +}; + +struct PrimeGenerationPolicy { + ProgressPhase (*add_progress_phase)(const PrimeGenerationPolicy *policy, + ProgressReceiver *prog, unsigned bits); + PrimeGenerationContext *(*new_context)( + const PrimeGenerationPolicy *policy); + void (*free_context)(PrimeGenerationContext *ctx); + mp_int *(*generate)( + PrimeGenerationContext *ctx, + PrimeCandidateSource *pcs, ProgressReceiver *prog); + strbuf *(*mpu_certificate)(PrimeGenerationContext *ctx, mp_int *p); + + const void *extra; /* additional data a particular impl might need */ +}; + +static inline ProgressPhase primegen_add_progress_phase( + PrimeGenerationContext *ctx, ProgressReceiver *prog, unsigned bits) +{ return ctx->vt->add_progress_phase(ctx->vt, prog, bits); } +static inline PrimeGenerationContext *primegen_new_context( + const PrimeGenerationPolicy *policy) +{ return policy->new_context(policy); } +static inline void primegen_free_context(PrimeGenerationContext *ctx) +{ ctx->vt->free_context(ctx); } +static inline mp_int *primegen_generate( + PrimeGenerationContext *ctx, + PrimeCandidateSource *pcs, ProgressReceiver *prog) +{ return ctx->vt->generate(ctx, pcs, prog); } +static inline strbuf *primegen_mpu_certificate( + PrimeGenerationContext *ctx, mp_int *p) +{ return ctx->vt->mpu_certificate(ctx, p); } + +extern const PrimeGenerationPolicy primegen_probabilistic; +extern const PrimeGenerationPolicy primegen_provable_fast; +extern const PrimeGenerationPolicy primegen_provable_maurer_simple; +extern const PrimeGenerationPolicy primegen_provable_maurer_complex; + +/* ---------------------------------------------------------------------- + * The overall top-level API for generating entire key pairs. + */ + +int rsa_generate(RSAKey *key, int bits, bool strong, + PrimeGenerationContext *pgc, ProgressReceiver *prog); +int dsa_generate(struct dss_key *key, int bits, PrimeGenerationContext *pgc, + ProgressReceiver *prog); +int ecdsa_generate(struct ecdsa_key *key, int bits); +int eddsa_generate(struct eddsa_key *key, int bits); diff --git a/sshmd5.c b/sshmd5.c index 04de681..9155c99 100644 --- a/sshmd5.c +++ b/sshmd5.c @@ -1,275 +1,245 @@ -#include -#include "ssh.h" - /* * MD5 implementation for PuTTY. Written directly from the spec by * Simon Tatham. */ -typedef struct { - uint32_t h[4]; -} MD5_Core_State; +#include +#include "ssh.h" -struct MD5Context { - MD5_Core_State core; - unsigned char block[64]; - int blkused; - uint64_t len; - BinarySink_IMPLEMENTATION; +static const uint32_t md5_initial_state[] = { + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, }; -/* ---------------------------------------------------------------------- - * Core MD5 algorithm: processes 16-word blocks into a message digest. - */ - -#define F(x,y,z) ( ((x) & (y)) | ((~(x)) & (z)) ) -#define G(x,y,z) ( ((x) & (z)) | ((~(z)) & (y)) ) -#define H(x,y,z) ( (x) ^ (y) ^ (z) ) -#define I(x,y,z) ( (y) ^ ( (x) | ~(z) ) ) - -#define rol(x,y) ( ((x) << (y)) | (((uint32_t)x) >> (32-y)) ) +static const struct md5_round_constant { + uint32_t addition, rotation, msg_index; +} md5_round_constants[] = { + { 0xd76aa478, 7, 0 }, { 0xe8c7b756, 12, 1 }, + { 0x242070db, 17, 2 }, { 0xc1bdceee, 22, 3 }, + { 0xf57c0faf, 7, 4 }, { 0x4787c62a, 12, 5 }, + { 0xa8304613, 17, 6 }, { 0xfd469501, 22, 7 }, + { 0x698098d8, 7, 8 }, { 0x8b44f7af, 12, 9 }, + { 0xffff5bb1, 17, 10 }, { 0x895cd7be, 22, 11 }, + { 0x6b901122, 7, 12 }, { 0xfd987193, 12, 13 }, + { 0xa679438e, 17, 14 }, { 0x49b40821, 22, 15 }, + { 0xf61e2562, 5, 1 }, { 0xc040b340, 9, 6 }, + { 0x265e5a51, 14, 11 }, { 0xe9b6c7aa, 20, 0 }, + { 0xd62f105d, 5, 5 }, { 0x02441453, 9, 10 }, + { 0xd8a1e681, 14, 15 }, { 0xe7d3fbc8, 20, 4 }, + { 0x21e1cde6, 5, 9 }, { 0xc33707d6, 9, 14 }, + { 0xf4d50d87, 14, 3 }, { 0x455a14ed, 20, 8 }, + { 0xa9e3e905, 5, 13 }, { 0xfcefa3f8, 9, 2 }, + { 0x676f02d9, 14, 7 }, { 0x8d2a4c8a, 20, 12 }, + { 0xfffa3942, 4, 5 }, { 0x8771f681, 11, 8 }, + { 0x6d9d6122, 16, 11 }, { 0xfde5380c, 23, 14 }, + { 0xa4beea44, 4, 1 }, { 0x4bdecfa9, 11, 4 }, + { 0xf6bb4b60, 16, 7 }, { 0xbebfbc70, 23, 10 }, + { 0x289b7ec6, 4, 13 }, { 0xeaa127fa, 11, 0 }, + { 0xd4ef3085, 16, 3 }, { 0x04881d05, 23, 6 }, + { 0xd9d4d039, 4, 9 }, { 0xe6db99e5, 11, 12 }, + { 0x1fa27cf8, 16, 15 }, { 0xc4ac5665, 23, 2 }, + { 0xf4292244, 6, 0 }, { 0x432aff97, 10, 7 }, + { 0xab9423a7, 15, 14 }, { 0xfc93a039, 21, 5 }, + { 0x655b59c3, 6, 12 }, { 0x8f0ccc92, 10, 3 }, + { 0xffeff47d, 15, 10 }, { 0x85845dd1, 21, 1 }, + { 0x6fa87e4f, 6, 8 }, { 0xfe2ce6e0, 10, 15 }, + { 0xa3014314, 15, 6 }, { 0x4e0811a1, 21, 13 }, + { 0xf7537e82, 6, 4 }, { 0xbd3af235, 10, 11 }, + { 0x2ad7d2bb, 15, 2 }, { 0xeb86d391, 21, 9 }, +}; -#define subround(f,w,x,y,z,k,s,ti) \ - w = x + rol(w + f(x,y,z) + block[k] + ti, s) +typedef struct md5_block md5_block; +struct md5_block { + uint8_t block[64]; + size_t used; + uint64_t len; +}; -static void MD5_Core_Init(MD5_Core_State * s) +static inline void md5_block_setup(md5_block *blk) { - s->h[0] = 0x67452301; - s->h[1] = 0xefcdab89; - s->h[2] = 0x98badcfe; - s->h[3] = 0x10325476; + blk->used = 0; + blk->len = 0; } -static void MD5_Block(MD5_Core_State *s, uint32_t *block) +static inline bool md5_block_write( + md5_block *blk, const void **vdata, size_t *len) { - uint32_t a, b, c, d; - - a = s->h[0]; - b = s->h[1]; - c = s->h[2]; - d = s->h[3]; - - subround(F, a, b, c, d, 0, 7, 0xd76aa478); - subround(F, d, a, b, c, 1, 12, 0xe8c7b756); - subround(F, c, d, a, b, 2, 17, 0x242070db); - subround(F, b, c, d, a, 3, 22, 0xc1bdceee); - subround(F, a, b, c, d, 4, 7, 0xf57c0faf); - subround(F, d, a, b, c, 5, 12, 0x4787c62a); - subround(F, c, d, a, b, 6, 17, 0xa8304613); - subround(F, b, c, d, a, 7, 22, 0xfd469501); - subround(F, a, b, c, d, 8, 7, 0x698098d8); - subround(F, d, a, b, c, 9, 12, 0x8b44f7af); - subround(F, c, d, a, b, 10, 17, 0xffff5bb1); - subround(F, b, c, d, a, 11, 22, 0x895cd7be); - subround(F, a, b, c, d, 12, 7, 0x6b901122); - subround(F, d, a, b, c, 13, 12, 0xfd987193); - subround(F, c, d, a, b, 14, 17, 0xa679438e); - subround(F, b, c, d, a, 15, 22, 0x49b40821); - subround(G, a, b, c, d, 1, 5, 0xf61e2562); - subround(G, d, a, b, c, 6, 9, 0xc040b340); - subround(G, c, d, a, b, 11, 14, 0x265e5a51); - subround(G, b, c, d, a, 0, 20, 0xe9b6c7aa); - subround(G, a, b, c, d, 5, 5, 0xd62f105d); - subround(G, d, a, b, c, 10, 9, 0x02441453); - subround(G, c, d, a, b, 15, 14, 0xd8a1e681); - subround(G, b, c, d, a, 4, 20, 0xe7d3fbc8); - subround(G, a, b, c, d, 9, 5, 0x21e1cde6); - subround(G, d, a, b, c, 14, 9, 0xc33707d6); - subround(G, c, d, a, b, 3, 14, 0xf4d50d87); - subround(G, b, c, d, a, 8, 20, 0x455a14ed); - subround(G, a, b, c, d, 13, 5, 0xa9e3e905); - subround(G, d, a, b, c, 2, 9, 0xfcefa3f8); - subround(G, c, d, a, b, 7, 14, 0x676f02d9); - subround(G, b, c, d, a, 12, 20, 0x8d2a4c8a); - subround(H, a, b, c, d, 5, 4, 0xfffa3942); - subround(H, d, a, b, c, 8, 11, 0x8771f681); - subround(H, c, d, a, b, 11, 16, 0x6d9d6122); - subround(H, b, c, d, a, 14, 23, 0xfde5380c); - subround(H, a, b, c, d, 1, 4, 0xa4beea44); - subround(H, d, a, b, c, 4, 11, 0x4bdecfa9); - subround(H, c, d, a, b, 7, 16, 0xf6bb4b60); - subround(H, b, c, d, a, 10, 23, 0xbebfbc70); - subround(H, a, b, c, d, 13, 4, 0x289b7ec6); - subround(H, d, a, b, c, 0, 11, 0xeaa127fa); - subround(H, c, d, a, b, 3, 16, 0xd4ef3085); - subround(H, b, c, d, a, 6, 23, 0x04881d05); - subround(H, a, b, c, d, 9, 4, 0xd9d4d039); - subround(H, d, a, b, c, 12, 11, 0xe6db99e5); - subround(H, c, d, a, b, 15, 16, 0x1fa27cf8); - subround(H, b, c, d, a, 2, 23, 0xc4ac5665); - subround(I, a, b, c, d, 0, 6, 0xf4292244); - subround(I, d, a, b, c, 7, 10, 0x432aff97); - subround(I, c, d, a, b, 14, 15, 0xab9423a7); - subround(I, b, c, d, a, 5, 21, 0xfc93a039); - subround(I, a, b, c, d, 12, 6, 0x655b59c3); - subround(I, d, a, b, c, 3, 10, 0x8f0ccc92); - subround(I, c, d, a, b, 10, 15, 0xffeff47d); - subround(I, b, c, d, a, 1, 21, 0x85845dd1); - subround(I, a, b, c, d, 8, 6, 0x6fa87e4f); - subround(I, d, a, b, c, 15, 10, 0xfe2ce6e0); - subround(I, c, d, a, b, 6, 15, 0xa3014314); - subround(I, b, c, d, a, 13, 21, 0x4e0811a1); - subround(I, a, b, c, d, 4, 6, 0xf7537e82); - subround(I, d, a, b, c, 11, 10, 0xbd3af235); - subround(I, c, d, a, b, 2, 15, 0x2ad7d2bb); - subround(I, b, c, d, a, 9, 21, 0xeb86d391); - - s->h[0] += a; - s->h[1] += b; - s->h[2] += c; - s->h[3] += d; + size_t blkleft = sizeof(blk->block) - blk->used; + size_t chunk = *len < blkleft ? *len : blkleft; + + const uint8_t *p = *vdata; + memcpy(blk->block + blk->used, p, chunk); + *vdata = p + chunk; + *len -= chunk; + blk->used += chunk; + blk->len += chunk; + + if (blk->used == sizeof(blk->block)) { + blk->used = 0; + return true; + } + + return false; } -/* ---------------------------------------------------------------------- - * Outer MD5 algorithm: take an arbitrary length byte string, - * convert it into 16-word blocks with the prescribed padding at - * the end, and pass those blocks to the core MD5 algorithm. - */ +static inline void md5_block_pad(md5_block *blk, BinarySink *bs) +{ + uint64_t final_len = blk->len << 3; + size_t pad = 63 & (55 - blk->used); -#define BLKSIZE 64 + put_byte(bs, 0x80); + put_padding(bs, pad, 0); -static void MD5_BinarySink_write(BinarySink *bs, const void *data, size_t len); + unsigned char buf[8]; + PUT_64BIT_LSB_FIRST(buf, final_len); + put_data(bs, buf, 8); + smemclr(buf, 8); -void MD5Init(struct MD5Context *s) -{ - MD5_Core_Init(&s->core); - s->blkused = 0; - s->len = 0; - BinarySink_INIT(s, MD5_BinarySink_write); + assert(blk->used == 0 && "Should have exactly hit a block boundary"); } -static void MD5_BinarySink_write(BinarySink *bs, const void *data, size_t len) +static inline uint32_t rol(uint32_t x, unsigned y) { - struct MD5Context *s = BinarySink_DOWNCAST(bs, struct MD5Context); - const unsigned char *q = (const unsigned char *)data; - uint32_t wordblock[16]; - uint32_t lenw = len; - int i; - - assert(lenw == len); - - /* - * Update the length field. - */ - s->len += lenw; - - if (s->blkused + len < BLKSIZE) { - /* - * Trivial case: just add to the block. - */ - memcpy(s->block + s->blkused, q, len); - s->blkused += len; - } else { - /* - * We must complete and process at least one block. - */ - while (s->blkused + len >= BLKSIZE) { - memcpy(s->block + s->blkused, q, BLKSIZE - s->blkused); - q += BLKSIZE - s->blkused; - len -= BLKSIZE - s->blkused; - /* Now process the block. Gather bytes little-endian into words */ - for (i = 0; i < 16; i++) { - wordblock[i] = - (((uint32_t) s->block[i * 4 + 3]) << 24) | - (((uint32_t) s->block[i * 4 + 2]) << 16) | - (((uint32_t) s->block[i * 4 + 1]) << 8) | - (((uint32_t) s->block[i * 4 + 0]) << 0); - } - MD5_Block(&s->core, wordblock); - s->blkused = 0; - } - memcpy(s->block, q, len); - s->blkused = len; - } + return (x << (31 & y)) | (x >> (31 & -y)); } -void MD5Final(unsigned char output[16], struct MD5Context *s) +static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0) { - int i; - unsigned pad; - unsigned char c[64]; - uint64_t len; - - if (s->blkused >= 56) - pad = 56 + 64 - s->blkused; - else - pad = 56 - s->blkused; - - len = (s->len << 3); - - memset(c, 0, pad); - c[0] = 0x80; - put_data(s, c, pad); - - PUT_64BIT_LSB_FIRST(c, len); + return if0 ^ (ctrl & (if1 ^ if0)); +} - put_data(s, c, 8); +/* Parameter functions for the four MD5 round types */ +static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z) +{ return Ch(x, y, z); } +static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z) +{ return Ch(z, x, y); } +static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z) +{ return x ^ y ^ z; } +static inline uint32_t I(uint32_t x, uint32_t y, uint32_t z) +{ return y ^ (x | ~z); } + +static inline void md5_round( + unsigned round_index, const uint32_t *message, + uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, + uint32_t (*f)(uint32_t, uint32_t, uint32_t)) +{ + struct md5_round_constant rc = md5_round_constants[round_index]; - for (i = 0; i < 4; i++) { - output[4 * i + 3] = (s->core.h[i] >> 24) & 0xFF; - output[4 * i + 2] = (s->core.h[i] >> 16) & 0xFF; - output[4 * i + 1] = (s->core.h[i] >> 8) & 0xFF; - output[4 * i + 0] = (s->core.h[i] >> 0) & 0xFF; - } + *a = *b + rol(*a + f(*b, *c, *d) + message[rc.msg_index] + rc.addition, + rc.rotation); } -void MD5Simple(void const *p, unsigned len, unsigned char output[16]) +static void md5_do_block(uint32_t *core, const uint8_t *block) { - struct MD5Context s; + uint32_t message_words[16]; + for (size_t i = 0; i < 16; i++) + message_words[i] = GET_32BIT_LSB_FIRST(block + 4*i); + + uint32_t a = core[0], b = core[1], c = core[2], d = core[3]; + + size_t t = 0; + for (size_t u = 0; u < 4; u++) { + md5_round(t++, message_words, &a, &b, &c, &d, F); + md5_round(t++, message_words, &d, &a, &b, &c, F); + md5_round(t++, message_words, &c, &d, &a, &b, F); + md5_round(t++, message_words, &b, &c, &d, &a, F); + } + for (size_t u = 0; u < 4; u++) { + md5_round(t++, message_words, &a, &b, &c, &d, G); + md5_round(t++, message_words, &d, &a, &b, &c, G); + md5_round(t++, message_words, &c, &d, &a, &b, G); + md5_round(t++, message_words, &b, &c, &d, &a, G); + } + for (size_t u = 0; u < 4; u++) { + md5_round(t++, message_words, &a, &b, &c, &d, H); + md5_round(t++, message_words, &d, &a, &b, &c, H); + md5_round(t++, message_words, &c, &d, &a, &b, H); + md5_round(t++, message_words, &b, &c, &d, &a, H); + } + for (size_t u = 0; u < 4; u++) { + md5_round(t++, message_words, &a, &b, &c, &d, I); + md5_round(t++, message_words, &d, &a, &b, &c, I); + md5_round(t++, message_words, &c, &d, &a, &b, I); + md5_round(t++, message_words, &b, &c, &d, &a, I); + } - MD5Init(&s); - put_data(&s, (unsigned char const *)p, len); - MD5Final(output, &s); - smemclr(&s, sizeof(s)); -} + core[0] += a; + core[1] += b; + core[2] += c; + core[3] += d; -/* ---------------------------------------------------------------------- - * Thin abstraction for things where hashes are pluggable. - */ + smemclr(message_words, sizeof(message_words)); +} -struct md5_hash { - struct MD5Context state; +typedef struct md5 { + uint32_t core[4]; + md5_block blk; + BinarySink_IMPLEMENTATION; ssh_hash hash; -}; +} md5; + +static void md5_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *md5_new(const ssh_hashalg *alg) { - struct md5_hash *h = snew(struct md5_hash); - MD5Init(&h->state); - h->hash.vt = alg; - BinarySink_DELEGATE_INIT(&h->hash, &h->state); - return &h->hash; + md5 *s = snew(md5); + + s->hash.vt = alg; + BinarySink_INIT(s, md5_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; } -static ssh_hash *md5_copy(ssh_hash *hashold) +static void md5_reset(ssh_hash *hash) { - struct md5_hash *hold, *hnew; - ssh_hash *hashnew = md5_new(hashold->vt); + md5 *s = container_of(hash, md5, hash); - hold = container_of(hashold, struct md5_hash, hash); - hnew = container_of(hashnew, struct md5_hash, hash); + memcpy(s->core, md5_initial_state, sizeof(s->core)); + md5_block_setup(&s->blk); +} - hnew->state = hold->state; - BinarySink_COPIED(&hnew->state); +static void md5_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + md5 *copy = container_of(hcopy, md5, hash); + md5 *orig = container_of(horig, md5, hash); - return hashnew; + memcpy(copy, orig, sizeof(*copy)); + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); } static void md5_free(ssh_hash *hash) { - struct md5_hash *h = container_of(hash, struct md5_hash, hash); + md5 *s = container_of(hash, md5, hash); - smemclr(h, sizeof(*h)); - sfree(h); + smemclr(s, sizeof(*s)); + sfree(s); } -static void md5_final(ssh_hash *hash, unsigned char *output) +static void md5_write(BinarySink *bs, const void *vp, size_t len) { - struct md5_hash *h = container_of(hash, struct md5_hash, hash); - MD5Final(output, &h->state); - md5_free(hash); + md5 *s = BinarySink_DOWNCAST(bs, md5); + + while (len > 0) + if (md5_block_write(&s->blk, &vp, &len)) + md5_do_block(s->core, s->blk.block); +} + +static void md5_digest(ssh_hash *hash, uint8_t *digest) +{ + md5 *s = container_of(hash, md5, hash); + + md5_block_pad(&s->blk, BinarySink_UPCAST(s)); + for (size_t i = 0; i < 4; i++) + PUT_32BIT_LSB_FIRST(digest + 4*i, s->core[i]); } const ssh_hashalg ssh_md5 = { - md5_new, md5_copy, md5_final, md5_free, 16, 64, HASHALG_NAMES_BARE("MD5"), + .new = md5_new, + .reset = md5_reset, + .copyfrom = md5_copyfrom, + .digest = md5_digest, + .free = md5_free, + .hlen = 16, + .blocklen = 64, + HASHALG_NAMES_BARE("MD5"), }; diff --git a/sshppl.h b/sshppl.h index 4d55d38..2c1dbf4 100644 --- a/sshppl.h +++ b/sshppl.h @@ -8,6 +8,7 @@ #define PUTTY_SSHPPL_H typedef void (*packet_handler_fn_t)(PacketProtocolLayer *ppl, PktIn *pktin); +typedef struct PacketProtocolLayerVtable PacketProtocolLayerVtable; struct PacketProtocolLayerVtable { void (*free)(PacketProtocolLayer *); @@ -19,6 +20,7 @@ struct PacketProtocolLayerVtable { bool (*want_user_input)(PacketProtocolLayer *ppl); void (*got_user_input)(PacketProtocolLayer *ppl); void (*reconfigure)(PacketProtocolLayer *ppl, Conf *conf); + size_t (*queued_data_size)(PacketProtocolLayer *ppl); /* Protocol-level name of this layer. */ const char *name; @@ -73,6 +75,8 @@ static inline void ssh_ppl_got_user_input(PacketProtocolLayer *ppl) { ppl->vt->got_user_input(ppl); } static inline void ssh_ppl_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { ppl->vt->reconfigure(ppl, conf); } +static inline size_t ssh_ppl_queued_data_size(PacketProtocolLayer *ppl) +{ return ppl->vt->queued_data_size(ppl); } /* ssh_ppl_free is more than just a macro wrapper on the vtable; it * does centralised parts of the freeing too. */ @@ -90,6 +94,11 @@ void ssh_ppl_setup_queues(PacketProtocolLayer *ppl, * avoid dereferencing itself on return from this function! */ void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new); +/* Default implementation of queued_data_size, which just adds up the + * sizes of all the packets in pq_out. A layer can override this if it + * has other things to take into account as well. */ +size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl); + PacketProtocolLayer *ssh1_login_new( Conf *conf, const char *host, int port, PacketProtocolLayer *successor_layer); @@ -107,7 +116,7 @@ PacketProtocolLayer *ssh2_transport_new( PacketProtocolLayer *ssh2_userauth_new( PacketProtocolLayer *successor_layer, const char *hostname, const char *fullhostname, - Filename *keyfile, bool show_banner, bool tryagent, + Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth, const char *default_username, bool change_username, bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth, diff --git a/sshprime.c b/sshprime.c index cab601d..d9bdebb 100644 --- a/sshprime.c +++ b/sshprime.c @@ -3,19 +3,18 @@ */ #include +#include + #include "ssh.h" #include "mpint.h" +#include "mpunsafe.h" +#include "sshkeygen.h" -/* - * This prime generation algorithm is pretty much cribbed from - * OpenSSL. The algorithm is: - * - * - invent a B-bit random number and ensure the top and bottom - * bits are set (so it's definitely B-bit, and it's definitely - * odd) +/* ---------------------------------------------------------------------- + * Standard probabilistic prime-generation algorithm: * - * - see if it's coprime to all primes below 2^16; increment it by - * two until it is (this shouldn't take long in general) + * - get a number from our PrimeCandidateSource which will at least + * avoid being divisible by any prime under 2^16 * * - perform the Miller-Rabin primality test enough times to * ensure the probability of it being composite is 2^-80 or @@ -24,454 +23,740 @@ * - go back to square one if any M-R test fails. */ -/* - * The Miller-Rabin primality test is an extension to the Fermat - * test. The Fermat test just checks that a^(p-1) == 1 mod p; this - * is vulnerable to Carmichael numbers. Miller-Rabin considers how - * that 1 is derived as well. - * - * Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1 - * or a == -1 (mod p). - * - * Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence, - * since p is prime, either p divides (a+1) or p divides (a-1). - * But this is the same as saying that either a is congruent to - * -1 mod p or a is congruent to +1 mod p. [] - * - * Comment: This fails when p is not prime. Consider p=mn, so - * that mn divides (a+1)(a-1). Now we could have m dividing (a+1) - * and n dividing (a-1), without the whole of mn dividing either. - * For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides - * 10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p - * without a having to be congruent to either 1 or -1. - * - * So the Miller-Rabin test, as well as considering a^(p-1), - * considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can - * go. In other words. we write p-1 as q * 2^k, with k as large as - * possible (i.e. q must be odd), and we consider the powers - * - * a^(q*2^0) a^(q*2^1) ... a^(q*2^(k-1)) a^(q*2^k) - * i.e. a^((n-1)/2^k) a^((n-1)/2^(k-1)) ... a^((n-1)/2) a^(n-1) - * - * If p is to be prime, the last of these must be 1. Therefore, by - * the above lemma, the one before it must be either 1 or -1. And - * _if_ it's 1, then the one before that must be either 1 or -1, - * and so on ... In other words, we expect to see a trailing chain - * of 1s preceded by a -1. (If we're unlucky, our trailing chain of - * 1s will be as long as the list so we'll never get to see what - * lies before it. This doesn't count as a test failure because it - * hasn't _proved_ that p is not prime.) - * - * For example, consider a=2 and p=1729. 1729 is a Carmichael - * number: although it's not prime, it satisfies a^(p-1) == 1 mod p - * for any a coprime to it. So the Fermat test wouldn't have a - * problem with it at all, unless we happened to stumble on an a - * which had a common factor. - * - * So. 1729 - 1 equals 27 * 2^6. So we look at - * - * 2^27 mod 1729 == 645 - * 2^108 mod 1729 == 1065 - * 2^216 mod 1729 == 1 - * 2^432 mod 1729 == 1 - * 2^864 mod 1729 == 1 - * 2^1728 mod 1729 == 1 - * - * We do have a trailing string of 1s, so the Fermat test would - * have been happy. But this trailing string of 1s is preceded by - * 1065; whereas if 1729 were prime, we'd expect to see it preceded - * by -1 (i.e. 1728.). Guards! Seize this impostor. - * - * (If we were unlucky, we might have tried a=16 instead of a=2; - * now 16^27 mod 1729 == 1, so we would have seen a long string of - * 1s and wouldn't have seen the thing _before_ the 1s. So, just - * like the Fermat test, for a given p there may well exist values - * of a which fail to show up its compositeness. So we try several, - * just like the Fermat test. The difference is that Miller-Rabin - * is not _in general_ fooled by Carmichael numbers.) - * - * Put simply, then, the Miller-Rabin test requires us to: - * - * 1. write p-1 as q * 2^k, with q odd - * 2. compute z = (a^q) mod p. - * 3. report success if z == 1 or z == -1. - * 4. square z at most k-1 times, and report success if it becomes - * -1 at any point. - * 5. report failure otherwise. - * - * (We expect z to become -1 after at most k-1 squarings, because - * if it became -1 after k squarings then a^(p-1) would fail to be - * 1. And we don't need to investigate what happens after we see a - * -1, because we _know_ that -1 squared is 1 modulo anything at - * all, so after we've seen a -1 we can be sure of seeing nothing - * but 1s.) - */ +static PrimeGenerationContext *probprime_new_context( + const PrimeGenerationPolicy *policy) +{ + PrimeGenerationContext *ctx = snew(PrimeGenerationContext); + ctx->vt = policy; + return ctx; +} -static unsigned short primes[6542]; /* # primes < 65536 */ -#define NPRIMES (lenof(primes)) +static void probprime_free_context(PrimeGenerationContext *ctx) +{ + sfree(ctx); +} -static void init_primes_array(void) +static ProgressPhase probprime_add_progress_phase( + const PrimeGenerationPolicy *policy, + ProgressReceiver *prog, unsigned bits) { - if (primes[0]) - return; /* already done */ + /* + * The density of primes near x is 1/(log x). When x is about 2^b, + * that's 1/(b log 2). + * + * But we're only doing the expensive part of the process (the M-R + * checks) for a number that passes the initial winnowing test of + * having no factor less than 2^16 (at least, unless the prime is + * so small that PrimeCandidateSource gives up on that winnowing). + * The density of _those_ numbers is about 1/19.76. So the odds of + * hitting a prime per expensive attempt are boosted by a factor + * of 19.76. + */ + const double log_2 = 0.693147180559945309417232121458; + double winnow_factor = (bits < 32 ? 1.0 : 19.76); + double prob = winnow_factor / (bits * log_2); - bool A[65536]; + /* + * Estimate the cost of prime generation as the cost of the M-R + * modexps. + */ + double cost = (miller_rabin_checks_needed(bits) * + estimate_modexp_cost(bits)); + return progress_add_probabilistic(prog, cost, prob); +} - for (size_t i = 2; i < lenof(A); i++) - A[i] = true; +static mp_int *probprime_generate( + PrimeGenerationContext *ctx, + PrimeCandidateSource *pcs, ProgressReceiver *prog) +{ + pcs_ready(pcs); - for (size_t i = 2; i < lenof(A); i++) { - if (!A[i]) - continue; - for (size_t j = 2*i; j < lenof(A); j += i) - A[j] = false; - } + while (true) { + progress_report_attempt(prog); - size_t pos = 0; - for (size_t i = 2; i < lenof(A); i++) - if (A[i]) - primes[pos++] = i; + mp_int *p = pcs_generate(pcs); + if (!p) { + pcs_free(pcs); + return NULL; + } - assert(pos == NPRIMES); -} + MillerRabin *mr = miller_rabin_new(p); + bool known_bad = false; + unsigned nchecks = miller_rabin_checks_needed(mp_get_nbits(p)); + for (unsigned check = 0; check < nchecks; check++) { + if (!miller_rabin_test_random(mr)) { + known_bad = true; + break; + } + } + miller_rabin_free(mr); + + if (!known_bad) { + /* + * We have a prime! + */ + pcs_free(pcs); + return p; + } -static unsigned short mp_mod_short(mp_int *x, unsigned short modulus) -{ - /* - * This function lives here rather than in mpint.c partly because - * this is the only place it's needed, but mostly because it - * doesn't pay careful attention to constant running time, since - * as far as I can tell that's a lost cause for key generation - * anyway. - */ - unsigned accumulator = 0; - for (size_t i = mp_max_bytes(x); i-- > 0 ;) { - accumulator = 0x100 * accumulator + mp_get_byte(x, i); - accumulator %= modulus; + mp_free(p); } - return accumulator; } -/* - * Generate a prime. We can deal with various extra properties of - * the prime: - * - * - to speed up use in RSA, we can arrange to select a prime with - * the property (prime % modulus) != residue. - * - * - for use in DSA, we can arrange to select a prime which is one - * more than a multiple of a dirty great bignum. In this case - * `bits' gives the size of the factor by which we _multiply_ - * that bignum, rather than the size of the whole number. - * - * - for the basically cosmetic purposes of generating keys of the - * length actually specified rather than off by one bit, we permit - * the caller to provide an unsigned integer 'firstbits' which will - * match the top few bits of the returned prime. (That is, there - * will exist some n such that (returnvalue >> n) == firstbits.) If - * 'firstbits' is not needed, specifying it to either 0 or 1 is - * an adequate no-op. - */ -mp_int *primegen( - int bits, int modulus, int residue, mp_int *factor, - int phase, progfn_t pfn, void *pfnparam, unsigned firstbits) +static strbuf *null_mpu_certificate(PrimeGenerationContext *ctx, mp_int *p) { - init_primes_array(); + return NULL; +} - int progress = 0; +const PrimeGenerationPolicy primegen_probabilistic = { + probprime_add_progress_phase, + probprime_new_context, + probprime_free_context, + probprime_generate, + null_mpu_certificate, +}; - size_t fbsize = 0; - while (firstbits >> fbsize) /* work out how to align this */ - fbsize++; +/* ---------------------------------------------------------------------- + * Alternative provable-prime algorithm, based on the following paper: + * + * [MAURER] Maurer, U.M. Fast generation of prime numbers and secure + * public-key cryptographic parameters. J. Cryptology 8, 123–155 + * (1995). https://doi.org/10.1007/BF00202269 + */ - STARTOVER: +typedef enum SubprimePolicy { + SPP_FAST, + SPP_MAURER_SIMPLE, + SPP_MAURER_COMPLEX, +} SubprimePolicy; + +typedef struct ProvablePrimePolicyExtra { + SubprimePolicy spp; +} ProvablePrimePolicyExtra; + +typedef struct ProvablePrimeContext ProvablePrimeContext; +struct ProvablePrimeContext { + Pockle *pockle; + PrimeGenerationContext pgc; + const ProvablePrimePolicyExtra *extra; +}; + +static PrimeGenerationContext *provableprime_new_context( + const PrimeGenerationPolicy *policy) +{ + ProvablePrimeContext *ppc = snew(ProvablePrimeContext); + ppc->pgc.vt = policy; + ppc->pockle = pockle_new(); + ppc->extra = policy->extra; + return &ppc->pgc; +} - pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress); +static void provableprime_free_context(PrimeGenerationContext *ctx) +{ + ProvablePrimeContext *ppc = container_of(ctx, ProvablePrimeContext, pgc); + pockle_free(ppc->pockle); + sfree(ppc); +} +static ProgressPhase provableprime_add_progress_phase( + const PrimeGenerationPolicy *policy, + ProgressReceiver *prog, unsigned bits) +{ /* - * Generate a k-bit random number with top and bottom bits set. - * Alternatively, if `factor' is nonzero, generate a k-bit - * random number with the top bit set and the bottom bit clear, - * multiply it by `factor', and add one. + * Estimating the cost of making a _provable_ prime is difficult + * because of all the recursions to smaller sizes. + * + * Once you have enough factors of p-1 to certify primality of p, + * the remaining work in provable prime generation is not very + * different from probabilistic: you generate a random candidate, + * test its primality probabilistically, and use the witness value + * generated as a byproduct of that test for the full Pocklington + * verification. The expensive part, as usual, is made of modpows. + * + * The Pocklington test needs at least two modpows (one for the + * Fermat check, and one per known factor of p-1). + * + * The prior M-R step needs an unknown number, because we iterate + * until we find a value whose order is divisible by the largest + * power of 2 that divides p-1, say 2^j. That excludes half the + * possible witness values (specifically, the quadratic residues), + * so we expect to need on average two M-R operations to find one. + * But that's only if the number _is_ prime - as usual, it's also + * possible that we hit a non-prime and have to try again. + * + * So, if we were only estimating the cost of that final step, it + * would look a lot like the probabilistic version: we'd have to + * estimate the expected total number of modexps by knowing + * something about the density of primes among our candidate + * integers, and then multiply that by estimate_modexp_cost(bits). + * But the problem is that we also have to _find_ a smaller prime, + * so we have to recurse. + * + * In the MAURER_SIMPLE version of the algorithm, you recurse to + * any one of a range of possible smaller sizes i, each with + * probability proportional to 1/i. So your expected time to + * generate an n-bit prime is given by a horrible recurrence of + * the form E_n = S_n + (sum E_i/i) / (sum 1/i), in which S_n is + * the expected cost of the final step once you have your smaller + * primes, and both sums are over ceil(n/2) <= i <= n-20. + * + * At this point I ran out of effort to actually do the maths + * rigorously, so instead I did the empirical experiment of + * generating that sequence in Python and plotting it on a graph. + * My Python code is here, in case I need it again: + +from math import log + +alpha = log(3)/log(2) + 1 # exponent for modexp using Karatsuba mult + +E = [1] * 16 # assume generating tiny primes is trivial + +for n in range(len(E), 4096): + + # Expected time for sub-generations, as a weighted mean of prior + # values of the same sequence. + lo = (n+1)//2 + hi = n-20 + if lo <= hi: + subrange = range(lo, hi+1) + num = sum(E[i]/i for i in subrange) + den = sum(1/i for i in subrange) + else: + num, den = 0, 1 + + # Constant term (cost of final step). + # Similar to probprime_add_progress_phase. + winnow_factor = 1 if n < 32 else 19.76 + prob = winnow_factor / (n * log(2)) + cost = 4 * n**alpha / prob + + E.append(cost + num / den) + +for i, p in enumerate(E): + try: + print(log(i), log(p)) + except ValueError: + continue + + * The output loop prints the logs of both i and E_i, so that when + * I plot the resulting data file in gnuplot I get a log-log + * diagram. That showed me some early noise and then a very + * straight-looking line; feeding the straight part of the graph + * to linear-regression analysis reported that it fits the line + * + * log E_n = -1.7901825337965498 + 3.6199197179662517 * log(n) + * => E_n = 0.16692969657466802 * n^3.6199197179662517 + * + * So my somewhat empirical estimate is that Maurer prime + * generation costs about 0.167 * bits^3.62, in the same arbitrary + * time units used by estimate_modexp_cost. */ - mp_int *p = mp_power_2(bits - 1); /* ensure top bit is 1 */ - mp_int *r = mp_random_bits(bits - 1); - mp_or_into(p, p, r); - mp_free(r); - mp_set_bit(p, 0, factor ? 0 : 1); /* set bottom bit appropriately */ - - for (size_t i = 0; i < fbsize; i++) - mp_set_bit(p, bits-fbsize + i, 1 & (firstbits >> i)); - - if (factor) { - mp_int *tmp = p; - p = mp_mul(tmp, factor); - mp_free(tmp); - assert(mp_get_bit(p, 0) == 0); - mp_set_bit(p, 0, 1); - } - /* - * We need to ensure this random number is coprime to the first - * few primes, by repeatedly adding either 2 or 2*factor to it - * until it is. To do this we make a list of (modulus, residue) - * pairs to avoid, and we also add to that list the extra pair our - * caller wants to avoid. - */ + return progress_add_linear(prog, 0.167 * pow(bits, 3.62)); +} - /* List the moduli */ - unsigned long moduli[NPRIMES + 1]; - for (size_t i = 0; i < NPRIMES; i++) - moduli[i] = primes[i]; - moduli[NPRIMES] = modulus; - - /* Find the residue of our starting number mod each of them. Also - * set up the multipliers array which tells us how each one will - * change when we increment the number (which isn't just 1 if - * we're incrementing by multiples of factor). */ - unsigned long residues[NPRIMES + 1], multipliers[NPRIMES + 1]; - for (size_t i = 0; i < lenof(moduli); i++) { - residues[i] = mp_mod_short(p, moduli[i]); - if (factor) - multipliers[i] = mp_mod_short(factor, moduli[i]); - else - multipliers[i] = 1; - } +static mp_int *primegen_small(Pockle *pockle, PrimeCandidateSource *pcs) +{ + assert(pcs_get_bits(pcs) <= 32); - /* Adjust the last entry so that it avoids a residue other than zero */ - residues[NPRIMES] = (residues[NPRIMES] + modulus - residue) % modulus; + pcs_ready(pcs); - /* - * Now loop until no residue in that list is zero, to find a - * sensible increment. We maintain the increment in an ordinary - * integer, so if it gets too big, we'll have to give up and go - * back to making up a fresh random large integer. - */ - unsigned delta = 0; - while (1) { - for (size_t i = 0; i < lenof(moduli); i++) - if (!((residues[i] + delta * multipliers[i]) % moduli[i])) - goto found_a_zero; - - /* If we didn't exit that loop by goto, we've got our candidate. */ - break; - - found_a_zero: - delta += 2; - if (delta > 65536) { - mp_free(p); - goto STARTOVER; + while (true) { + mp_int *p = pcs_generate(pcs); + if (!p) { + pcs_free(pcs); + return NULL; } + if (pockle_add_small_prime(pockle, p) == POCKLE_OK) { + pcs_free(pcs); + return p; + } + mp_free(p); } +} - /* - * Having found a plausible increment, actually add it on. - */ - if (factor) { - mp_int *d = mp_from_integer(delta); - mp_int *df = mp_mul(d, factor); - mp_add_into(p, p, df); - mp_free(d); - mp_free(df); - } else { - mp_add_integer_into(p, p, delta); - } +#ifdef DEBUG_PRIMEGEN +static void timestamp(FILE *fp) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + fprintf(fp, "%lu.%09lu: ", (unsigned long)ts.tv_sec, + (unsigned long)ts.tv_nsec); +} +static PRINTF_LIKE(1, 2) void debug_f(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + timestamp(stderr); + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); + va_end(ap); +} +static void debug_f_mp(const char *fmt, mp_int *x, ...) +{ + va_list ap; + va_start(ap, x); + timestamp(stderr); + vfprintf(stderr, fmt, ap); + mp_dump(stderr, "", x, "\n"); + va_end(ap); +} +#else +#define debug_f(...) ((void)0) +#define debug_f_mp(...) ((void)0) +#endif - /* - * Now apply the Miller-Rabin primality test a few times. First - * work out how many checks are needed. - */ - unsigned checks = - bits >= 1300 ? 2 : bits >= 850 ? 3 : bits >= 650 ? 4 : - bits >= 550 ? 5 : bits >= 450 ? 6 : bits >= 400 ? 7 : - bits >= 350 ? 8 : bits >= 300 ? 9 : bits >= 250 ? 12 : - bits >= 200 ? 15 : bits >= 150 ? 18 : 27; +static double uniform_random_double(void) +{ + unsigned char randbuf[8]; + random_read(randbuf, 8); + return GET_64BIT_MSB_FIRST(randbuf) * 0x1.0p-64; +} - /* - * Next, write p-1 as q*2^k. - */ - size_t k; - for (k = 0; mp_get_bit(p, k) == !k; k++) - continue; /* find first 1 bit in p-1 */ - mp_int *q = mp_rshift_safe(p, k); +static mp_int *mp_ceil_div(mp_int *n, mp_int *d) +{ + mp_int *nplus = mp_add(n, d); + mp_sub_integer_into(nplus, nplus, 1); + mp_int *toret = mp_div(nplus, d); + mp_free(nplus); + return toret; +} - /* - * Set up stuff for the Miller-Rabin checks. - */ - mp_int *two = mp_from_integer(2); - mp_int *pm1 = mp_copy(p); - mp_sub_integer_into(pm1, pm1, 1); - MontyContext *mc = monty_new(p); - mp_int *m_pm1 = monty_import(mc, pm1); +static mp_int *provableprime_generate_inner( + ProvablePrimeContext *ppc, PrimeCandidateSource *pcs, + ProgressReceiver *prog, double progress_origin, double progress_scale) +{ + unsigned bits = pcs_get_bits(pcs); + assert(bits > 1); - bool known_bad = false; + if (bits <= 32) { + debug_f("ppgi(%u) -> small", bits); + return primegen_small(ppc->pockle, pcs); + } - /* - * Now, for each check ... - */ - for (unsigned check = 0; check < checks && !known_bad; check++) { + unsigned min_bits_needed, max_bits_needed; + { /* - * Invent a random number between 1 and p-1. + * Find the product of all the prime factors we already know + * about. */ - mp_int *w = mp_random_in_range(two, pm1); - monty_import_into(mc, w, w); - - pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress); + mp_int *size_got = mp_from_integer(1); + size_t nfactors; + mp_int **factors = pcs_get_known_prime_factors(pcs, &nfactors); + for (size_t i = 0; i < nfactors; i++) { + mp_int *to_free = size_got; + size_got = mp_unsafe_shrink(mp_mul(size_got, factors[i])); + mp_free(to_free); + } /* - * Compute w^q mod p. + * Find the largest cofactor we might be able to use, and the + * smallest one we can get away with. */ - mp_int *wqp = monty_pow(mc, w, q); - mp_free(w); + mp_int *upperbound = pcs_get_upper_bound(pcs); + mp_int *size_needed = mp_nthroot(upperbound, 3, NULL); + debug_f_mp("upperbound = ", upperbound); + { + mp_int *to_free = upperbound; + upperbound = mp_unsafe_shrink(mp_div(upperbound, size_got)); + mp_free(to_free); + } + debug_f_mp("size_needed = ", size_needed); + { + mp_int *to_free = size_needed; + size_needed = mp_unsafe_shrink(mp_ceil_div(size_needed, size_got)); + mp_free(to_free); + } + + max_bits_needed = pcs_get_bits_remaining(pcs); /* - * See if this is 1, or if it is -1, or if it becomes -1 - * when squared at most k-1 times. + * We need a prime that is greater than or equal to + * 'size_needed' in order for the product of all our known + * factors of p-1 to exceed the cube root of the largest value + * p might take. + * + * Since pcs_new wants a size specified in bits, we must count + * the bits in size_needed and then add 1. Otherwise we might + * get a value with the same bit count as size_needed but + * slightly smaller than it. + * + * An exception is if size_needed = 1. In that case the + * product of existing known factors is _already_ enough, so + * we don't need to generate an extra factor at all. */ - bool passed = false; - - if (mp_cmp_eq(wqp, monty_identity(mc)) || mp_cmp_eq(wqp, m_pm1)) { - passed = true; + if (mp_hs_integer(size_needed, 2)) { + min_bits_needed = mp_get_nbits(size_needed) + 1; } else { - for (size_t i = 0; i < k - 1; i++) { - monty_mul_into(mc, wqp, wqp, wqp); - if (mp_cmp_eq(wqp, m_pm1)) { - passed = true; + min_bits_needed = 0; + } + + mp_free(upperbound); + mp_free(size_needed); + mp_free(size_got); + } + + double progress = 0.0; + + if (min_bits_needed) { + debug_f("ppgi(%u) recursing, need [%u,%u] more bits", + bits, min_bits_needed, max_bits_needed); + + unsigned *sizes = NULL; + size_t nsizes = 0, sizesize = 0; + + unsigned real_min = max_bits_needed / 2; + unsigned real_max = (max_bits_needed >= 20 ? + max_bits_needed - 20 : 0); + if (real_min < min_bits_needed) + real_min = min_bits_needed; + if (real_max < real_min) + real_max = real_min; + debug_f("ppgi(%u) revised bits interval = [%u,%u]", + bits, real_min, real_max); + + switch (ppc->extra->spp) { + case SPP_FAST: + /* + * Always pick the smallest subsidiary prime we can get + * away with: just over n/3 bits. + * + * This is not a good mode for cryptographic prime + * generation, because it skews the distribution of primes + * greatly, and worse, it skews them in a direction that + * heads away from the properties crypto algorithms tend + * to like. + * + * (For both discrete-log systems and RSA, people have + * tended to recommend in the past that p-1 should have a + * _large_ factor if possible. There's some disagreement + * on which algorithms this is really necessary for, but + * certainly I've never seen anyone recommend arranging a + * _small_ factor on purpose.) + * + * I originally implemented this mode because it was + * convenient for debugging - it wastes as little time as + * possible on finding a sub-prime and lets you get to the + * interesting part! And I leave it in the code because it + * might still be useful for _something_. Because it's + * cryptographically questionable, it's not selectable in + * the UI of either version of PuTTYgen proper; but it can + * be accessed through testcrypt, and if for some reason a + * definite prime is needed for non-crypto purposes, it + * may still be the fastest way to put your hands on one. + */ + debug_f("ppgi(%u) fast mode, just ask for %u bits", + bits, min_bits_needed); + sgrowarray(sizes, sizesize, nsizes); + sizes[nsizes++] = min_bits_needed; + break; + case SPP_MAURER_SIMPLE: { + /* + * Select the size of the subsidiary prime at random from + * sqrt(outputprime) up to outputprime/2^20, in such a way + * that the probability distribution matches that of the + * largest prime factor of a random n-bit number. + * + * Per [MAURER] section 3.4, the cumulative distribution + * function of this relative size is 1+log2(x), for x in + * [1/2,1]. You can generate a value from the distribution + * given by a cdf by applying the inverse cdf to a uniform + * value in [0,1]. Simplifying that in this case, what we + * have to do is raise 2 to the power of a random real + * number between -1 and 0. (And that gives you the number + * of _bits_ in the sub-prime, as a factor of the desired + * output number of bits.) + * + * We also require that the subsidiary prime q is at least + * 20 bits smaller than the output one, to give us a + * fighting chance of there being _any_ prime we can find + * such that q | p-1. + * + * (But these rules have to be applied in an order that + * still leaves us _some_ interval of possible sizes we + * can pick!) + */ + maurer_simple: + debug_f("ppgi(%u) Maurer simple mode", bits); + + unsigned sub_bits; + do { + double uniform = uniform_random_double(); + sub_bits = real_max * pow(2.0, uniform - 1) + 0.5; + debug_f(" ... %.6f -> %u?", uniform, sub_bits); + } while (!(real_min <= sub_bits && sub_bits <= real_max)); + + debug_f("ppgi(%u) asking for %u bits", bits, sub_bits); + sgrowarray(sizes, sizesize, nsizes); + sizes[nsizes++] = sub_bits; + + break; + } + case SPP_MAURER_COMPLEX: { + /* + * In this mode, we may generate multiple factors of p-1 + * which between them add up to at least n/2 bits, in such + * a way that those are guaranteed to be the largest + * factors of p-1 and that they have the same probability + * distribution as the largest k factors would have in a + * random integer. The idea is that this more elaborate + * procedure gets as close as possible to the same + * probability distribution you'd get by selecting a + * completely random prime (if you feasibly could). + * + * Algorithm from Appendix 1 of [MAURER]: we generate + * random real numbers that sum to at most 1, by choosing + * each one uniformly from the range [0, 1 - sum of all + * the previous ones]. We maintain them in a list in + * decreasing order, and we stop as soon as we find an + * initial subsequence of the list s_1,...,s_r such that + * s_1 + ... + s_{r-1} + 2 s_r > 1. In particular, this + * guarantees that the sum of that initial subsequence is + * at least 1/2, so we end up with enough factors to + * satisfy Pocklington. + */ + + if (max_bits_needed / 2 + 1 > real_max) { + /* Early exit path in the case where this algorithm + * can't possibly generate a value in the range we + * need. In that situation, fall back to Maurer + * simple. */ + debug_f("ppgi(%u) skipping GenerateSizeList, " + "real_max too small", bits); + goto maurer_simple; /* sorry! */ + } + + double *s = NULL; + size_t ns, ssize = 0; + + while (true) { + debug_f("ppgi(%u) starting GenerateSizeList", bits); + ns = 0; + double range = 1.0; + while (true) { + /* Generate the next number */ + double u = uniform_random_double() * range; + range -= u; + debug_f(" u_%"SIZEu" = %g", ns, u); + + /* Insert it in the list */ + sgrowarray(s, ssize, ns); + size_t i; + for (i = ns; i > 0 && s[i-1] < u; i--) + s[i] = s[i-1]; + s[i] = u; + ns++; + debug_f(" inserting as s[%"SIZEu"]", i); + + /* Look for a suitable initial subsequence */ + double sum = 0; + for (i = 0; i < ns; i++) { + sum += s[i]; + if (sum + s[i] > 1.0) { + debug_f(" s[0..%"SIZEu"] works!", i); + + /* Truncate the sequence here, and stop + * generating random real numbers. */ + ns = i+1; + goto got_list; + } + } + } + + got_list:; + /* + * Now translate those real numbers into actual bit + * counts, and do a last-minute check to make sure + * their product is going to be in range. + * + * We have to check both the min and max sizes of the + * total. A b-bit number is in [2^{b-1},2^b). So the + * product of numbers of sizes b_1,...,b_k is at least + * 2^{\sum (b_i-1)}, and less than 2^{\sum b_i}. + */ + nsizes = 0; + + unsigned min_total = 0, max_total = 0; + + for (size_t i = 0; i < ns; i++) { + /* These sizes are measured in actual entropy, so + * add 1 bit each time to account for the + * zero-information leading 1 */ + unsigned this_size = max_bits_needed * s[i] + 1; + debug_f(" bits[%"SIZEu"] = %u", i, this_size); + sgrowarray(sizes, sizesize, nsizes); + sizes[nsizes++] = this_size; + + min_total += this_size - 1; + max_total += this_size; + } + + debug_f(" total bits = [%u,%u)", min_total, max_total); + if (min_total < real_min || max_total > real_max+1) { + debug_f(" total out of range, try again"); + } else { + debug_f(" success! %"SIZEu" sub-primes totalling [%u,%u) " + "bits", nsizes, min_total, max_total); break; } } + + smemclr(s, ssize * sizeof(*s)); + sfree(s); + break; + } + default: + unreachable("bad subprime policy"); } - if (!passed) - known_bad = true; + for (size_t i = 0; i < nsizes; i++) { + unsigned sub_bits = sizes[i]; + double progress_in_this_prime = (double)sub_bits / bits; + mp_int *q = provableprime_generate_inner( + ppc, pcs_new(sub_bits), + prog, progress_origin + progress_scale * progress, + progress_scale * progress_in_this_prime); + progress += progress_in_this_prime; + assert(q); + debug_f_mp("ppgi(%u) got factor ", q, bits); + pcs_require_residue_1_mod_prime(pcs, q); + mp_free(q); + } - mp_free(wqp); + smemclr(sizes, sizesize * sizeof(*sizes)); + sfree(sizes); + } else { + debug_f("ppgi(%u) no need to recurse", bits); } - mp_free(q); - mp_free(two); - mp_free(pm1); - monty_free(mc); - mp_free(m_pm1); + debug_f("ppgi(%u) ready, %u bits remaining", + bits, pcs_get_bits_remaining(pcs)); + pcs_ready(pcs); - if (known_bad) { - mp_free(p); - goto STARTOVER; - } + while (true) { + mp_int *p = pcs_generate(pcs); + if (!p) { + pcs_free(pcs); + return NULL; + } - /* - * We have a prime! - */ - return p; -} + debug_f_mp("provable_step p=", p); -/* - * Invent a pair of values suitable for use as 'firstbits' in the - * above function, such that their product is at least 2, and such - * that their difference is also at least min_separation. - * - * This is used for generating both RSA and DSA keys which have - * exactly the specified number of bits rather than one fewer - if you - * generate an a-bit and a b-bit number completely at random and - * multiply them together, you could end up with either an (ab-1)-bit - * number or an (ab)-bit number. The former happens log(2)*2-1 of the - * time (about 39%) and, though actually harmless, every time it - * occurs it has a non-zero probability of sparking a user email along - * the lines of 'Hey, I asked PuTTYgen for a 2048-bit key and I only - * got 2047 bits! Bug!' - */ -static inline unsigned firstbits_b_min( - unsigned a, unsigned lo, unsigned hi, unsigned min_separation) -{ - /* To get a large enough product, b must be at least this much */ - unsigned b_min = (2*lo*lo + a - 1) / a; - /* Now enforce a hi) - b_min = hi; - return b_min; -} + MillerRabin *mr = miller_rabin_new(p); + debug_f("provable_step mr setup done"); + mp_int *witness = miller_rabin_find_potential_primitive_root(mr); + miller_rabin_free(mr); -void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation) -{ - /* - * We'll pick 12 initial bits (number selected at random) for each - * prime, not counting the leading 1. So we want to return two - * values in the range [2^12,2^13) whose product is at least 2^25. - * - * Strategy: count up all the viable pairs, then select a random - * number in that range and use it to pick a pair. - * - * To keep things simple, we'll ensure a < b, and randomly swap - * them at the end. - */ - const unsigned lo = 1<<12, hi = 1<<13, minproduct = 2*lo*lo; - unsigned a, b; + if (!witness) { + debug_f("provable_step mr failed"); + mp_free(p); + continue; + } - /* - * Count up the number of prefixes of b that would be valid for - * each prefix of a. - */ - mp_int *total = mp_new(32); - for (a = lo; a < hi; a++) { - unsigned b_min = firstbits_b_min(a, lo, hi, min_separation); - mp_add_integer_into(total, total, hi - b_min); - } + size_t nfactors; + mp_int **factors = pcs_get_known_prime_factors(pcs, &nfactors); + PockleStatus st = pockle_add_prime( + ppc->pockle, p, factors, nfactors, witness); + + if (st != POCKLE_OK) { + debug_f("provable_step proof failed %d", (int)st); + + /* + * Check by assertion that the error status is not one of + * the ones we ought to have ruled out already by + * construction. If there's a bug in this code that means + * we can _never_ pass this test (e.g. picking products of + * factors that never quite reach cbrt(n)), we'd rather + * fail an assertion than loop forever. + */ + assert(st == POCKLE_DISCRIMINANT_IS_SQUARE || + st == POCKLE_WITNESS_POWER_IS_1 || + st == POCKLE_WITNESS_POWER_NOT_COPRIME); - /* - * Make up a random number in the range [0,2*total). - */ - mp_int *mlo = mp_from_integer(0), *mhi = mp_new(32); - mp_lshift_fixed_into(mhi, total, 1); - mp_int *randval = mp_random_in_range(mlo, mhi); - mp_free(mlo); - mp_free(mhi); + mp_free(p); + if (witness) + mp_free(witness); + continue; + } - /* - * Use the low bit of randval as our swap indicator, leaving the - * rest of it in the range [0,total). - */ - unsigned swap = mp_get_bit(randval, 0); - mp_rshift_fixed_into(randval, randval, 1); + mp_free(witness); + pcs_free(pcs); + debug_f_mp("ppgi(%u) done, got ", p, bits); + progress_report(prog, progress_origin + progress_scale); + return p; + } +} - /* - * Now do the same counting loop again to make the actual choice. - */ - a = b = 0; - for (unsigned a_candidate = lo; a_candidate < hi; a_candidate++) { - unsigned b_min = firstbits_b_min(a_candidate, lo, hi, min_separation); - unsigned limit = hi - b_min; +static mp_int *provableprime_generate( + PrimeGenerationContext *ctx, + PrimeCandidateSource *pcs, ProgressReceiver *prog) +{ + ProvablePrimeContext *ppc = container_of(ctx, ProvablePrimeContext, pgc); + mp_int *p = provableprime_generate_inner(ppc, pcs, prog, 0.0, 1.0); + + return p; +} - unsigned b_candidate = b_min + mp_get_integer(randval); - unsigned use_it = 1 ^ mp_hs_integer(randval, limit); - a ^= (a ^ a_candidate) & -use_it; - b ^= (b ^ b_candidate) & -use_it; +static inline strbuf *provableprime_mpu_certificate( + PrimeGenerationContext *ctx, mp_int *p) +{ + ProvablePrimeContext *ppc = container_of(ctx, ProvablePrimeContext, pgc); + return pockle_mpu(ppc->pockle, p); +} - mp_sub_integer_into(randval, randval, limit); +#define DECLARE_POLICY(name, policy) \ + static const struct ProvablePrimePolicyExtra \ + pppextra_##name = {policy}; \ + const PrimeGenerationPolicy name = { \ + provableprime_add_progress_phase, \ + provableprime_new_context, \ + provableprime_free_context, \ + provableprime_generate, \ + provableprime_mpu_certificate, \ + &pppextra_##name, \ } - mp_free(randval); - mp_free(total); +DECLARE_POLICY(primegen_provable_fast, SPP_FAST); +DECLARE_POLICY(primegen_provable_maurer_simple, SPP_MAURER_SIMPLE); +DECLARE_POLICY(primegen_provable_maurer_complex, SPP_MAURER_COMPLEX); - /* - * Check everything came out right. - */ - assert(lo <= a); - assert(a < hi); - assert(lo <= b); - assert(b < hi); - assert(a * b >= minproduct); - assert(b >= a + min_separation); +/* ---------------------------------------------------------------------- + * Reusable null implementation of the progress-reporting API. + */ +static inline ProgressPhase null_progress_add(void) { + ProgressPhase ph = { .n = 0 }; + return ph; +} +ProgressPhase null_progress_add_linear( + ProgressReceiver *prog, double c) { return null_progress_add(); } +ProgressPhase null_progress_add_probabilistic( + ProgressReceiver *prog, double c, double p) { return null_progress_add(); } +void null_progress_ready(ProgressReceiver *prog) {} +void null_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase) {} +void null_progress_report(ProgressReceiver *prog, double progress) {} +void null_progress_report_attempt(ProgressReceiver *prog) {} +void null_progress_report_phase_complete(ProgressReceiver *prog) {} +const ProgressReceiverVtable null_progress_vt = { + .add_linear = null_progress_add_linear, + .add_probabilistic = null_progress_add_probabilistic, + .ready = null_progress_ready, + .start_phase = null_progress_start_phase, + .report = null_progress_report, + .report_attempt = null_progress_report_attempt, + .report_phase_complete = null_progress_report_phase_complete, +}; + +/* ---------------------------------------------------------------------- + * Helper function for progress estimation. + */ + +double estimate_modexp_cost(unsigned bits) +{ /* - * Last-minute optional swap of a and b. + * A modexp of n bits goes roughly like O(n^2.58), on the grounds + * that our modmul is O(n^1.58) (Karatsuba) and you need O(n) of + * them in a modexp. */ - unsigned diff = (a ^ b) & (-swap); - a ^= diff; - b ^= diff; - - *one = a; - *two = b; + return pow(bits, 2.58); } diff --git a/sshprng.c b/sshprng.c index ea40c99..58df994 100644 --- a/sshprng.c +++ b/sshprng.c @@ -7,7 +7,7 @@ #include "putty.h" #include "ssh.h" -#include "mpint.h" +#include "mpint_i.h" #ifdef PRNG_DIAGNOSTICS #define prngdebug debug @@ -55,17 +55,9 @@ struct prng_impl { * into it. The counter-mode generation is achieved by copying * that hash object, appending the counter value to the copy, and * calling ssh_hash_final. - * - * pending_output is a buffer of size equal to the hash length, - * which receives each of those hashes as it's generated. The - * bytes of the hash are returned in reverse order, just because - * that made it marginally easier to deal with the - * pending_output_remaining field. */ ssh_hash *generator; - mp_int *counter; - uint8_t *pending_output; - size_t pending_output_remaining; + BignumInt counter[128 / BIGNUM_INT_BITS]; /* * When re-seeding the generator, you call prng_seed_begin(), @@ -121,9 +113,7 @@ prng *prng_new(const ssh_hashalg *hashalg) pi->hashalg = hashalg; pi->keymaker = NULL; pi->generator = NULL; - pi->pending_output = snewn(pi->hashalg->hlen, uint8_t); - pi->pending_output_remaining = 0; - pi->counter = mp_new(128); + memset(pi->counter, 0, sizeof(pi->counter)); for (size_t i = 0; i < NCOLLECTORS; i++) pi->collectors[i] = ssh_hash_new(pi->hashalg); pi->until_reseed = 0; @@ -138,8 +128,7 @@ void prng_free(prng *pr) { prng_impl *pi = container_of(pr, prng_impl, Prng); - sfree(pi->pending_output); - mp_free(pi->counter); + smemclr(pi->counter, sizeof(pi->counter)); for (size_t i = 0; i < NCOLLECTORS; i++) ssh_hash_free(pi->collectors[i]); if (pi->generator) @@ -177,13 +166,14 @@ static void prng_seed_BinarySink_write( prng *pr = BinarySink_DOWNCAST(bs, prng); prng_impl *pi = container_of(pr, prng_impl, Prng); assert(pi->keymaker); - prngdebug("prng: got %zu bytes of seed\n", len); + prngdebug("prng: got %"SIZEu" bytes of seed\n", len); put_data(pi->keymaker, data, len); } void prng_seed_finish(prng *pr) { prng_impl *pi = container_of(pr, prng_impl, Prng); + unsigned char buf[MAX_HASH_LEN]; assert(pi->keymaker); @@ -192,7 +182,7 @@ void prng_seed_finish(prng *pr) /* * Actually generate the key. */ - ssh_hash_final(pi->keymaker, pi->pending_output); + ssh_hash_final(pi->keymaker, buf); pi->keymaker = NULL; /* @@ -201,44 +191,48 @@ void prng_seed_finish(prng *pr) */ assert(!pi->generator); pi->generator = ssh_hash_new(pi->hashalg); - put_data(pi->generator, pi->pending_output, pi->hashalg->hlen); - smemclr(pi->pending_output, pi->hashalg->hlen); + put_data(pi->generator, buf, pi->hashalg->hlen); pi->until_reseed = RESEED_DATA_SIZE; pi->last_reseed_time = prng_reseed_time_ms(); - pi->pending_output_remaining = 0; + + smemclr(buf, sizeof(buf)); } -static inline void prng_generate(prng_impl *pi) +static inline void prng_generate(prng_impl *pi, void *outbuf) { ssh_hash *h = ssh_hash_copy(pi->generator); prngdebug("prng_generate\n"); - put_byte(h, 'G'); - put_mp_ssh2(h, pi->counter); - mp_add_integer_into(pi->counter, pi->counter, 1); - ssh_hash_final(h, pi->pending_output); - pi->pending_output_remaining = pi->hashalg->hlen; + for (unsigned i = 0; i < 128; i += 8) + put_byte(h, pi->counter[i/BIGNUM_INT_BITS] >> (i%BIGNUM_INT_BITS)); + BignumCarry c = 1; + for (unsigned i = 0; i < lenof(pi->counter); i++) + BignumADC(pi->counter[i], c, pi->counter[i], 0, c); + ssh_hash_final(h, outbuf); } void prng_read(prng *pr, void *vout, size_t size) { prng_impl *pi = container_of(pr, prng_impl, Prng); + unsigned char buf[MAX_HASH_LEN]; assert(!pi->keymaker); - prngdebug("prng_read %zu\n", size); + prngdebug("prng_read %"SIZEu"\n", size); uint8_t *out = (uint8_t *)vout; - for (; size > 0; size--) { - if (pi->pending_output_remaining == 0) - prng_generate(pi); - pi->pending_output_remaining--; - *out++ = pi->pending_output[pi->pending_output_remaining]; - pi->pending_output[pi->pending_output_remaining] = 0; + while (size > 0) { + prng_generate(pi, buf); + size_t to_use = size > pi->hashalg->hlen ? pi->hashalg->hlen : size; + memcpy(out, buf, to_use); + out += to_use; + size -= to_use; } + smemclr(buf, sizeof(buf)); + prng_seed_begin(&pi->Prng); prng_seed_finish(&pi->Prng); } @@ -256,7 +250,7 @@ void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data) index++; } - prngdebug("prng_add_entropy source=%u size=%zu -> collector %zi\n", + prngdebug("prng_add_entropy source=%u size=%"SIZEu" -> collector %zi\n", source_id, data.len, index); put_datapl(pi->collectors[index], data); @@ -269,18 +263,19 @@ void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data) prng_reseed_time_ms() - pi->last_reseed_time >= 100) { prng_seed_begin(&pi->Prng); + unsigned char buf[MAX_HASH_LEN]; uint32_t reseed_index = ++pi->reseeds; prngdebug("prng entropy reseed #%"PRIu32"\n", reseed_index); for (size_t i = 0; i < NCOLLECTORS; i++) { - prngdebug("emptying collector %zu\n", i); - ssh_hash_final(pi->collectors[i], pi->pending_output); - put_data(&pi->Prng, pi->pending_output, pi->hashalg->hlen); - pi->collectors[i] = ssh_hash_new(pi->hashalg); + prngdebug("emptying collector %"SIZEu"\n", i); + ssh_hash_digest(pi->collectors[i], buf); + put_data(&pi->Prng, buf, pi->hashalg->hlen); + ssh_hash_reset(pi->collectors[i]); if (reseed_index & 1) break; reseed_index >>= 1; } - + smemclr(buf, sizeof(buf)); prng_seed_finish(&pi->Prng); } } diff --git a/sshpubk.c b/sshpubk.c index 442fb5e..3fad887 100644 --- a/sshpubk.c +++ b/sshpubk.c @@ -6,24 +6,17 @@ */ #include +#include +#include #include #include +#include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "misc.h" -#define rsa_signature "SSH PRIVATE KEY FILE FORMAT 1.1\n" - -#define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\ - (x)-'a'<26 ? (x)-'a'+26 :\ - (x)-'0'<10 ? (x)-'0'+52 :\ - (x)=='+' ? 62 : \ - (x)=='/' ? 63 : 0 ) - -static int key_type_fp(FILE *fp); - /* * Fairly arbitrary size limit on any public or private key blob. * Chosen to match AGENT_MAX_MSGLEN, on the basis that any key too @@ -36,39 +29,140 @@ static int key_type_fp(FILE *fp); #define MAX_KEY_BLOB_SIZE 262144 #define MAX_KEY_BLOB_LINES (MAX_KEY_BLOB_SIZE / 48) -static int rsa_ssh1_load_main(FILE * fp, RSAKey *key, bool pub_only, - char **commentptr, const char *passphrase, - const char **error) +/* + * Corresponding limit on the size of a key _file_ itself, based on + * base64-encoding the key blob and then adding a few Kb for + * surrounding metadata. + */ +#define MAX_KEY_FILE_SIZE (MAX_KEY_BLOB_SIZE * 4 / 3 + 4096) + +static const ptrlen rsa1_signature = + PTRLEN_DECL_LITERAL("SSH PRIVATE KEY FILE FORMAT 1.1\n\0"); + +#define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\ + (x)-'a'<26 ? (x)-'a'+26 :\ + (x)-'0'<10 ? (x)-'0'+52 :\ + (x)=='+' ? 62 : \ + (x)=='/' ? 63 : 0 ) + +LoadedFile *lf_new(size_t max_size) { - strbuf *buf; - int ciphertype; - int ret = 0; - ptrlen comment; - BinarySource src[1]; + LoadedFile *lf = snew_plus(LoadedFile, max_size); + lf->data = snew_plus_get_aux(lf); + lf->len = 0; + lf->max_size = max_size; + return lf; +} - *error = NULL; +void lf_free(LoadedFile *lf) +{ + smemclr(lf->data, lf->max_size); + smemclr(lf, sizeof(LoadedFile)); + sfree(lf); +} - /* Slurp the whole file (minus the header) into a buffer. */ - buf = strbuf_new_nm(); - { - int ch; - while ((ch = fgetc(fp)) != EOF) - put_byte(buf, ch); +LoadFileStatus lf_load_fp(LoadedFile *lf, FILE *fp) +{ + lf->len = 0; + while (lf->len < lf->max_size) { + size_t retd = fread(lf->data + lf->len, 1, lf->max_size - lf->len, fp); + if (ferror(fp)) + return LF_ERROR; + + if (retd == 0) + break; + + lf->len += retd; } + + LoadFileStatus status = LF_OK; + + if (lf->len == lf->max_size) { + /* The file might be too long to fit in our fixed-size + * structure. Try reading one more byte, to check. */ + if (fgetc(fp) != EOF) + status = LF_TOO_BIG; + } + + BinarySource_INIT(lf, lf->data, lf->len); + + return status; +} + +LoadFileStatus lf_load(LoadedFile *lf, const Filename *filename) +{ + FILE *fp = f_open(filename, "rb", false); + if (!fp) + return LF_ERROR; + + LoadFileStatus status = lf_load_fp(lf, fp); fclose(fp); + return status; +} - BinarySource_BARE_INIT(src, buf->u, buf->len); +static inline bool lf_load_keyfile_helper(LoadFileStatus status, + const char **errptr) +{ + const char *error; + switch (status) { + case LF_OK: + return true; + case LF_TOO_BIG: + error = "file is too large to be a key file"; + break; + case LF_ERROR: + error = strerror(errno); + break; + default: + unreachable("bad status value in lf_load_keyfile_helper"); + } + if (errptr) + *errptr = error; + return false; +} - *error = "file format error"; +LoadedFile *lf_load_keyfile(const Filename *filename, const char **errptr) +{ + LoadedFile *lf = lf_new(MAX_KEY_FILE_SIZE); + if (!lf_load_keyfile_helper(lf_load(lf, filename), errptr)) { + lf_free(lf); + return NULL; + } + return lf; +} - /* - * A zero byte. (The signature includes a terminating NUL, which - * we haven't gone past yet because we read it using fgets which - * stopped after the \n.) - */ - if (get_byte(src) != 0) +LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr) +{ + LoadedFile *lf = lf_new(MAX_KEY_FILE_SIZE); + if (!lf_load_keyfile_helper(lf_load_fp(lf, fp), errptr)) { + lf_free(lf); + return NULL; + } + return lf; +} + +static bool expect_signature(BinarySource *src, ptrlen realsig) +{ + ptrlen thissig = get_data(src, realsig.len); + return !get_err(src) && ptrlen_eq_ptrlen(realsig, thissig); +} + +static int rsa1_load_s_internal(BinarySource *src, RSAKey *key, bool pub_only, + char **commentptr, const char *passphrase, + const char **error) +{ + strbuf *buf = NULL; + int ciphertype; + int ret = 0; + ptrlen comment; + + *error = "not an SSH-1 RSA file"; + + if (!expect_signature(src, rsa1_signature)) goto end; + *error = "file format error"; + /* One byte giving encryption type, and one reserved uint32. */ ciphertype = get_byte(src); if (ciphertype != 0 && ciphertype != SSH1_CIPHER_3DES) @@ -101,15 +195,19 @@ static int rsa_ssh1_load_main(FILE * fp, RSAKey *key, bool pub_only, * Decrypt remainder of buffer. */ if (ciphertype) { - unsigned char keybuf[16]; - size_t enclen = buf->len - src->pos; - + size_t enclen = get_avail(src); if (enclen & 7) goto end; + buf = strbuf_new_nm(); + put_datapl(buf, get_data(src, enclen)); + + unsigned char keybuf[16]; hash_simple(&ssh_md5, ptrlen_from_asciz(passphrase), keybuf); - des3_decrypt_pubkey(keybuf, buf->u + src->pos, enclen); + des3_decrypt_pubkey(keybuf, buf->u, enclen); smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ + + BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(buf)); } /* @@ -142,91 +240,63 @@ static int rsa_ssh1_load_main(FILE * fp, RSAKey *key, bool pub_only, *error = "rsa_verify failed"; freersakey(key); ret = 0; - } else + } else { + *error = NULL; ret = 1; + } end: - strbuf_free(buf); + if (buf) + strbuf_free(buf); return ret; } -int rsa_ssh1_loadkey(const Filename *filename, RSAKey *key, - const char *passphrase, const char **errorstr) +int rsa1_load_s(BinarySource *src, RSAKey *key, + const char *passphrase, const char **errstr) { - FILE *fp; - char buf[64]; - int ret = 0; - const char *error = NULL; - - fp = f_open(filename, "rb", false); - if (!fp) { - error = "can't open file"; - goto end; - } - - /* - * Read the first line of the file and see if it's a v1 private - * key file. - */ - if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { - /* - * This routine will take care of calling fclose() for us. - */ - ret = rsa_ssh1_load_main(fp, key, false, NULL, passphrase, &error); - fp = NULL; - goto end; - } + return rsa1_load_s_internal(src, key, false, NULL, passphrase, errstr); +} - /* - * Otherwise, we have nothing. Return empty-handed. - */ - error = "not an SSH-1 RSA file"; +int rsa1_load_f(const Filename *filename, RSAKey *key, + const char *passphrase, const char **errstr) +{ + LoadedFile *lf = lf_load_keyfile(filename, errstr); + if (!lf) + return false; - end: - if (fp) - fclose(fp); - if ((ret != 1) && errorstr) - *errorstr = error; - return ret; + int toret = rsa1_load_s(BinarySource_UPCAST(lf), key, passphrase, errstr); + lf_free(lf); + return toret; } /* * See whether an RSA key is encrypted. Return its comment field as * well. */ -bool rsa_ssh1_encrypted(const Filename *filename, char **comment) +bool rsa1_encrypted_s(BinarySource *src, char **comment) { - FILE *fp; - char buf[64]; + const char *dummy; + return rsa1_load_s_internal(src, NULL, false, comment, NULL, &dummy) == 1; +} - fp = f_open(filename, "rb", false); - if (!fp) - return false; /* doesn't even exist */ +bool rsa1_encrypted_f(const Filename *filename, char **comment) +{ + LoadedFile *lf = lf_load_keyfile(filename, NULL); + if (!lf) + return false; /* couldn't even open the file */ - /* - * Read the first line of the file and see if it's a v1 private - * key file. - */ - if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { - const char *dummy; - /* - * This routine will take care of calling fclose() for us. - */ - return rsa_ssh1_load_main(fp, NULL, false, comment, NULL, &dummy) == 1; - } - fclose(fp); - return false; /* wasn't the right kind of file */ + bool toret = rsa1_encrypted_s(BinarySource_UPCAST(lf), comment); + lf_free(lf); + return toret; } /* * Read the public part of an SSH-1 RSA key from a file (public or * private), and generate its public blob in exponent-first order. */ -int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs, - char **commentptr, const char **errorstr) +int rsa1_loadpub_s(BinarySource *src, BinarySink *bs, + char **commentptr, const char **errorstr) { - FILE *fp; - char buf[64]; RSAKey key; int ret; const char *error = NULL; @@ -234,32 +304,26 @@ int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs, /* Default return if we fail. */ ret = 0; - fp = f_open(filename, "rb", false); - if (!fp) { - error = "can't open file"; - goto end; - } + bool is_privkey_file = expect_signature(src, rsa1_signature); + BinarySource_REWIND(src); - /* - * Read the first line of the file and see if it's a v1 private - * key file. - */ - if (fgets(buf, sizeof(buf), fp) && !strcmp(buf, rsa_signature)) { + if (is_privkey_file) { + /* + * Load just the public half from an SSH-1 private key file. + */ memset(&key, 0, sizeof(key)); - if (rsa_ssh1_load_main(fp, &key, true, commentptr, NULL, &error)) { + if (rsa1_load_s_internal(src, &key, true, commentptr, NULL, &error)) { rsa_ssh1_public_blob(bs, &key, RSA_SSH1_EXPONENT_FIRST); freersakey(&key); ret = 1; } - fp = NULL; /* rsa_ssh1_load_main unconditionally closes fp */ } else { /* * Try interpreting the file as an SSH-1 public key. */ char *line, *p, *bitsp, *expp, *modp, *commentp; - rewind(fp); - line = chomp(fgetline(fp)); + line = mkstr(get_chomped_line(src)); p = line; bitsp = p; @@ -300,7 +364,6 @@ int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs, rsa_ssh1_public_blob(bs, &key, RSA_SSH1_EXPONENT_FIRST); freersakey(&key); sfree(line); - fclose(fp); return 1; not_public_either: @@ -309,27 +372,33 @@ int rsa_ssh1_loadpub(const Filename *filename, BinarySink *bs, } end: - if (fp) - fclose(fp); if ((ret != 1) && errorstr) *errorstr = error; return ret; } -/* - * Save an RSA key file. Return true on success. - */ -bool rsa_ssh1_savekey(const Filename *filename, RSAKey *key, - char *passphrase) +int rsa1_loadpub_f(const Filename *filename, BinarySink *bs, + char **commentptr, const char **errorstr) +{ + LoadedFile *lf = lf_load_keyfile(filename, errorstr); + if (!lf) + return 0; + + int toret = rsa1_loadpub_s(BinarySource_UPCAST(lf), bs, + commentptr, errorstr); + lf_free(lf); + return toret; +} + +strbuf *rsa1_save_sb(RSAKey *key, const char *passphrase) { strbuf *buf = strbuf_new_nm(); int estart; - FILE *fp; /* * The public part of the key. */ - put_data(buf, rsa_signature, sizeof(rsa_signature)); + put_datapl(buf, rsa1_signature); put_byte(buf, passphrase ? SSH1_CIPHER_3DES : 0); /* encryption type */ put_uint32(buf, 0); /* reserved */ rsa_ssh1_public_blob(BinarySink_UPCAST(buf), key, @@ -372,121 +441,49 @@ bool rsa_ssh1_savekey(const Filename *filename, RSAKey *key, if (passphrase) { unsigned char keybuf[16]; - ssh_hash *h = ssh_hash_new(&ssh_md5); - put_data(h, passphrase, strlen(passphrase)); - ssh_hash_final(h, keybuf); + hash_simple(&ssh_md5, ptrlen_from_asciz(passphrase), keybuf); des3_encrypt_pubkey(keybuf, buf->u + estart, buf->len - estart); smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } - /* - * Done. Write the result to the file. - */ - fp = f_open(filename, "wb", true); - bool ret = false; - if (fp) { - ret = (fwrite(buf->u, 1, buf->len, fp) == (size_t) (buf->len)); - if (fclose(fp)) - ret = false; - } + return buf; +} + +/* + * Save an RSA key file. Return true on success. + */ +bool rsa1_save_f(const Filename *filename, RSAKey *key, const char *passphrase) +{ + FILE *fp = f_open(filename, "wb", true); + if (!fp) + return false; + + strbuf *buf = rsa1_save_sb(key, passphrase); + bool toret = fwrite(buf->s, 1, buf->len, fp) == buf->len; + if (fclose(fp)) + toret = false; strbuf_free(buf); - return ret; + return toret; } /* ---------------------------------------------------------------------- * SSH-2 private key load/store functions. - */ - -/* - * PuTTY's own format for SSH-2 keys is as follows: * - * The file is text. Lines are terminated by CRLF, although CR-only - * and LF-only are tolerated on input. - * - * The first line says "PuTTY-User-Key-File-2: " plus the name of the - * algorithm ("ssh-dss", "ssh-rsa" etc). - * - * The next line says "Encryption: " plus an encryption type. - * Currently the only supported encryption types are "aes256-cbc" - * and "none". - * - * The next line says "Comment: " plus the comment string. - * - * Next there is a line saying "Public-Lines: " plus a number N. - * The following N lines contain a base64 encoding of the public - * part of the key. This is encoded as the standard SSH-2 public key - * blob (with no initial length): so for RSA, for example, it will - * read - * - * string "ssh-rsa" - * mpint exponent - * mpint modulus - * - * Next, there is a line saying "Private-Lines: " plus a number N, - * and then N lines containing the (potentially encrypted) private - * part of the key. For the key type "ssh-rsa", this will be - * composed of - * - * mpint private_exponent - * mpint p (the larger of the two primes) - * mpint q (the smaller prime) - * mpint iqmp (the inverse of q modulo p) - * data padding (to reach a multiple of the cipher block size) - * - * And for "ssh-dss", it will be composed of - * - * mpint x (the private key parameter) - * [ string hash 20-byte hash of mpints p || q || g only in old format ] - * - * Finally, there is a line saying "Private-MAC: " plus a hex - * representation of a HMAC-SHA-1 of: - * - * string name of algorithm ("ssh-dss", "ssh-rsa") - * string encryption type - * string comment - * string public-blob - * string private-plaintext (the plaintext version of the - * private part, including the final - * padding) - * - * The key to the MAC is itself a SHA-1 hash of: - * - * data "putty-private-key-file-mac-key" - * data passphrase - * - * (An empty passphrase is used for unencrypted keys.) - * - * If the key is encrypted, the encryption key is derived from the - * passphrase by means of a succession of SHA-1 hashes. Each hash - * is the hash of: - * - * uint32 sequence-number - * data passphrase - * - * where the sequence-number increases from zero. As many of these - * hashes are used as necessary. - * - * For backwards compatibility with snapshots between 0.51 and - * 0.52, we also support the older key file format, which begins - * with "PuTTY-User-Key-File-1" (version number differs). In this - * format the Private-MAC: field only covers the private-plaintext - * field and nothing else (and without the 4-byte string length on - * the front too). Moreover, the Private-MAC: field can be replaced - * with a Private-Hash: field which is a plain SHA-1 hash instead of - * an HMAC (this was generated for unencrypted keys). + * PuTTY's own file format for SSH-2 keys is given in doc/ppk.but, aka + * the "PPK file format" appendix in the PuTTY manual. */ -static bool read_header(FILE * fp, char *header) +static bool read_header(BinarySource *src, char *header) { int len = 39; int c; while (1) { - c = fgetc(fp); - if (c == '\n' || c == '\r' || c == EOF) + c = get_byte(src); + if (c == '\n' || c == '\r' || get_err(src)) return false; /* failure */ if (c == ':') { - c = fgetc(fp); + c = get_byte(src); if (c != ' ') return false; *header = '\0'; @@ -500,17 +497,17 @@ static bool read_header(FILE * fp, char *header) return false; /* failure */ } -static char *read_body(FILE * fp) +static char *read_body(BinarySource *src) { strbuf *buf = strbuf_new_nm(); while (1) { - int c = fgetc(fp); - if (c == '\r' || c == '\n' || c == EOF) { - if (c != EOF) { - c = fgetc(fp); - if (c != '\r' && c != '\n') - ungetc(c, fp); + int c = get_byte(src); + if (c == '\r' || c == '\n' || get_err(src)) { + if (!get_err(src)) { + c = get_byte(src); + if (c != '\r' && c != '\n' && !get_err(src)) + src->pos--; } return strbuf_to_str(buf); } @@ -518,7 +515,7 @@ static char *read_body(FILE * fp) } } -static bool read_blob(FILE *fp, int nlines, BinarySink *bs) +static bool read_blob(BinarySource *src, int nlines, BinarySink *bs) { unsigned char *blob; char *line; @@ -530,7 +527,7 @@ static bool read_blob(FILE *fp, int nlines, BinarySink *bs) blob = snewn(48 * nlines, unsigned char); for (i = 0; i < nlines; i++) { - line = read_body(fp); + line = read_body(src); if (!line) { sfree(blob); return false; @@ -562,22 +559,26 @@ static bool read_blob(FILE *fp, int nlines, BinarySink *bs) */ ssh2_userkey ssh2_wrong_passphrase = { NULL, NULL }; +const ssh_keyalg *const all_keyalgs[] = { + &ssh_rsa, + &ssh_rsa_sha256, + &ssh_rsa_sha512, + &ssh_dss, + &ssh_ecdsa_nistp256, + &ssh_ecdsa_nistp384, + &ssh_ecdsa_nistp521, + &ssh_ecdsa_ed25519, + &ssh_ecdsa_ed448, +}; +const size_t n_keyalgs = lenof(all_keyalgs); + const ssh_keyalg *find_pubkey_alg_len(ptrlen name) { - if (ptrlen_eq_string(name, "ssh-rsa")) - return &ssh_rsa; - else if (ptrlen_eq_string(name, "ssh-dss")) - return &ssh_dss; - else if (ptrlen_eq_string(name, "ecdsa-sha2-nistp256")) - return &ssh_ecdsa_nistp256; - else if (ptrlen_eq_string(name, "ecdsa-sha2-nistp384")) - return &ssh_ecdsa_nistp384; - else if (ptrlen_eq_string(name, "ecdsa-sha2-nistp521")) - return &ssh_ecdsa_nistp521; - else if (ptrlen_eq_string(name, "ssh-ed25519")) - return &ssh_ecdsa_ed25519; - else - return NULL; + for (size_t i = 0; i < n_keyalgs; i++) + if (ptrlen_eq_string(name, all_keyalgs[i]->ssh_id)) + return all_keyalgs[i]; + + return NULL; } const ssh_keyalg *find_pubkey_alg(const char *name) @@ -585,17 +586,85 @@ const ssh_keyalg *find_pubkey_alg(const char *name) return find_pubkey_alg_len(ptrlen_from_asciz(name)); } -static void ssh2_ppk_derivekey(ptrlen passphrase, uint8_t *key) +struct ppk_cipher { + const char *name; + size_t blocklen, keylen, ivlen; +}; +static const struct ppk_cipher ppk_cipher_none = { "none", 1, 0, 0 }; +static const struct ppk_cipher ppk_cipher_aes256_cbc = { "aes256-cbc", 16, 32, 16 }; + +static void ssh2_ppk_derive_keys( + unsigned fmt_version, const struct ppk_cipher *ciphertype, + ptrlen passphrase, strbuf *storage, ptrlen *cipherkey, ptrlen *cipheriv, + ptrlen *mackey, ptrlen passphrase_salt, ppk_save_parameters *params) { - ssh_hash *h; - h = ssh_hash_new(&ssh_sha1); - put_uint32(h, 0); - put_datapl(h, passphrase); - ssh_hash_final(h, key + 0); - h = ssh_hash_new(&ssh_sha1); - put_uint32(h, 1); - put_datapl(h, passphrase); - ssh_hash_final(h, key + 20); + size_t mac_keylen; + + switch (fmt_version) { + case 3: { + if (ciphertype->keylen == 0) { + mac_keylen = 0; + break; + } + ptrlen empty = PTRLEN_LITERAL(""); + + mac_keylen = 32; + + uint32_t taglen = ciphertype->keylen + ciphertype->ivlen + mac_keylen; + + if (params->argon2_passes_auto) { + uint32_t passes; + + argon2_choose_passes( + params->argon2_flavour, params->argon2_mem, + params->argon2_milliseconds, &passes, + params->argon2_parallelism, taglen, + passphrase, passphrase_salt, empty, empty, storage); + + params->argon2_passes_auto = false; + params->argon2_passes = passes; + } else { + argon2(params->argon2_flavour, params->argon2_mem, + params->argon2_passes, params->argon2_parallelism, taglen, + passphrase, passphrase_salt, empty, empty, storage); + } + + break; + } + + case 2: + case 1: { + /* Counter-mode iteration to generate cipher key data. */ + for (unsigned ctr = 0; ctr * 20 < ciphertype->keylen; ctr++) { + ssh_hash *h = ssh_hash_new(&ssh_sha1); + put_uint32(h, ctr); + put_datapl(h, passphrase); + ssh_hash_final(h, strbuf_append(storage, 20)); + } + strbuf_shrink_to(storage, ciphertype->keylen); + + /* In this version of the format, the CBC IV was always all 0. */ + put_padding(storage, ciphertype->ivlen, 0); + + /* Completely separate hash for the MAC key. */ + ssh_hash *h = ssh_hash_new(&ssh_sha1); + mac_keylen = ssh_hash_alg(h)->hlen; + put_datapl(h, PTRLEN_LITERAL("putty-private-key-file-mac-key")); + put_datapl(h, passphrase); + ssh_hash_final(h, strbuf_append(storage, mac_keylen)); + + break; + } + + default: + unreachable("bad format version in ssh2_ppk_derive_keys"); + } + + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(storage)); + *cipherkey = get_data(src, ciphertype->keylen); + *cipheriv = get_data(src, ciphertype->ivlen); + *mackey = get_data(src, mac_keylen); } static int userkey_parse_line_counter(const char *text) @@ -608,41 +677,51 @@ static int userkey_parse_line_counter(const char *text) return -1; } -ssh2_userkey *ssh2_load_userkey( - const Filename *filename, const char *passphrase, const char **errorstr) +static bool str_to_uint32_t(const char *s, uint32_t *out) +{ + char *endptr; + unsigned long converted = strtoul(s, &endptr, 10); + if (*s && !*endptr && converted <= ~(uint32_t)0) { + *out = converted; + return true; + } else { + return false; + } +} + +ssh2_userkey *ppk_load_s(BinarySource *src, const char *passphrase, + const char **errorstr) { - FILE *fp; char header[40], *b, *encryption, *comment, *mac; const ssh_keyalg *alg; ssh2_userkey *ret; - int cipher, cipherblk; - strbuf *public_blob, *private_blob; + strbuf *public_blob, *private_blob, *cipher_mac_keys_blob; + strbuf *passphrase_salt = strbuf_new(); + ptrlen cipherkey, cipheriv, mackey; + const struct ppk_cipher *ciphertype; int i; - bool is_mac, old_fmt; - int passlen = passphrase ? strlen(passphrase) : 0; + bool is_mac; + unsigned fmt_version; const char *error = NULL; + ppk_save_parameters params; ret = NULL; /* return NULL for most errors */ encryption = comment = mac = NULL; - public_blob = private_blob = NULL; - - fp = f_open(filename, "rb", false); - if (!fp) { - error = "can't open file"; - goto error; - } + public_blob = private_blob = cipher_mac_keys_blob = NULL; /* Read the first header line which contains the key type. */ - if (!read_header(fp, header)) { + if (!read_header(src, header)) { error = "no header line found in key file"; goto error; } - if (0 == strcmp(header, "PuTTY-User-Key-File-2")) { - old_fmt = false; + if (0 == strcmp(header, "PuTTY-User-Key-File-3")) { + fmt_version = 3; + } else if (0 == strcmp(header, "PuTTY-User-Key-File-2")) { + fmt_version = 2; } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) { /* this is an old key file; warn and then continue */ old_keyfile_warning(); - old_fmt = true; + fmt_version = 1; } else if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) { /* this is a key file FROM THE FUTURE; refuse it, but with a * more specific error message than the generic one below */ @@ -653,7 +732,7 @@ ssh2_userkey *ssh2_load_userkey( goto error; } error = "file format error"; - if ((b = read_body(fp)) == NULL) + if ((b = read_body(src)) == NULL) goto error; /* Select key algorithm structure. */ alg = find_pubkey_alg(b); @@ -664,94 +743,165 @@ ssh2_userkey *ssh2_load_userkey( sfree(b); /* Read the Encryption header line. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) + if (!read_header(src, header) || 0 != strcmp(header, "Encryption")) goto error; - if ((encryption = read_body(fp)) == NULL) + if ((encryption = read_body(src)) == NULL) goto error; if (!strcmp(encryption, "aes256-cbc")) { - cipher = 1; - cipherblk = 16; + ciphertype = &ppk_cipher_aes256_cbc; } else if (!strcmp(encryption, "none")) { - cipher = 0; - cipherblk = 1; + ciphertype = &ppk_cipher_none; } else { goto error; } /* Read the Comment header line. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) + if (!read_header(src, header) || 0 != strcmp(header, "Comment")) goto error; - if ((comment = read_body(fp)) == NULL) + if ((comment = read_body(src)) == NULL) goto error; + memset(¶ms, 0, sizeof(params)); /* in particular, sets + * passes_auto=false */ + /* Read the Public-Lines header line and the public blob. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines")) + if (!read_header(src, header) || 0 != strcmp(header, "Public-Lines")) goto error; - if ((b = read_body(fp)) == NULL) + if ((b = read_body(src)) == NULL) goto error; i = userkey_parse_line_counter(b); sfree(b); if (i < 0) goto error; public_blob = strbuf_new(); - if (!read_blob(fp, i, BinarySink_UPCAST(public_blob))) + if (!read_blob(src, i, BinarySink_UPCAST(public_blob))) goto error; + if (fmt_version >= 3 && ciphertype->keylen != 0) { + /* Read Argon2 key derivation parameters. */ + if (!read_header(src, header) || 0 != strcmp(header, "Key-Derivation")) + goto error; + if ((b = read_body(src)) == NULL) + goto error; + if (!strcmp(b, "Argon2d")) { + params.argon2_flavour = Argon2d; + } else if (!strcmp(b, "Argon2i")) { + params.argon2_flavour = Argon2i; + } else if (!strcmp(b, "Argon2id")) { + params.argon2_flavour = Argon2id; + } else { + sfree(b); + goto error; + } + sfree(b); + + if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Memory")) + goto error; + if ((b = read_body(src)) == NULL) + goto error; + if (!str_to_uint32_t(b, ¶ms.argon2_mem)) { + sfree(b); + goto error; + } + sfree(b); + + if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Passes")) + goto error; + if ((b = read_body(src)) == NULL) + goto error; + if (!str_to_uint32_t(b, ¶ms.argon2_passes)) { + sfree(b); + goto error; + } + sfree(b); + + if (!read_header(src, header) || + 0 != strcmp(header, "Argon2-Parallelism")) + goto error; + if ((b = read_body(src)) == NULL) + goto error; + if (!str_to_uint32_t(b, ¶ms.argon2_parallelism)) { + sfree(b); + goto error; + } + sfree(b); + + if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Salt")) + goto error; + if ((b = read_body(src)) == NULL) + goto error; + for (size_t i = 0; b[i]; i += 2) { + if (isxdigit((unsigned char)b[i]) && b[i+1] && + isxdigit((unsigned char)b[i+1])) { + char s[3]; + s[0] = b[i]; + s[1] = b[i+1]; + s[2] = '\0'; + put_byte(passphrase_salt, strtoul(s, NULL, 16)); + } else { + sfree(b); + goto error; + } + } + sfree(b); + } + /* Read the Private-Lines header line and the Private blob. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Private-Lines")) + if (!read_header(src, header) || 0 != strcmp(header, "Private-Lines")) goto error; - if ((b = read_body(fp)) == NULL) + if ((b = read_body(src)) == NULL) goto error; i = userkey_parse_line_counter(b); sfree(b); if (i < 0) goto error; private_blob = strbuf_new_nm(); - if (!read_blob(fp, i, BinarySink_UPCAST(private_blob))) + if (!read_blob(src, i, BinarySink_UPCAST(private_blob))) goto error; /* Read the Private-MAC or Private-Hash header line. */ - if (!read_header(fp, header)) + if (!read_header(src, header)) goto error; if (0 == strcmp(header, "Private-MAC")) { - if ((mac = read_body(fp)) == NULL) + if ((mac = read_body(src)) == NULL) goto error; is_mac = true; - } else if (0 == strcmp(header, "Private-Hash") && old_fmt) { - if ((mac = read_body(fp)) == NULL) + } else if (0 == strcmp(header, "Private-Hash") && fmt_version == 1) { + if ((mac = read_body(src)) == NULL) goto error; is_mac = false; } else goto error; - fclose(fp); - fp = NULL; + cipher_mac_keys_blob = strbuf_new(); + ssh2_ppk_derive_keys(fmt_version, ciphertype, + ptrlen_from_asciz(passphrase ? passphrase : ""), + cipher_mac_keys_blob, &cipherkey, &cipheriv, &mackey, + ptrlen_from_strbuf(passphrase_salt), ¶ms); /* * Decrypt the private blob. */ - if (cipher) { - unsigned char key[40]; - - if (!passphrase) - goto error; - if (private_blob->len % cipherblk) - goto error; - - ssh2_ppk_derivekey(ptrlen_from_asciz(passphrase), key); - aes256_decrypt_pubkey(key, private_blob->u, private_blob->len); + if (private_blob->len % ciphertype->blocklen) + goto error; + if (ciphertype == &ppk_cipher_aes256_cbc) { + aes256_decrypt_pubkey(cipherkey.ptr, cipheriv.ptr, + private_blob->u, private_blob->len); } /* * Verify the MAC. */ { - char realmac[41]; - unsigned char binary[20]; + unsigned char binary[32]; + char realmac[sizeof(binary) * 2 + 1]; strbuf *macdata; bool free_macdata; - if (old_fmt) { + const ssh2_macalg *mac_alg = + fmt_version <= 2 ? &ssh_hmac_sha1 : &ssh_hmac_sha256; + + if (fmt_version == 1) { /* MAC (or hash) only covers the private blob. */ macdata = private_blob; free_macdata = false; @@ -768,25 +918,14 @@ ssh2_userkey *ssh2_load_userkey( } if (is_mac) { - ssh_hash *hash; ssh2_mac *mac; - unsigned char mackey[20]; - char header[] = "putty-private-key-file-mac-key"; - - hash = ssh_hash_new(&ssh_sha1); - put_data(hash, header, sizeof(header)-1); - if (cipher && passphrase) - put_data(hash, passphrase, passlen); - ssh_hash_final(hash, mackey); - mac = ssh2_mac_new(&ssh_hmac_sha1, NULL); - ssh2_mac_setkey(mac, make_ptrlen(mackey, 20)); + mac = ssh2_mac_new(mac_alg, NULL); + ssh2_mac_setkey(mac, mackey); ssh2_mac_start(mac); put_data(mac, macdata->s, macdata->len); ssh2_mac_genresult(mac, binary); ssh2_mac_free(mac); - - smemclr(mackey, sizeof(mackey)); } else { hash_simple(&ssh_sha1, ptrlen_from_strbuf(macdata), binary); } @@ -794,13 +933,13 @@ ssh2_userkey *ssh2_load_userkey( if (free_macdata) strbuf_free(macdata); - for (i = 0; i < 20; i++) + for (i = 0; i < mac_alg->len; i++) sprintf(realmac + 2 * i, "%02x", binary[i]); if (strcmp(mac, realmac)) { /* An incorrect MAC is an unconditional Error if the key is * unencrypted. Otherwise, it means Wrong Passphrase. */ - if (cipher) { + if (ciphertype->keylen != 0) { error = "wrong passphrase"; ret = SSH2_WRONG_PASSPHRASE; } else { @@ -810,14 +949,13 @@ ssh2_userkey *ssh2_load_userkey( goto error; } } - sfree(mac); - mac = NULL; /* * Create and return the key. */ ret = snew(ssh2_userkey); ret->comment = comment; + comment = NULL; ret->key = ssh_key_new_priv( alg, ptrlen_from_strbuf(public_blob), ptrlen_from_strbuf(private_blob)); @@ -827,19 +965,12 @@ ssh2_userkey *ssh2_load_userkey( error = "createkey failed"; goto error; } - strbuf_free(public_blob); - strbuf_free(private_blob); - sfree(encryption); - if (errorstr) - *errorstr = NULL; - return ret; + error = NULL; /* * Error processing. */ error: - if (fp) - fclose(fp); if (comment) sfree(comment); if (encryption) @@ -850,14 +981,32 @@ ssh2_userkey *ssh2_load_userkey( strbuf_free(public_blob); if (private_blob) strbuf_free(private_blob); + if (cipher_mac_keys_blob) + strbuf_free(cipher_mac_keys_blob); + strbuf_free(passphrase_salt); if (errorstr) *errorstr = error; return ret; } -bool rfc4716_loadpub(FILE *fp, char **algorithm, - BinarySink *bs, - char **commentptr, const char **errorstr) +ssh2_userkey *ppk_load_f(const Filename *filename, const char *passphrase, + const char **errorstr) +{ + LoadedFile *lf = lf_load_keyfile(filename, errorstr); + ssh2_userkey *toret; + if (lf) { + toret = ppk_load_s(BinarySource_UPCAST(lf), passphrase, errorstr); + lf_free(lf); + } else { + toret = NULL; + *errorstr = "can't open file"; + } + return toret; +} + +static bool rfc4716_loadpub(BinarySource *src, char **algorithm, + BinarySink *bs, + char **commentptr, const char **errorstr) { const char *error; char *line, *colon, *value; @@ -868,7 +1017,7 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm, int base64bytes; int alglen; - line = chomp(fgetline(fp)); + line = mkstr(get_chomped_line(src)); if (!line || 0 != strcmp(line, "---- BEGIN SSH2 PUBLIC KEY ----")) { error = "invalid begin line in SSH-2 public key file"; goto error; @@ -876,7 +1025,7 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm, sfree(line); line = NULL; while (1) { - line = chomp(fgetline(fp)); + line = mkstr(get_chomped_line(src)); if (!line) { error = "truncated SSH-2 public key file"; goto error; @@ -936,7 +1085,7 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm, } } sfree(line); line = NULL; - line = chomp(fgetline(fp)); + line = mkstr(get_chomped_line(src)); } /* @@ -982,9 +1131,9 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm, return false; } -bool openssh_loadpub(FILE *fp, char **algorithm, - BinarySink *bs, - char **commentptr, const char **errorstr) +static bool openssh_loadpub(BinarySource *src, char **algorithm, + BinarySink *bs, + char **commentptr, const char **errorstr) { const char *error; char *line, *base64; @@ -993,7 +1142,7 @@ bool openssh_loadpub(FILE *fp, char **algorithm, int pubbloblen, pubblobsize; int alglen; - line = chomp(fgetline(fp)); + line = mkstr(get_chomped_line(src)); base64 = strchr(line, ' '); if (!base64) { @@ -1058,33 +1207,23 @@ bool openssh_loadpub(FILE *fp, char **algorithm, return false; } -bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, - BinarySink *bs, - char **commentptr, const char **errorstr) +bool ppk_loadpub_s(BinarySource *src, char **algorithm, BinarySink *bs, + char **commentptr, const char **errorstr) { - FILE *fp; char header[40], *b; const ssh_keyalg *alg; int type, i; const char *error = NULL; char *comment = NULL; - fp = f_open(filename, "rb", false); - if (!fp) { - error = "can't open file"; - goto error; - } - /* Initially, check if this is a public-only key file. Sometimes * we'll be asked to read a public blob from one of those. */ - type = key_type_fp(fp); + type = key_type_s(src); if (type == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) { - bool ret = rfc4716_loadpub(fp, algorithm, bs, commentptr, errorstr); - fclose(fp); + bool ret = rfc4716_loadpub(src, algorithm, bs, commentptr, errorstr); return ret; } else if (type == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { - bool ret = openssh_loadpub(fp, algorithm, bs, commentptr, errorstr); - fclose(fp); + bool ret = openssh_loadpub(src, algorithm, bs, commentptr, errorstr); return ret; } else if (type != SSH_KEYTYPE_SSH2) { error = "not a PuTTY SSH-2 private key"; @@ -1092,8 +1231,9 @@ bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, } /* Read the first header line which contains the key type. */ - if (!read_header(fp, header) - || (0 != strcmp(header, "PuTTY-User-Key-File-2") && + if (!read_header(src, header) + || (0 != strcmp(header, "PuTTY-User-Key-File-3") && + 0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { if (0 == strncmp(header, "PuTTY-User-Key-File-", 20)) error = "PuTTY key format too new"; @@ -1102,7 +1242,7 @@ bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, goto error; } error = "file format error"; - if ((b = read_body(fp)) == NULL) + if ((b = read_body(src)) == NULL) goto error; /* Select key algorithm structure. */ alg = find_pubkey_alg(b); @@ -1112,16 +1252,16 @@ bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, } /* Read the Encryption header line. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) + if (!read_header(src, header) || 0 != strcmp(header, "Encryption")) goto error; - if ((b = read_body(fp)) == NULL) + if ((b = read_body(src)) == NULL) goto error; sfree(b); /* we don't care */ /* Read the Comment header line. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) + if (!read_header(src, header) || 0 != strcmp(header, "Comment")) goto error; - if ((comment = read_body(fp)) == NULL) + if ((comment = read_body(src)) == NULL) goto error; if (commentptr) @@ -1130,18 +1270,17 @@ bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, sfree(comment); /* Read the Public-Lines header line and the public blob. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Public-Lines")) + if (!read_header(src, header) || 0 != strcmp(header, "Public-Lines")) goto error; - if ((b = read_body(fp)) == NULL) + if ((b = read_body(src)) == NULL) goto error; i = userkey_parse_line_counter(b); sfree(b); if (i < 0) goto error; - if (!read_blob(fp, i, bs)) + if (!read_blob(src, i, bs)) goto error; - fclose(fp); if (algorithm) *algorithm = dupstr(alg->ssh_id); return true; @@ -1150,8 +1289,6 @@ bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, * Error processing. */ error: - if (fp) - fclose(fp); if (errorstr) *errorstr = error; if (comment && commentptr) { @@ -1161,47 +1298,51 @@ bool ssh2_userkey_loadpub(const Filename *filename, char **algorithm, return false; } -bool ssh2_userkey_encrypted(const Filename *filename, char **commentptr) +bool ppk_loadpub_f(const Filename *filename, char **algorithm, BinarySink *bs, + char **commentptr, const char **errorstr) +{ + LoadedFile *lf = lf_load_keyfile(filename, errorstr); + if (!lf) + return false; + + bool toret = ppk_loadpub_s(BinarySource_UPCAST(lf), algorithm, bs, + commentptr, errorstr); + lf_free(lf); + return toret; +} + +bool ppk_encrypted_s(BinarySource *src, char **commentptr) { - FILE *fp; char header[40], *b, *comment; bool ret; if (commentptr) *commentptr = NULL; - fp = f_open(filename, "rb", false); - if (!fp) - return false; - if (!read_header(fp, header) - || (0 != strcmp(header, "PuTTY-User-Key-File-2") && + if (!read_header(src, header) + || (0 != strcmp(header, "PuTTY-User-Key-File-3") && + 0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { - fclose(fp); return false; } - if ((b = read_body(fp)) == NULL) { - fclose(fp); + if ((b = read_body(src)) == NULL) { return false; } sfree(b); /* we don't care about key type here */ /* Read the Encryption header line. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Encryption")) { - fclose(fp); + if (!read_header(src, header) || 0 != strcmp(header, "Encryption")) { return false; } - if ((b = read_body(fp)) == NULL) { - fclose(fp); + if ((b = read_body(src)) == NULL) { return false; } /* Read the Comment header line. */ - if (!read_header(fp, header) || 0 != strcmp(header, "Comment")) { - fclose(fp); + if (!read_header(src, header) || 0 != strcmp(header, "Comment")) { sfree(b); return true; } - if ((comment = read_body(fp)) == NULL) { - fclose(fp); + if ((comment = read_body(src)) == NULL) { sfree(b); return true; } @@ -1211,7 +1352,6 @@ bool ssh2_userkey_encrypted(const Filename *filename, char **commentptr) else sfree(comment); - fclose(fp); if (!strcmp(b, "aes256-cbc")) ret = true; else @@ -1220,13 +1360,28 @@ bool ssh2_userkey_encrypted(const Filename *filename, char **commentptr) return ret; } +bool ppk_encrypted_f(const Filename *filename, char **commentptr) +{ + LoadedFile *lf = lf_load_keyfile(filename, NULL); + if (!lf) { + if (commentptr) + *commentptr = NULL; + return false; + } + + bool toret = ppk_encrypted_s(BinarySource_UPCAST(lf), commentptr); + lf_free(lf); + return toret; +} + int base64_lines(int datalen) { /* When encoding, we use 64 chars/line, which equals 48 real chars. */ return (datalen + 47) / 48; } -void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl) +static void base64_encode_s(BinarySink *bs, const unsigned char *data, + int datalen, int cpl) { int linelen = 0; char out[4]; @@ -1240,26 +1395,82 @@ void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl) for (i = 0; i < 4; i++) { if (linelen >= cpl) { linelen = 0; - fputc('\n', fp); + put_byte(bs, '\n'); } - fputc(out[i], fp); + put_byte(bs, out[i]); linelen++; } } - fputc('\n', fp); + put_byte(bs, '\n'); } -bool ssh2_save_userkey( - const Filename *filename, ssh2_userkey *key, char *passphrase) +void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl) +{ + stdio_sink ss; + stdio_sink_init(&ss, fp); + base64_encode_s(BinarySink_UPCAST(&ss), data, datalen, cpl); +} + +const ppk_save_parameters ppk_save_default_parameters = { + .fmt_version = 3, + + /* + * The Argon2 spec recommends the hybrid variant Argon2id, where + * you don't have a good reason to go with the pure Argon2d or + * Argon2i. + */ + .argon2_flavour = Argon2id, + + /* + * Memory requirement for hashing a password: I don't want to set + * this to some truly huge thing like a gigabyte, because for all + * I know people might perfectly reasonably be running PuTTY on + * machines that don't _have_ a gigabyte spare to hash a private + * key passphrase in the legitimate use cases. + * + * I've picked 8 MB as an amount of memory that isn't unreasonable + * to expect a desktop client machine to have, but is also large + * compared to the memory requirements of the PPK v2 password hash + * (which was plain SHA-1), so it still imposes a limit on + * parallel attacks on someone's key file. + */ + .argon2_mem = 8192, /* require 8 Mb memory */ + + /* + * Automatically scale the number of Argon2 passes so that the + * overall time taken is about 1/10 second. (Again, I could crank + * this up to a larger time and _most_ people might be OK with it, + * but for the moment, I'm trying to err on the side of not + * stopping anyone from using the tools at all.) + */ + .argon2_passes_auto = true, + .argon2_milliseconds = 100, + + /* + * PuTTY's own Argon2 implementation is single-threaded. So we + * might as well set parallelism to 1, which requires that + * attackers' implementations must also be effectively + * single-threaded, and they don't get any benefit from using + * multiple cores on the same hash attempt. (Of course they can + * still use multiple cores for _separate_ hash attempts, but at + * least they don't get a speed advantage over us in computing + * even one hash.) + */ + .argon2_parallelism = 1, +}; + +strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase, + const ppk_save_parameters *params_orig) { - FILE *fp; - strbuf *pub_blob, *priv_blob; + strbuf *pub_blob, *priv_blob, *cipher_mac_keys_blob; unsigned char *priv_blob_encrypted; int priv_encrypted_len; int cipherblk; int i; const char *cipherstr; - unsigned char priv_mac[20]; + ptrlen cipherkey, cipheriv, mackey; + const struct ppk_cipher *ciphertype; + unsigned char priv_mac[32]; /* * Fetch the key component blobs. @@ -1275,9 +1486,11 @@ bool ssh2_save_userkey( if (passphrase) { cipherstr = "aes256-cbc"; cipherblk = 16; + ciphertype = &ppk_cipher_aes256_cbc; } else { cipherstr = "none"; cipherblk = 1; + ciphertype = &ppk_cipher_none; } priv_encrypted_len = priv_blob->len + cipherblk - 1; priv_encrypted_len -= priv_encrypted_len % cipherblk; @@ -1291,11 +1504,33 @@ bool ssh2_save_userkey( memcpy(priv_blob_encrypted + priv_blob->len, priv_mac, priv_encrypted_len - priv_blob->len); + /* Copy the save parameters, so that when derive_keys chooses the + * number of Argon2 passes, it can write the result back to our + * copy for us to retrieve. */ + ppk_save_parameters params = *params_orig; + + strbuf *passphrase_salt = strbuf_new(); + + if (params.fmt_version == 3) { + /* Invent a salt for the password hash. */ + if (params.salt) + put_data(passphrase_salt, params.salt, params.saltlen); + else + random_read(strbuf_append(passphrase_salt, 16), 16); + } + + cipher_mac_keys_blob = strbuf_new(); + ssh2_ppk_derive_keys(params.fmt_version, ciphertype, + ptrlen_from_asciz(passphrase ? passphrase : ""), + cipher_mac_keys_blob, &cipherkey, &cipheriv, &mackey, + ptrlen_from_strbuf(passphrase_salt), ¶ms); + + const ssh2_macalg *macalg = (params.fmt_version == 2 ? + &ssh_hmac_sha1 : &ssh_hmac_sha256); + /* Now create the MAC. */ { strbuf *macdata; - unsigned char mackey[20]; - char header[] = "putty-private-key-file-mac-key"; macdata = strbuf_new_nm(); put_stringz(macdata, ssh_key_ssh_id(key->key)); @@ -1304,52 +1539,67 @@ bool ssh2_save_userkey( put_string(macdata, pub_blob->s, pub_blob->len); put_string(macdata, priv_blob_encrypted, priv_encrypted_len); - ssh_hash *h = ssh_hash_new(&ssh_sha1); - put_data(h, header, sizeof(header)-1); - if (passphrase) - put_data(h, passphrase, strlen(passphrase)); - ssh_hash_final(h, mackey); - mac_simple(&ssh_hmac_sha1, make_ptrlen(mackey, 20), - ptrlen_from_strbuf(macdata), priv_mac); + mac_simple(macalg, mackey, ptrlen_from_strbuf(macdata), priv_mac); strbuf_free(macdata); - smemclr(mackey, sizeof(mackey)); } if (passphrase) { - unsigned char key[40]; - - ssh2_ppk_derivekey(ptrlen_from_asciz(passphrase), key); - aes256_encrypt_pubkey(key, priv_blob_encrypted, priv_encrypted_len); - - smemclr(key, sizeof(key)); + assert(cipherkey.len == 32); + aes256_encrypt_pubkey(cipherkey.ptr, cipheriv.ptr, + priv_blob_encrypted, priv_encrypted_len); } - fp = f_open(filename, "w", true); - if (!fp) { - strbuf_free(pub_blob); - strbuf_free(priv_blob); - smemclr(priv_blob_encrypted, priv_encrypted_len); - sfree(priv_blob_encrypted); - return false; + strbuf *out = strbuf_new_nm(); + strbuf_catf(out, "PuTTY-User-Key-File-%u: %s\n", + params.fmt_version, ssh_key_ssh_id(key->key)); + strbuf_catf(out, "Encryption: %s\n", cipherstr); + strbuf_catf(out, "Comment: %s\n", key->comment); + strbuf_catf(out, "Public-Lines: %d\n", base64_lines(pub_blob->len)); + base64_encode_s(BinarySink_UPCAST(out), pub_blob->u, pub_blob->len, 64); + if (params.fmt_version == 3 && ciphertype->keylen != 0) { + strbuf_catf(out, "Key-Derivation: %s\n", + params.argon2_flavour == Argon2d ? "Argon2d" : + params.argon2_flavour == Argon2i ? "Argon2i" : "Argon2id"); + strbuf_catf(out, "Argon2-Memory: %"PRIu32"\n", params.argon2_mem); + assert(!params.argon2_passes_auto); + strbuf_catf(out, "Argon2-Passes: %"PRIu32"\n", params.argon2_passes); + strbuf_catf(out, "Argon2-Parallelism: %"PRIu32"\n", + params.argon2_parallelism); + strbuf_catf(out, "Argon2-Salt: "); + for (size_t i = 0; i < passphrase_salt->len; i++) + strbuf_catf(out, "%02x", passphrase_salt->u[i]); + strbuf_catf(out, "\n"); } - fprintf(fp, "PuTTY-User-Key-File-2: %s\n", ssh_key_ssh_id(key->key)); - fprintf(fp, "Encryption: %s\n", cipherstr); - fprintf(fp, "Comment: %s\n", key->comment); - fprintf(fp, "Public-Lines: %d\n", base64_lines(pub_blob->len)); - base64_encode(fp, pub_blob->u, pub_blob->len, 64); - fprintf(fp, "Private-Lines: %d\n", base64_lines(priv_encrypted_len)); - base64_encode(fp, priv_blob_encrypted, priv_encrypted_len, 64); - fprintf(fp, "Private-MAC: "); - for (i = 0; i < 20; i++) - fprintf(fp, "%02x", priv_mac[i]); - fprintf(fp, "\n"); - fclose(fp); - + strbuf_catf(out, "Private-Lines: %d\n", base64_lines(priv_encrypted_len)); + base64_encode_s(BinarySink_UPCAST(out), + priv_blob_encrypted, priv_encrypted_len, 64); + strbuf_catf(out, "Private-MAC: "); + for (i = 0; i < macalg->len; i++) + strbuf_catf(out, "%02x", priv_mac[i]); + strbuf_catf(out, "\n"); + + strbuf_free(cipher_mac_keys_blob); + strbuf_free(passphrase_salt); strbuf_free(pub_blob); strbuf_free(priv_blob); smemclr(priv_blob_encrypted, priv_encrypted_len); sfree(priv_blob_encrypted); - return true; + return out; +} + +bool ppk_save_f(const Filename *filename, ssh2_userkey *key, + const char *passphrase, const ppk_save_parameters *params) +{ + FILE *fp = f_open(filename, "wb", true); + if (!fp) + return false; + + strbuf *buf = ppk_save_sb(key, passphrase, params); + bool toret = fwrite(buf->s, 1, buf->len, fp) == buf->len; + if (fclose(fp)) + toret = false; + strbuf_free(buf); + return toret; } /* ---------------------------------------------------------------------- @@ -1362,8 +1612,8 @@ char *ssh1_pubkey_str(RSAKey *key) dec1 = mp_get_decimal(key->exponent); dec2 = mp_get_decimal(key->modulus); - buffer = dupprintf("%zd %s %s%s%s", mp_get_nbits(key->modulus), dec1, dec2, - key->comment ? " " : "", + buffer = dupprintf("%"SIZEu" %s %s%s%s", mp_get_nbits(key->modulus), + dec1, dec2, key->comment ? " " : "", key->comment ? key->comment : ""); sfree(dec1); sfree(dec2); @@ -1484,114 +1734,166 @@ void ssh2_write_pubkey(FILE *fp, const char *comment, /* ---------------------------------------------------------------------- * Utility functions to compute SSH-2 fingerprints in a uniform way. */ -char *ssh2_fingerprint_blob(ptrlen blob) +static void ssh2_fingerprint_blob_md5(ptrlen blob, strbuf *sb) { unsigned char digest[16]; - char fingerprint_str[16*3]; - ptrlen algname; - const ssh_keyalg *alg; - int i; - BinarySource src[1]; - /* - * The fingerprint hash itself is always just the MD5 of the blob. - */ hash_simple(&ssh_md5, blob, digest); - for (i = 0; i < 16; i++) - sprintf(fingerprint_str + i*3, "%02x%s", digest[i], i==15 ? "" : ":"); + for (unsigned i = 0; i < 16; i++) + strbuf_catf(sb, "%02x%s", digest[i], i==15 ? "" : ":"); +} + +static void ssh2_fingerprint_blob_sha256(ptrlen blob, strbuf *sb) +{ + unsigned char digest[32]; + hash_simple(&ssh_sha256, blob, digest); + + put_datapl(sb, PTRLEN_LITERAL("SHA256:")); + + for (unsigned i = 0; i < 32; i += 3) { + char buf[5]; + unsigned len = 32-i; + if (len > 3) + len = 3; + base64_encode_atom(digest + i, len, buf); + put_data(sb, buf, 4); + } + strbuf_chomp(sb, '='); +} + +char *ssh2_fingerprint_blob(ptrlen blob, FingerprintType fptype) +{ + strbuf *sb = strbuf_new(); /* * Identify the key algorithm, if possible. + * + * If we can't do that, then we have a seriously confused key + * blob, in which case we return only the hash. */ + BinarySource src[1]; BinarySource_BARE_INIT_PL(src, blob); - algname = get_string(src); + ptrlen algname = get_string(src); if (!get_err(src)) { - alg = find_pubkey_alg_len(algname); + const ssh_keyalg *alg = find_pubkey_alg_len(algname); if (alg) { int bits = ssh_key_public_bits(alg, blob); - return dupprintf("%.*s %d %s", PTRLEN_PRINTF(algname), - bits, fingerprint_str); + strbuf_catf(sb, "%.*s %d ", PTRLEN_PRINTF(algname), bits); } else { - return dupprintf("%.*s %s", PTRLEN_PRINTF(algname), - fingerprint_str); + strbuf_catf(sb, "%.*s ", PTRLEN_PRINTF(algname)); } - } else { - /* - * No algorithm available (which means a seriously confused - * key blob, but there we go). Return only the hash. - */ - return dupstr(fingerprint_str); } + + switch (fptype) { + case SSH_FPTYPE_MD5: + ssh2_fingerprint_blob_md5(blob, sb); + break; + case SSH_FPTYPE_SHA256: + ssh2_fingerprint_blob_sha256(blob, sb); + break; + } + + return strbuf_to_str(sb); +} + +char **ssh2_all_fingerprints_for_blob(ptrlen blob) +{ + char **fps = snewn(SSH_N_FPTYPES, char *); + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + fps[i] = ssh2_fingerprint_blob(blob, i); + return fps; +} + +char *ssh2_fingerprint(ssh_key *data, FingerprintType fptype) +{ + strbuf *blob = strbuf_new(); + ssh_key_public_blob(data, BinarySink_UPCAST(blob)); + char *ret = ssh2_fingerprint_blob(ptrlen_from_strbuf(blob), fptype); + strbuf_free(blob); + return ret; } -char *ssh2_fingerprint(ssh_key *data) +char **ssh2_all_fingerprints(ssh_key *data) { strbuf *blob = strbuf_new(); ssh_key_public_blob(data, BinarySink_UPCAST(blob)); - char *ret = ssh2_fingerprint_blob(ptrlen_from_strbuf(blob)); + char **ret = ssh2_all_fingerprints_for_blob(ptrlen_from_strbuf(blob)); strbuf_free(blob); return ret; } +void ssh2_free_all_fingerprints(char **fps) +{ + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + sfree(fps[i]); + sfree(fps); +} + /* ---------------------------------------------------------------------- * Determine the type of a private key file. */ -static int key_type_fp(FILE *fp) +static int key_type_s_internal(BinarySource *src) { - char buf[1024]; - const char public_std_sig[] = "---- BEGIN SSH2 PUBLIC KEY"; - const char putty2_sig[] = "PuTTY-User-Key-File-"; - const char sshcom_sig[] = "---- BEGIN SSH2 ENCRYPTED PRIVAT"; - const char openssh_new_sig[] = "-----BEGIN OPENSSH PRIVATE KEY"; - const char openssh_sig[] = "-----BEGIN "; - int i; - char *p; - - i = fread(buf, 1, sizeof(buf)-1, fp); - rewind(fp); - - if (i < 0) - return SSH_KEYTYPE_UNOPENABLE; - if (i < 32) - return SSH_KEYTYPE_UNKNOWN; - assert(i > 0 && i < sizeof(buf)); - buf[i] = '\0'; - if (!memcmp(buf, rsa_signature, sizeof(rsa_signature)-1)) + static const ptrlen public_std_sig = + PTRLEN_DECL_LITERAL("---- BEGIN SSH2 PUBLIC KEY"); + static const ptrlen putty2_sig = + PTRLEN_DECL_LITERAL("PuTTY-User-Key-File-"); + static const ptrlen sshcom_sig = + PTRLEN_DECL_LITERAL("---- BEGIN SSH2 ENCRYPTED PRIVAT"); + static const ptrlen openssh_new_sig = + PTRLEN_DECL_LITERAL("-----BEGIN OPENSSH PRIVATE KEY"); + static const ptrlen openssh_sig = + PTRLEN_DECL_LITERAL("-----BEGIN "); + + if (BinarySource_REWIND(src), expect_signature(src, rsa1_signature)) return SSH_KEYTYPE_SSH1; - if (!memcmp(buf, public_std_sig, sizeof(public_std_sig)-1)) + if (BinarySource_REWIND(src), expect_signature(src, public_std_sig)) return SSH_KEYTYPE_SSH2_PUBLIC_RFC4716; - if (!memcmp(buf, putty2_sig, sizeof(putty2_sig)-1)) + if (BinarySource_REWIND(src), expect_signature(src, putty2_sig)) return SSH_KEYTYPE_SSH2; - if (!memcmp(buf, openssh_new_sig, sizeof(openssh_new_sig)-1)) + if (BinarySource_REWIND(src), expect_signature(src, openssh_new_sig)) return SSH_KEYTYPE_OPENSSH_NEW; - if (!memcmp(buf, openssh_sig, sizeof(openssh_sig)-1)) + if (BinarySource_REWIND(src), expect_signature(src, openssh_sig)) return SSH_KEYTYPE_OPENSSH_PEM; - if (!memcmp(buf, sshcom_sig, sizeof(sshcom_sig)-1)) + if (BinarySource_REWIND(src), expect_signature(src, sshcom_sig)) return SSH_KEYTYPE_SSHCOM; - if ((p = buf + strspn(buf, "0123456789"), *p == ' ') && - (p = p+1 + strspn(p+1, "0123456789"), *p == ' ') && - (p = p+1 + strspn(p+1, "0123456789"), *p == ' ' || *p == '\n' || !*p)) + + BinarySource_REWIND(src); + if (get_chars(src, "0123456789").len > 0 && get_chars(src, " ").len == 1 && + get_chars(src, "0123456789").len > 0 && get_chars(src, " ").len == 1 && + get_chars(src, "0123456789").len > 0 && + get_nonchars(src, " \n").len == 0) return SSH_KEYTYPE_SSH1_PUBLIC; - if ((p = buf + strcspn(buf, " "), - find_pubkey_alg_len(make_ptrlen(buf, p-buf))) && - (p = p+1 + strspn(p+1, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij" - "klmnopqrstuvwxyz+/="), - *p == ' ' || *p == '\n' || !*p)) + + BinarySource_REWIND(src); + if (find_pubkey_alg_len(get_nonchars(src, " \n")) > 0 && + get_chars(src, " ").len == 1 && + get_chars(src, "0123456789ABCDEFGHIJKLMNOPQRSTUV" + "WXYZabcdefghijklmnopqrstuvwxyz+/=").len > 0 && + get_nonchars(src, " \n").len == 0) return SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH; + return SSH_KEYTYPE_UNKNOWN; /* unrecognised or EOF */ } -int key_type(const Filename *filename) +int key_type_s(BinarySource *src) { - FILE *fp; - int ret; + int toret = key_type_s_internal(src); + BinarySource_REWIND(src); + return toret; +} - fp = f_open(filename, "r", false); - if (!fp) +int key_type(const Filename *filename) +{ + LoadedFile *lf = lf_new(1024); + if (lf_load(lf, filename) == LF_ERROR) { + lf_free(lf); return SSH_KEYTYPE_UNOPENABLE; - ret = key_type_fp(fp); - fclose(fp); - return ret; + } + + int toret = key_type_s(BinarySource_UPCAST(lf)); + lf_free(lf); + return toret; } /* @@ -1601,23 +1903,80 @@ int key_type(const Filename *filename) const char *key_type_to_str(int type) { switch (type) { - case SSH_KEYTYPE_UNOPENABLE: return "unable to open file"; break; - case SSH_KEYTYPE_UNKNOWN: return "not a recognised key file format"; break; - case SSH_KEYTYPE_SSH1_PUBLIC: return "SSH-1 public key"; break; - case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: return "SSH-2 public key (RFC 4716 format)"; break; - case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: return "SSH-2 public key (OpenSSH format)"; break; - case SSH_KEYTYPE_SSH1: return "SSH-1 private key"; break; - case SSH_KEYTYPE_SSH2: return "PuTTY SSH-2 private key"; break; - case SSH_KEYTYPE_OPENSSH_PEM: return "OpenSSH SSH-2 private key (old PEM format)"; break; - case SSH_KEYTYPE_OPENSSH_NEW: return "OpenSSH SSH-2 private key (new format)"; break; - case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH-2 private key"; break; + case SSH_KEYTYPE_UNOPENABLE: + return "unable to open file"; + case SSH_KEYTYPE_UNKNOWN: + return "not a recognised key file format"; + case SSH_KEYTYPE_SSH1_PUBLIC: + return "SSH-1 public key"; + case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: + return "SSH-2 public key (RFC 4716 format)"; + case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: + return "SSH-2 public key (OpenSSH format)"; + case SSH_KEYTYPE_SSH1: + return "SSH-1 private key"; + case SSH_KEYTYPE_SSH2: + return "PuTTY SSH-2 private key"; + case SSH_KEYTYPE_OPENSSH_PEM: + return "OpenSSH SSH-2 private key (old PEM format)"; + case SSH_KEYTYPE_OPENSSH_NEW: + return "OpenSSH SSH-2 private key (new format)"; + case SSH_KEYTYPE_SSHCOM: + return "ssh.com SSH-2 private key"; + /* * This function is called with a key type derived from * looking at an actual key file, so the output-only type * OPENSSH_AUTO should never get here, and is much an INTERNAL * ERROR as a code we don't even understand. */ - case SSH_KEYTYPE_OPENSSH_AUTO: return "INTERNAL ERROR (OPENSSH_AUTO)"; break; - default: return "INTERNAL ERROR"; break; + case SSH_KEYTYPE_OPENSSH_AUTO: + unreachable("OPENSSH_AUTO should never reach key_type_to_str"); + default: + unreachable("bad key type in key_type_to_str"); + } +} + +key_components *key_components_new(void) +{ + key_components *kc = snew(key_components); + kc->ncomponents = 0; + kc->componentsize = 0; + kc->components = NULL; + return kc; +} + +void key_components_add_text(key_components *kc, + const char *name, const char *value) +{ + sgrowarray(kc->components, kc->componentsize, kc->ncomponents); + size_t n = kc->ncomponents++; + kc->components[n].name = dupstr(name); + kc->components[n].is_mp_int = false; + kc->components[n].text = dupstr(value); +} + +void key_components_add_mp(key_components *kc, + const char *name, mp_int *value) +{ + sgrowarray(kc->components, kc->componentsize, kc->ncomponents); + size_t n = kc->ncomponents++; + kc->components[n].name = dupstr(name); + kc->components[n].is_mp_int = true; + kc->components[n].mp = mp_copy(value); +} + +void key_components_free(key_components *kc) +{ + for (size_t i = 0; i < kc->ncomponents; i++) { + sfree(kc->components[i].name); + if (kc->components[i].is_mp_int) { + mp_free(kc->components[i].mp); + } else { + smemclr(kc->components[i].text, strlen(kc->components[i].text)); + sfree(kc->components[i].text); + } } + sfree(kc->components); + sfree(kc); } diff --git a/sshrand.c b/sshrand.c index 387a068..7f3d641 100644 --- a/sshrand.c +++ b/sshrand.c @@ -19,7 +19,7 @@ int random_active = 0; */ void random_add_noise(NoiseSourceId source, const void *noise, int length) { } void random_ref(void) { } -void random_setup_special(void) { } +void random_setup_custom(const ssh_hashalg *hash) { } void random_unref(void) { } void random_read(void *out, size_t size) { @@ -97,10 +97,10 @@ void random_ref(void) random_create(&ssh_sha256); } -void random_setup_special() +void random_setup_custom(const ssh_hashalg *hash) { random_active++; - random_create(&ssh_sha512); + random_create(hash); } void random_reseed(ptrlen seed) diff --git a/sshrsa.c b/sshrsa.c index e30ca47..e3dcbdd 100644 --- a/sshrsa.c +++ b/sshrsa.c @@ -43,6 +43,39 @@ void BinarySource_get_rsa_ssh1_priv( rsa->private_exponent = get_mp_ssh1(src); } +key_components *rsa_components(RSAKey *rsa) +{ + key_components *kc = key_components_new(); + key_components_add_text(kc, "key_type", "RSA"); + key_components_add_mp(kc, "public_modulus", rsa->modulus); + key_components_add_mp(kc, "public_exponent", rsa->exponent); + if (rsa->private_exponent) { + key_components_add_mp(kc, "private_exponent", rsa->private_exponent); + key_components_add_mp(kc, "private_p", rsa->p); + key_components_add_mp(kc, "private_q", rsa->q); + key_components_add_mp(kc, "private_inverse_q_mod_p", rsa->iqmp); + } + return kc; +} + +RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src) +{ + RSAKey *rsa = snew(RSAKey); + memset(rsa, 0, sizeof(RSAKey)); + + get_rsa_ssh1_pub(src, rsa, RSA_SSH1_MODULUS_FIRST); + get_rsa_ssh1_priv(src, rsa); + + /* SSH-1 names p and q the other way round, i.e. we have the + * inverse of p mod q and not of q mod p. We swap the names, + * because our internal RSA wants iqmp. */ + rsa->iqmp = get_mp_ssh1(src); + rsa->q = get_mp_ssh1(src); + rsa->p = get_mp_ssh1(src); + + return rsa; +} + bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key) { mp_int *b1, *b2; @@ -110,8 +143,8 @@ bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key) * Uses Chinese Remainder Theorem to speed computation up over the * obvious implementation of a single big modpow. */ -mp_int *crt_modpow(mp_int *base, mp_int *exp, mp_int *mod, - mp_int *p, mp_int *q, mp_int *iqmp) +static mp_int *crt_modpow(mp_int *base, mp_int *exp, mp_int *mod, + mp_int *p, mp_int *q, mp_int *iqmp) { mp_int *pm1, *qm1, *pexp, *qexp, *presult, *qresult; mp_int *diff, *multiplier, *ret0, *ret; @@ -278,7 +311,7 @@ char *rsa_ssh1_fingerprint(RSAKey *key) ssh_hash_final(hash, digest); out = strbuf_new(); - strbuf_catf(out, "%d ", mp_get_nbits(key->modulus)); + strbuf_catf(out, "%"SIZEu" ", mp_get_nbits(key->modulus)); for (i = 0; i < 16; i++) strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]); if (key->comment) @@ -286,6 +319,19 @@ char *rsa_ssh1_fingerprint(RSAKey *key) return strbuf_to_str(out); } +/* + * Wrap the output of rsa_ssh1_fingerprint up into the same kind of + * structure that comes from ssh2_all_fingerprints. + */ +char **rsa_ssh1_fake_all_fingerprints(RSAKey *key) +{ + char **ret = snewn(SSH_N_FPTYPES, char *); + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + ret[i] = NULL; + ret[SSH_FPTYPE_MD5] = rsa_ssh1_fingerprint(key); + return ret; +} + /* * Verify that the public data in an RSA key matches the private * data. We also check the private data itself: we ensure that p > @@ -356,6 +402,15 @@ void rsa_ssh1_public_blob(BinarySink *bs, RSAKey *key, } } +void rsa_ssh1_private_blob_agent(BinarySink *bs, RSAKey *key) +{ + rsa_ssh1_public_blob(bs, key, RSA_SSH1_MODULUS_FIRST); + put_mp_ssh1(bs, key->private_exponent); + put_mp_ssh1(bs, key->iqmp); + put_mp_ssh1(bs, key->q); + put_mp_ssh1(bs, key->p); +} + /* Given an SSH-1 public key blob, determine its length. */ int rsa_ssh1_public_blob_len(ptrlen data) { @@ -414,9 +469,13 @@ void freersakey(RSAKey *key) } /* ---------------------------------------------------------------------- - * Implementation of the ssh-rsa signing key type. + * Implementation of the ssh-rsa signing key type family. */ +struct ssh2_rsa_extra { + unsigned signflags; +}; + static void rsa2_freekey(ssh_key *key); /* forward reference */ static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data) @@ -429,7 +488,7 @@ static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data) return NULL; rsa = snew(RSAKey); - rsa->sshk.vt = &ssh_rsa; + rsa->sshk.vt = self; rsa->exponent = get_mp_ssh2(src); rsa->modulus = get_mp_ssh2(src); rsa->private_exponent = NULL; @@ -457,6 +516,12 @@ static char *rsa2_cache_str(ssh_key *key) return rsastr_fmt(rsa); } +static key_components *rsa2_components(ssh_key *key) +{ + RSAKey *rsa = container_of(key, RSAKey, sshk); + return rsa_components(rsa); +} + static void rsa2_public_blob(ssh_key *key, BinarySink *bs) { RSAKey *rsa = container_of(key, RSAKey, sshk); @@ -681,8 +746,10 @@ static bool rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data) ptrlen type, in_pl; mp_int *in, *out; - /* If we need to support variable flags on verify, this is where they go */ - const ssh_hashalg *halg = rsa2_hash_alg_for_flags(0, NULL); + const struct ssh2_rsa_extra *extra = + (const struct ssh2_rsa_extra *)key->vt->extra; + + const ssh_hashalg *halg = rsa2_hash_alg_for_flags(extra->signflags, NULL); /* Start by making sure the key is even long enough to encode a * signature. If not, everything fails to verify. */ @@ -703,7 +770,7 @@ static bool rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data) * mp_from_bytes_be, which will tolerate anything. */ in_pl = get_string(src); - if (get_err(src) || !ptrlen_eq_string(type, "ssh-rsa")) + if (get_err(src) || !ptrlen_eq_string(type, key->vt->ssh_id)) return false; in = mp_from_bytes_be(in_pl); @@ -732,6 +799,10 @@ static void rsa2_sign(ssh_key *key, ptrlen data, const ssh_hashalg *halg; const char *sign_alg_name; + const struct ssh2_rsa_extra *extra = + (const struct ssh2_rsa_extra *)key->vt->extra; + flags |= extra->signflags; + halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name); nbytes = (mp_get_nbits(rsa->modulus) + 7) / 8; @@ -753,7 +824,7 @@ static void rsa2_sign(ssh_key *key, ptrlen data, mp_free(out); } -char *rsa2_invalid(ssh_key *key, unsigned flags) +static char *rsa2_invalid(ssh_key *key, unsigned flags) { RSAKey *rsa = container_of(key, RSAKey, sshk); size_t bits = mp_get_nbits(rsa->modulus), nbytes = (bits + 7) / 8; @@ -761,33 +832,53 @@ char *rsa2_invalid(ssh_key *key, unsigned flags) const ssh_hashalg *halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name); if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) { return dupprintf( - "%zu-bit RSA key is too short to generate %s signatures", + "%"SIZEu"-bit RSA key is too short to generate %s signatures", bits, sign_alg_name); } return NULL; } +static const struct ssh2_rsa_extra + rsa_extra = { 0 }, + rsa_sha256_extra = { SSH_AGENT_RSA_SHA2_256 }, + rsa_sha512_extra = { SSH_AGENT_RSA_SHA2_512 }; + +#define COMMON_KEYALG_FIELDS \ + .new_pub = rsa2_new_pub, \ + .new_priv = rsa2_new_priv, \ + .new_priv_openssh = rsa2_new_priv_openssh, \ + .freekey = rsa2_freekey, \ + .invalid = rsa2_invalid, \ + .sign = rsa2_sign, \ + .verify = rsa2_verify, \ + .public_blob = rsa2_public_blob, \ + .private_blob = rsa2_private_blob, \ + .openssh_blob = rsa2_openssh_blob, \ + .cache_str = rsa2_cache_str, \ + .components = rsa2_components, \ + .pubkey_bits = rsa2_pubkey_bits, \ + .cache_id = "rsa2" + const ssh_keyalg ssh_rsa = { - rsa2_new_pub, - rsa2_new_priv, - rsa2_new_priv_openssh, - - rsa2_freekey, - rsa2_invalid, - rsa2_sign, - rsa2_verify, - rsa2_public_blob, - rsa2_private_blob, - rsa2_openssh_blob, - rsa2_cache_str, - - rsa2_pubkey_bits, - - "ssh-rsa", - "rsa2", - NULL, - SSH_AGENT_RSA_SHA2_256 | SSH_AGENT_RSA_SHA2_512, + COMMON_KEYALG_FIELDS, + .ssh_id = "ssh-rsa", + .supported_flags = SSH_AGENT_RSA_SHA2_256 | SSH_AGENT_RSA_SHA2_512, + .extra = &rsa_extra, +}; + +const ssh_keyalg ssh_rsa_sha256 = { + COMMON_KEYALG_FIELDS, + .ssh_id = "rsa-sha2-256", + .supported_flags = 0, + .extra = &rsa_sha256_extra, +}; + +const ssh_keyalg ssh_rsa_sha512 = { + COMMON_KEYALG_FIELDS, + .ssh_id = "rsa-sha2-512", + .supported_flags = 0, + .extra = &rsa_sha512_extra, }; RSAKey *ssh_rsakex_newkey(ptrlen data) @@ -814,16 +905,17 @@ static void oaep_mask(const ssh_hashalg *h, void *seed, int seedlen, unsigned char *data = (unsigned char *)vdata; unsigned count = 0; + ssh_hash *s = ssh_hash_new(h); + while (datalen > 0) { int i, max = (datalen > h->hlen ? h->hlen : datalen); - ssh_hash *s; unsigned char hash[MAX_HASH_LEN]; + ssh_hash_reset(s); assert(h->hlen <= MAX_HASH_LEN); - s = ssh_hash_new(h); put_data(s, seed, seedlen); put_uint32(s, count); - ssh_hash_final(s, hash); + ssh_hash_digest(s, hash); count++; for (i = 0; i < max; i++) @@ -832,6 +924,8 @@ static void oaep_mask(const ssh_hashalg *h, void *seed, int seedlen, data += max; datalen -= max; } + + ssh_hash_free(s); } strbuf *ssh_rsakex_encrypt(RSAKey *rsa, const ssh_hashalg *h, ptrlen in) @@ -889,10 +983,7 @@ strbuf *ssh_rsakex_encrypt(RSAKey *rsa, const ssh_hashalg *h, ptrlen in) random_read(out + 1, HLEN); /* At position 1+HLEN, the data block DB, consisting of: */ /* The hash of the label (we only support an empty label here) */ - { - ssh_hash *s = ssh_hash_new(h); - ssh_hash_final(s, out + HLEN + 1); - } + hash_simple(h, PTRLEN_LITERAL(""), out + HLEN + 1); /* A bunch of zero octets */ memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1)); /* A single 1 octet, followed by the input message data. */ @@ -935,7 +1026,6 @@ mp_int *ssh_rsakex_decrypt( int outlen, i; unsigned char *out; unsigned char labelhash[64]; - ssh_hash *hash; BinarySource src[1]; const int HLEN = h->hlen; @@ -969,8 +1059,7 @@ mp_int *ssh_rsakex_decrypt( } /* Check the label hash at position 1+HLEN */ assert(HLEN <= lenof(labelhash)); - hash = ssh_hash_new(h); - ssh_hash_final(hash, labelhash); + hash_simple(h, PTRLEN_LITERAL(""), labelhash); if (memcmp(out + HLEN + 1, labelhash, HLEN)) { sfree(out); return NULL; @@ -980,7 +1069,7 @@ mp_int *ssh_rsakex_decrypt( if (out[i] == 1) { i++; /* skip over the 1 byte */ break; - } else if (out[i] != 1) { + } else if (out[i] != 0) { sfree(out); return NULL; } diff --git a/sshrsag.c b/sshrsag.c index fe96c99..b9676e7 100644 --- a/sshrsag.c +++ b/sshrsag.c @@ -5,52 +5,102 @@ #include #include "ssh.h" +#include "sshkeygen.h" #include "mpint.h" -#define RSA_EXPONENT 37 /* we like this prime */ +#define RSA_EXPONENT 65537 -int rsa_generate(RSAKey *key, int bits, progfn_t pfn, - void *pfnparam) +#define NFIRSTBITS 13 +static void invent_firstbits(unsigned *one, unsigned *two, + unsigned min_separation); + +typedef struct RSAPrimeDetails RSAPrimeDetails; +struct RSAPrimeDetails { + bool strong; + int bits, bitsm1m1, bitsm1, bitsp1; + unsigned firstbits; + ProgressPhase phase_main, phase_m1m1, phase_m1, phase_p1; +}; + +#define STRONG_MARGIN (20 + NFIRSTBITS) + +static RSAPrimeDetails setup_rsa_prime( + int bits, bool strong, PrimeGenerationContext *pgc, ProgressReceiver *prog) { - unsigned pfirst, qfirst; + RSAPrimeDetails pd; + pd.bits = bits; + if (strong) { + pd.bitsm1 = (bits - STRONG_MARGIN) / 2; + pd.bitsp1 = (bits - STRONG_MARGIN) - pd.bitsm1; + pd.bitsm1m1 = (pd.bitsm1 - STRONG_MARGIN) / 2; + if (pd.bitsm1m1 < STRONG_MARGIN) { + /* Absurdly small prime, but we should at least not crash. */ + strong = false; + } + } + pd.strong = strong; - key->sshk.vt = &ssh_rsa; + if (pd.strong) { + pd.phase_m1m1 = primegen_add_progress_phase(pgc, prog, pd.bitsm1m1); + pd.phase_m1 = primegen_add_progress_phase(pgc, prog, pd.bitsm1); + pd.phase_p1 = primegen_add_progress_phase(pgc, prog, pd.bitsp1); + } + pd.phase_main = primegen_add_progress_phase(pgc, prog, pd.bits); - /* - * Set up the phase limits for the progress report. We do this - * by passing minus the phase number. - * - * For prime generation: our initial filter finds things - * coprime to everything below 2^16. Computing the product of - * (p-1)/p for all prime p below 2^16 gives about 20.33; so - * among B-bit integers, one in every 20.33 will get through - * the initial filter to be a candidate prime. - * - * Meanwhile, we are searching for primes in the region of 2^B; - * since pi(x) ~ x/log(x), when x is in the region of 2^B, the - * prime density will be d/dx pi(x) ~ 1/log(B), i.e. about - * 1/0.6931B. So the chance of any given candidate being prime - * is 20.33/0.6931B, which is roughly 29.34 divided by B. - * - * So now we have this probability P, we're looking at an - * exponential distribution with parameter P: we will manage in - * one attempt with probability P, in two with probability - * P(1-P), in three with probability P(1-P)^2, etc. The - * probability that we have still not managed to find a prime - * after N attempts is (1-P)^N. - * - * We therefore inform the progress indicator of the number B - * (29.34/B), so that it knows how much to increment by each - * time. We do this in 16-bit fixed point, so 29.34 becomes - * 0x1D.57C4. - */ - pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x10000); - pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / (bits / 2)); - pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x10000); - pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / (bits - bits / 2)); - pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x4000); - pfn(pfnparam, PROGFN_LIN_PHASE, 3, 5); - pfn(pfnparam, PROGFN_READY, 0, 0); + return pd; +} + +static mp_int *generate_rsa_prime( + RSAPrimeDetails pd, PrimeGenerationContext *pgc, ProgressReceiver *prog) +{ + mp_int *m1m1 = NULL, *m1 = NULL, *p1 = NULL, *p = NULL; + PrimeCandidateSource *pcs; + + if (pd.strong) { + progress_start_phase(prog, pd.phase_m1m1); + pcs = pcs_new_with_firstbits(pd.bitsm1m1, pd.firstbits, NFIRSTBITS); + m1m1 = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + + progress_start_phase(prog, pd.phase_m1); + pcs = pcs_new_with_firstbits(pd.bitsm1, pd.firstbits, NFIRSTBITS); + pcs_require_residue_1_mod_prime(pcs, m1m1); + m1 = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + + progress_start_phase(prog, pd.phase_p1); + pcs = pcs_new_with_firstbits(pd.bitsp1, pd.firstbits, NFIRSTBITS); + p1 = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + } + + progress_start_phase(prog, pd.phase_main); + pcs = pcs_new_with_firstbits(pd.bits, pd.firstbits, NFIRSTBITS); + pcs_avoid_residue_small(pcs, RSA_EXPONENT, 1); + if (pd.strong) { + pcs_require_residue_1_mod_prime(pcs, m1); + mp_int *p1_minus_1 = mp_copy(p1); + mp_sub_integer_into(p1_minus_1, p1, 1); + pcs_require_residue(pcs, p1, p1_minus_1); + mp_free(p1_minus_1); + } + p = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + + if (m1m1) + mp_free(m1m1); + if (m1) + mp_free(m1); + if (p1) + mp_free(p1); + + return p; +} + +int rsa_generate(RSAKey *key, int bits, bool strong, + PrimeGenerationContext *pgc, ProgressReceiver *prog) +{ + key->sshk.vt = &ssh_rsa; /* * We don't generate e; we just use a standard one always. @@ -70,14 +120,18 @@ int rsa_generate(RSAKey *key, int bits, progfn_t pfn, * more so than an attacker guessing a whole 256-bit session key - * but it doesn't cost much to make sure.) */ - invent_firstbits(&pfirst, &qfirst, 2); int qbits = bits / 2; int pbits = bits - qbits; assert(pbits >= qbits); - mp_int *p = primegen(pbits, RSA_EXPONENT, 1, NULL, - 1, pfn, pfnparam, pfirst); - mp_int *q = primegen(qbits, RSA_EXPONENT, 1, NULL, - 2, pfn, pfnparam, qfirst); + + RSAPrimeDetails pd = setup_rsa_prime(pbits, strong, pgc, prog); + RSAPrimeDetails qd = setup_rsa_prime(qbits, strong, pgc, prog); + progress_ready(prog); + + invent_firstbits(&pd.firstbits, &qd.firstbits, 2); + + mp_int *p = generate_rsa_prime(pd, pgc, prog); + mp_int *q = generate_rsa_prime(qd, pgc, prog); /* * Ensure p > q, by swapping them if not. @@ -97,22 +151,17 @@ int rsa_generate(RSAKey *key, int bits, progfn_t pfn, * the other helpful quantities: n=pq, d=e^-1 mod (p-1)(q-1), * and (q^-1 mod p). */ - pfn(pfnparam, PROGFN_PROGRESS, 3, 1); mp_int *modulus = mp_mul(p, q); - pfn(pfnparam, PROGFN_PROGRESS, 3, 2); mp_int *pm1 = mp_copy(p); mp_sub_integer_into(pm1, pm1, 1); mp_int *qm1 = mp_copy(q); mp_sub_integer_into(qm1, qm1, 1); mp_int *phi_n = mp_mul(pm1, qm1); - pfn(pfnparam, PROGFN_PROGRESS, 3, 3); mp_free(pm1); mp_free(qm1); mp_int *private_exponent = mp_invert(exponent, phi_n); - pfn(pfnparam, PROGFN_PROGRESS, 3, 4); mp_free(phi_n); mp_int *iqmp = mp_invert(q, p); - pfn(pfnparam, PROGFN_PROGRESS, 3, 5); /* * Populate the returned structure. @@ -124,5 +173,120 @@ int rsa_generate(RSAKey *key, int bits, progfn_t pfn, key->q = q; key->iqmp = iqmp; + key->bits = mp_get_nbits(modulus); + key->bytes = (key->bits + 7) / 8; + return 1; } + +/* + * Invent a pair of values suitable for use as the 'firstbits' values + * for the two RSA primes, such that their product is at least 2, and + * such that their difference is also at least min_separation. + * + * This is used for generating RSA keys which have exactly the + * specified number of bits rather than one fewer - if you generate an + * a-bit and a b-bit number completely at random and multiply them + * together, you could end up with either an (ab-1)-bit number or an + * (ab)-bit number. The former happens log(2)*2-1 of the time (about + * 39%) and, though actually harmless, every time it occurs it has a + * non-zero probability of sparking a user email along the lines of + * 'Hey, I asked PuTTYgen for a 2048-bit key and I only got 2047 bits! + * Bug!' + */ +static inline unsigned firstbits_b_min( + unsigned a, unsigned lo, unsigned hi, unsigned min_separation) +{ + /* To get a large enough product, b must be at least this much */ + unsigned b_min = (2*lo*lo + a - 1) / a; + /* Now enforce a hi) + b_min = hi; + return b_min; +} + +static void invent_firstbits(unsigned *one, unsigned *two, + unsigned min_separation) +{ + /* + * We'll pick 12 initial bits (number selected at random) for each + * prime, not counting the leading 1. So we want to return two + * values in the range [2^12,2^13) whose product is at least 2^25. + * + * Strategy: count up all the viable pairs, then select a random + * number in that range and use it to pick a pair. + * + * To keep things simple, we'll ensure a < b, and randomly swap + * them at the end. + */ + const unsigned lo = 1<<12, hi = 1<<13, minproduct = 2*lo*lo; + unsigned a, b; + + /* + * Count up the number of prefixes of b that would be valid for + * each prefix of a. + */ + mp_int *total = mp_new(32); + for (a = lo; a < hi; a++) { + unsigned b_min = firstbits_b_min(a, lo, hi, min_separation); + mp_add_integer_into(total, total, hi - b_min); + } + + /* + * Make up a random number in the range [0,2*total). + */ + mp_int *mlo = mp_from_integer(0), *mhi = mp_new(32); + mp_lshift_fixed_into(mhi, total, 1); + mp_int *randval = mp_random_in_range(mlo, mhi); + mp_free(mlo); + mp_free(mhi); + + /* + * Use the low bit of randval as our swap indicator, leaving the + * rest of it in the range [0,total). + */ + unsigned swap = mp_get_bit(randval, 0); + mp_rshift_fixed_into(randval, randval, 1); + + /* + * Now do the same counting loop again to make the actual choice. + */ + a = b = 0; + for (unsigned a_candidate = lo; a_candidate < hi; a_candidate++) { + unsigned b_min = firstbits_b_min(a_candidate, lo, hi, min_separation); + unsigned limit = hi - b_min; + + unsigned b_candidate = b_min + mp_get_integer(randval); + unsigned use_it = 1 ^ mp_hs_integer(randval, limit); + a ^= (a ^ a_candidate) & -use_it; + b ^= (b ^ b_candidate) & -use_it; + + mp_sub_integer_into(randval, randval, limit); + } + + mp_free(randval); + mp_free(total); + + /* + * Check everything came out right. + */ + assert(lo <= a); + assert(a < hi); + assert(lo <= b); + assert(b < hi); + assert(a * b >= minproduct); + assert(b >= a + min_separation); + + /* + * Last-minute optional swap of a and b. + */ + unsigned diff = (a ^ b) & (-swap); + a ^= diff; + b ^= diff; + + *one = a; + *two = b; +} diff --git a/sshserver.c b/sshserver.c index 7889b7d..cc1c880 100644 --- a/sshserver.c +++ b/sshserver.c @@ -9,6 +9,7 @@ #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" +#include "sshchan.h" #include "sshserver.h" #ifndef NO_GSSAPI #include "sshgssc.h" @@ -45,6 +46,8 @@ struct server { LogPolicy *logpolicy; const SftpServerVtable *sftpserver_vt; + agentfwd *stunt_agentfwd; + Seat seat; Ssh ssh; struct ssh_version_receiver version_receiver; @@ -85,7 +88,7 @@ void ssh_check_frozen(Ssh *ssh) {} mainchan *mainchan_new( PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf, - int term_width, int term_height, int is_simple, SshChannel **sc_out) + int term_width, int term_height, bool is_simple, SshChannel **sc_out) { return NULL; } void mainchan_get_specials( mainchan *mc, add_special_fn_t add_special, void *ctx) {} @@ -102,28 +105,31 @@ static int server_confirm_weak_cached_hostkey( void (*callback)(void *ctx, int result), void *ctx) { return 1; } static const SeatVtable server_seat_vt = { - nullseat_output, - nullseat_eof, - nullseat_get_userpass_input, - nullseat_notify_remote_exit, - nullseat_connection_fatal, - nullseat_update_specials_menu, - nullseat_get_ttymode, - nullseat_set_busy_status, - nullseat_verify_ssh_host_key, - server_confirm_weak_crypto_primitive, - server_confirm_weak_cached_hostkey, - nullseat_is_never_utf8, - nullseat_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - nullseat_get_window_pixel_size, - nullseat_stripctrl_new, - nullseat_set_trust_status, + .output = nullseat_output, + .eof = nullseat_eof, + .get_userpass_input = nullseat_get_userpass_input, + .notify_remote_exit = nullseat_notify_remote_exit, + .connection_fatal = nullseat_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = nullseat_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .verify_ssh_host_key = nullseat_verify_ssh_host_key, + .confirm_weak_crypto_primitive = server_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = server_confirm_weak_cached_hostkey, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = nullseat_get_window_pixel_size, + .stripctrl_new = nullseat_stripctrl_new, + .set_trust_status = nullseat_set_trust_status, + .verbose = nullseat_verbose_no, + .interactive = nullseat_interactive_no, + .get_cursor_position = nullseat_get_cursor_position, }; -static void server_socket_log(Plug *plug, int type, SockAddr *addr, int port, - const char *error_msg, int error_code) +static void server_socket_log(Plug *plug, PlugLogType type, SockAddr *addr, + int port, const char *error_msg, int error_code) { /* server *srv = container_of(plug, server, plug); */ /* FIXME */ @@ -231,11 +237,10 @@ Conf *make_ssh_server_conf(void) } static const PlugVtable ssh_server_plugvt = { - server_socket_log, - server_closing, - server_receive, - server_sent, - NULL + .log = server_socket_log, + .closing = server_closing, + .receive = server_receive, + .sent = server_sent, }; Plug *ssh_server_plug( @@ -280,7 +285,9 @@ void ssh_server_start(Plug *plug, Socket *socket) server *srv = container_of(plug, server, plug); const char *our_protoversion; - if (srv->hostkey1 && srv->nhostkeys) { + if (srv->ssc->bare_connection) { + our_protoversion = "2.0"; /* SSH-2 only */ + } else if (srv->hostkey1 && srv->nhostkeys) { our_protoversion = "1.99"; /* offer both SSH-1 and SSH-2 */ } else if (srv->hostkey1) { our_protoversion = "1.5"; /* SSH-1 only */ @@ -295,9 +302,9 @@ void ssh_server_start(Plug *plug, Socket *socket) srv->ic_out_raw.ctx = srv; srv->version_receiver.got_ssh_version = server_got_ssh_version; srv->bpp = ssh_verstring_new( - srv->conf, srv->logctx, false /* bare_connection */, + srv->conf, srv->logctx, srv->ssc->bare_connection, our_protoversion, &srv->version_receiver, - true, "Uppity"); + true, srv->ssc->application_name); server_connect_bpp(srv); queue_idempotent_callback(&srv->bpp->ic_in_raw); } @@ -315,6 +322,9 @@ static void ssh_server_free_callback(void *vsrv) if (srv->socket) sk_close(srv->socket); + if (srv->stunt_agentfwd) + agentfwd_free(srv->stunt_agentfwd); + if (srv->base_layer) ssh_ppl_free(srv->base_layer); if (srv->bpp) @@ -490,7 +500,19 @@ static void server_got_ssh_version(struct ssh_version_receiver *rcv, old_bpp = srv->bpp; srv->remote_bugs = ssh_verstring_get_bugs(old_bpp); - if (major_version == 2) { + if (srv->ssc->bare_connection) { + srv->bpp = ssh2_bare_bpp_new(srv->logctx); + server_connect_bpp(srv); + + connection_layer = ssh2_connection_new( + &srv->ssh, NULL, false, srv->conf, + ssh_verstring_get_local(old_bpp), &srv->cl); + ssh2connection_server_configure(connection_layer, + srv->sftpserver_vt, srv->ssc); + server_connect_ppl(srv, connection_layer); + + srv->base_layer = connection_layer; + } else if (major_version == 2) { PacketProtocolLayer *userauth_layer, *transport_child_layer; srv->bpp = ssh2_bpp_new(srv->logctx, &srv->stats, true); @@ -552,6 +574,16 @@ static void server_got_ssh_version(struct ssh_version_receiver *rcv, srv->pinger = pinger_new(srv->conf, &srv->backend); #endif + if (srv->ssc->stunt_open_unconditional_agent_socket) { + char *socketname; + srv->stunt_agentfwd = agentfwd_new(srv->cl, &socketname); + if (srv->stunt_agentfwd) { + logeventf(srv->logctx, "opened unconditional agent socket at %s\n", + socketname); + sfree(socketname); + } + } + queue_idempotent_callback(&srv->bpp->ic_in_raw); ssh_ppl_process_queue(srv->base_layer); diff --git a/sshserver.h b/sshserver.h index 129d6fe..5cc393d 100644 --- a/sshserver.h +++ b/sshserver.h @@ -1,6 +1,7 @@ typedef struct AuthPolicy AuthPolicy; struct SshServerConfig { + const char *application_name; const char *session_starting_dir; RSAKey *rsa_kex_key; @@ -16,6 +17,10 @@ struct SshServerConfig { unsigned long ssh1_cipher_mask; bool ssh1_allow_compression; + bool bare_connection; + + bool stunt_pretend_to_accept_any_pubkey; + bool stunt_open_unconditional_agent_socket; }; Plug *ssh_server_plug( @@ -129,3 +134,10 @@ int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, Socket **sockets, Conf *conf); Conf *make_ssh_server_conf(void); + +/* Provided by Unix front end programs to uxsftpserver.c */ +void make_unix_sftp_filehandle_key(void *data, size_t size); + +typedef struct agentfwd agentfwd; +agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out); +void agentfwd_free(agentfwd *agent); diff --git a/sshsh256.c b/sshsh256.c index 1e44517..db1f96b 100644 --- a/sshsh256.c +++ b/sshsh256.c @@ -98,8 +98,10 @@ static ssh_hash *sha256_select(const ssh_hashalg *alg) } const ssh_hashalg ssh_sha256 = { - sha256_select, NULL, NULL, NULL, - 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "dummy selector vtable"), + .new = sha256_select, + .hlen = 32, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-256", "dummy selector vtable"), }; /* ---------------------------------------------------------------------- @@ -276,26 +278,28 @@ static ssh_hash *sha256_sw_new(const ssh_hashalg *alg) { sha256_sw *s = snew(sha256_sw); - memcpy(s->core, sha256_initial_state, sizeof(s->core)); - - sha256_block_setup(&s->blk); - s->hash.vt = alg; BinarySink_INIT(s, sha256_sw_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } -static ssh_hash *sha256_sw_copy(ssh_hash *hash) +static void sha256_sw_reset(ssh_hash *hash) { sha256_sw *s = container_of(hash, sha256_sw, hash); - sha256_sw *copy = snew(sha256_sw); - memcpy(copy, s, sizeof(*copy)); + memcpy(s->core, sha256_initial_state, sizeof(s->core)); + sha256_block_setup(&s->blk); +} + +static void sha256_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + sha256_sw *copy = container_of(hcopy, sha256_sw, hash); + sha256_sw *orig = container_of(horig, sha256_sw, hash); + + memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); - - return ©->hash; } static void sha256_sw_free(ssh_hash *hash) @@ -315,19 +319,24 @@ static void sha256_sw_write(BinarySink *bs, const void *vp, size_t len) sha256_sw_block(s->core, s->blk.block); } -static void sha256_sw_final(ssh_hash *hash, uint8_t *digest) +static void sha256_sw_digest(ssh_hash *hash, uint8_t *digest) { sha256_sw *s = container_of(hash, sha256_sw, hash); sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); for (size_t i = 0; i < 8; i++) PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]); - sha256_sw_free(hash); } const ssh_hashalg ssh_sha256_sw = { - sha256_sw_new, sha256_sw_copy, sha256_sw_final, sha256_sw_free, - 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "unaccelerated"), + .new = sha256_sw_new, + .reset = sha256_sw_reset, + .copyfrom = sha256_sw_copyfrom, + .digest = sha256_sw_digest, + .free = sha256_sw_free, + .hlen = 32, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-256", "unaccelerated"), }; /* ---------------------------------------------------------------------- @@ -602,13 +611,24 @@ static sha256_ni *sha256_ni_alloc(void) return s; } -FUNC_ISA static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) +static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) { if (!sha256_hw_available_cached()) return NULL; sha256_ni *s = sha256_ni_alloc(); + s->hash.vt = alg; + BinarySink_INIT(s, sha256_ni_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + + return &s->hash; +} + +FUNC_ISA static void sha256_ni_reset(ssh_hash *hash) +{ + sha256_ni *s = container_of(hash, sha256_ni, hash); + /* Initialise the core vectors in their storage order */ s->core[0] = _mm_set_epi64x( 0x6a09e667bb67ae85ULL, 0x510e527f9b05688cULL); @@ -616,26 +636,19 @@ FUNC_ISA static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) 0x3c6ef372a54ff53aULL, 0x1f83d9ab5be0cd19ULL); sha256_block_setup(&s->blk); - - s->hash.vt = alg; - BinarySink_INIT(s, sha256_ni_write); - BinarySink_DELEGATE_INIT(&s->hash, s); - return &s->hash; } -static ssh_hash *sha256_ni_copy(ssh_hash *hash) +static void sha256_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { - sha256_ni *s = container_of(hash, sha256_ni, hash); - sha256_ni *copy = sha256_ni_alloc(); + sha256_ni *copy = container_of(hcopy, sha256_ni, hash); + sha256_ni *orig = container_of(horig, sha256_ni, hash); void *ptf_save = copy->pointer_to_free; - *copy = *s; /* structure copy */ + *copy = *orig; /* structure copy */ copy->pointer_to_free = ptf_save; BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); - - return ©->hash; } static void sha256_ni_free(ssh_hash *hash) @@ -656,7 +669,7 @@ static void sha256_ni_write(BinarySink *bs, const void *vp, size_t len) sha256_ni_block(s->core, s->blk.block); } -FUNC_ISA static void sha256_ni_final(ssh_hash *hash, uint8_t *digest) +FUNC_ISA static void sha256_ni_digest(ssh_hash *hash, uint8_t *digest) { sha256_ni *s = container_of(hash, sha256_ni, hash); @@ -677,13 +690,17 @@ FUNC_ISA static void sha256_ni_final(ssh_hash *hash, uint8_t *digest) __m128i *output = (__m128i *)digest; _mm_storeu_si128(output, dcba); _mm_storeu_si128(output+1, hgfe); - - sha256_ni_free(hash); } const ssh_hashalg ssh_sha256_hw = { - sha256_ni_new, sha256_ni_copy, sha256_ni_final, sha256_ni_free, - 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "SHA-NI accelerated"), + .new = sha256_ni_new, + .reset = sha256_ni_reset, + .copyfrom = sha256_ni_copyfrom, + .digest = sha256_ni_digest, + .free = sha256_ni_free, + .hlen = 32, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-256", "SHA-NI accelerated"), }; /* ---------------------------------------------------------------------- @@ -706,6 +723,7 @@ const ssh_hashalg ssh_sha256_hw = { */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 +#define __ARM_FEATURE_SHA2 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ @@ -818,28 +836,31 @@ static ssh_hash *sha256_neon_new(const ssh_hashalg *alg) sha256_neon *s = snew(sha256_neon); - s->core.abcd = vld1q_u32(sha256_initial_state); - s->core.efgh = vld1q_u32(sha256_initial_state + 4); - - sha256_block_setup(&s->blk); - s->hash.vt = alg; BinarySink_INIT(s, sha256_neon_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } -static ssh_hash *sha256_neon_copy(ssh_hash *hash) +static void sha256_neon_reset(ssh_hash *hash) { sha256_neon *s = container_of(hash, sha256_neon, hash); - sha256_neon *copy = snew(sha256_neon); - *copy = *s; /* structure copy */ + s->core.abcd = vld1q_u32(sha256_initial_state); + s->core.efgh = vld1q_u32(sha256_initial_state + 4); + + sha256_block_setup(&s->blk); +} + +static void sha256_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + sha256_neon *copy = container_of(hcopy, sha256_neon, hash); + sha256_neon *orig = container_of(horig, sha256_neon, hash); + + *copy = *orig; /* structure copy */ BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); - - return ©->hash; } static void sha256_neon_free(ssh_hash *hash) @@ -858,19 +879,24 @@ static void sha256_neon_write(BinarySink *bs, const void *vp, size_t len) sha256_neon_block(&s->core, s->blk.block); } -static void sha256_neon_final(ssh_hash *hash, uint8_t *digest) +static void sha256_neon_digest(ssh_hash *hash, uint8_t *digest) { sha256_neon *s = container_of(hash, sha256_neon, hash); sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd))); vst1q_u8(digest + 16, vrev32q_u8(vreinterpretq_u8_u32(s->core.efgh))); - sha256_neon_free(hash); } const ssh_hashalg ssh_sha256_hw = { - sha256_neon_new, sha256_neon_copy, sha256_neon_final, sha256_neon_free, - 32, 64, HASHALG_NAMES_ANNOTATED("SHA-256", "NEON accelerated"), + .new = sha256_neon_new, + .reset = sha256_neon_reset, + .copyfrom = sha256_neon_copyfrom, + .digest = sha256_neon_digest, + .free = sha256_neon_free, + .hlen = 32, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-256", "NEON accelerated"), }; /* ---------------------------------------------------------------------- @@ -895,14 +921,20 @@ static ssh_hash *sha256_stub_new(const ssh_hashalg *alg) #define STUB_BODY { unreachable("Should never be called"); } -static ssh_hash *sha256_stub_copy(ssh_hash *hash) STUB_BODY +static void sha256_stub_reset(ssh_hash *hash) STUB_BODY +static void sha256_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY static void sha256_stub_free(ssh_hash *hash) STUB_BODY -static void sha256_stub_final(ssh_hash *hash, uint8_t *digest) STUB_BODY +static void sha256_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY const ssh_hashalg ssh_sha256_hw = { - sha256_stub_new, sha256_stub_copy, sha256_stub_final, sha256_stub_free, - 32, 64, HASHALG_NAMES_ANNOTATED( - "SHA-256", "!NONEXISTENT ACCELERATED VERSION!"), + .new = sha256_stub_new, + .reset = sha256_stub_reset, + .copyfrom = sha256_stub_copyfrom, + .digest = sha256_stub_digest, + .free = sha256_stub_free, + .hlen = 32, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-256", "!NONEXISTENT ACCELERATED VERSION!"), }; #endif /* HW_SHA256 */ diff --git a/sshsh512.c b/sshsh512.c index 6712047..cba7f38 100644 --- a/sshsh512.c +++ b/sshsh512.c @@ -9,361 +9,828 @@ #include #include "ssh.h" -#define BLKSIZE 128 +/* + * Start by deciding whether we can support hardware SHA at all. + */ +#define HW_SHA512_NONE 0 +#define HW_SHA512_NEON 1 + +#ifdef _FORCE_SHA512_NEON +# define HW_SHA512 HW_SHA512_NEON +#elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + /* Arm can potentially support both endiannesses, but this code + * hasn't been tested on anything but little. If anyone wants to + * run big-endian, they'll need to fix it first. */ +#elif defined __ARM_FEATURE_SHA512 + /* If the Arm SHA-512 extension is available already, we can + * support NEON SHA without having to enable anything by hand */ +# define HW_SHA512 HW_SHA512_NEON +#elif defined(__clang__) +# if __has_attribute(target) && __has_include() && \ + (defined(__aarch64__)) + /* clang can enable the crypto extension in AArch64 using + * __attribute__((target)) */ +# define HW_SHA512 HW_SHA512_NEON +# define USE_CLANG_ATTR_TARGET_AARCH64 +# endif +#endif + +#if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA512 +# undef HW_SHA512 +# define HW_SHA512 HW_SHA512_NONE +#endif -typedef struct { - uint64_t h[8]; - unsigned char block[BLKSIZE]; - int blkused; - uint64_t lenhi, lenlo; - BinarySink_IMPLEMENTATION; -} SHA512_State; +/* + * The actual query function that asks if hardware acceleration is + * available. + */ +static bool sha512_hw_available(void); /* - * Arithmetic implementations. Note that AND, XOR and NOT can - * overlap destination with one source, but the others can't. + * The top-level selection function, caching the results of + * sha512_hw_available() so it only has to run once. */ -#define add(r,x,y) ( r = (x) + (y) ) -#define rorB(r,x,y) ( r = ((x) >> (y)) | ((x) << (64-(y))) ) -#define rorL(r,x,y) ( r = ((x) >> (y)) | ((x) << (64-(y))) ) -#define shrB(r,x,y) ( r = (x) >> (y) ) -#define shrL(r,x,y) ( r = (x) >> (y) ) -#define and(r,x,y) ( r = (x) & (y) ) -#define xor(r,x,y) ( r = (x) ^ (y) ) -#define not(r,x) ( r = ~(x) ) -#define INIT(h,l) ((((uint64_t)(h)) << 32) | (l)) -#define BUILD(r,h,l) ( r = ((((uint64_t)(h)) << 32) | (l)) ) -#define EXTRACT(h,l,r) ( h = (r) >> 32, l = (r) & 0xFFFFFFFFU ) +static bool sha512_hw_available_cached(void) +{ + static bool initialised = false; + static bool hw_available; + if (!initialised) { + hw_available = sha512_hw_available(); + initialised = true; + } + return hw_available; +} + +struct sha512_select_options { + const ssh_hashalg *hw, *sw; +}; + +static ssh_hash *sha512_select(const ssh_hashalg *alg) +{ + const struct sha512_select_options *options = + (const struct sha512_select_options *)alg->extra; + + const ssh_hashalg *real_alg = + sha512_hw_available_cached() ? options->hw : options->sw; + + return ssh_hash_new(real_alg); +} + +const struct sha512_select_options ssh_sha512_select_options = { + &ssh_sha512_hw, &ssh_sha512_sw, +}; +const struct sha512_select_options ssh_sha384_select_options = { + &ssh_sha384_hw, &ssh_sha384_sw, +}; + +const ssh_hashalg ssh_sha512 = { + .new = sha512_select, + .hlen = 64, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-512", "dummy selector vtable"), + .extra = &ssh_sha512_select_options, +}; + +const ssh_hashalg ssh_sha384 = { + .new = sha512_select, + .hlen = 48, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-384", "dummy selector vtable"), + .extra = &ssh_sha384_select_options, +}; /* ---------------------------------------------------------------------- - * Core SHA512 algorithm: processes 16-doubleword blocks into a - * message digest. + * Definitions likely to be helpful to multiple implementations. */ -#define Ch(r,t,x,y,z) ( not(t,x), and(r,t,z), and(t,x,y), xor(r,r,t) ) -#define Maj(r,t,x,y,z) ( and(r,x,y), and(t,x,z), xor(r,r,t), \ - and(t,y,z), xor(r,r,t) ) -#define bigsigma0(r,t,x) ( rorL(r,x,28), rorB(t,x,34), xor(r,r,t), \ - rorB(t,x,39), xor(r,r,t) ) -#define bigsigma1(r,t,x) ( rorL(r,x,14), rorL(t,x,18), xor(r,r,t), \ - rorB(t,x,41), xor(r,r,t) ) -#define smallsigma0(r,t,x) ( rorL(r,x,1), rorL(t,x,8), xor(r,r,t), \ - shrL(t,x,7), xor(r,r,t) ) -#define smallsigma1(r,t,x) ( rorL(r,x,19), rorB(t,x,61), xor(r,r,t), \ - shrL(t,x,6), xor(r,r,t) ) - -static void SHA512_Core_Init(SHA512_State *s) { - static const uint64_t iv[] = { - INIT(0x6a09e667, 0xf3bcc908), - INIT(0xbb67ae85, 0x84caa73b), - INIT(0x3c6ef372, 0xfe94f82b), - INIT(0xa54ff53a, 0x5f1d36f1), - INIT(0x510e527f, 0xade682d1), - INIT(0x9b05688c, 0x2b3e6c1f), - INIT(0x1f83d9ab, 0xfb41bd6b), - INIT(0x5be0cd19, 0x137e2179), - }; - int i; - for (i = 0; i < 8; i++) - s->h[i] = iv[i]; -} - -static void SHA384_Core_Init(SHA512_State *s) { - static const uint64_t iv[] = { - INIT(0xcbbb9d5d, 0xc1059ed8), - INIT(0x629a292a, 0x367cd507), - INIT(0x9159015a, 0x3070dd17), - INIT(0x152fecd8, 0xf70e5939), - INIT(0x67332667, 0xffc00b31), - INIT(0x8eb44a87, 0x68581511), - INIT(0xdb0c2e0d, 0x64f98fa7), - INIT(0x47b5481d, 0xbefa4fa4), - }; - int i; - for (i = 0; i < 8; i++) - s->h[i] = iv[i]; -} - -static void SHA512_Block(SHA512_State *s, uint64_t *block) { - uint64_t w[80]; - uint64_t a,b,c,d,e,f,g,h; - static const uint64_t k[] = { - INIT(0x428a2f98, 0xd728ae22), INIT(0x71374491, 0x23ef65cd), - INIT(0xb5c0fbcf, 0xec4d3b2f), INIT(0xe9b5dba5, 0x8189dbbc), - INIT(0x3956c25b, 0xf348b538), INIT(0x59f111f1, 0xb605d019), - INIT(0x923f82a4, 0xaf194f9b), INIT(0xab1c5ed5, 0xda6d8118), - INIT(0xd807aa98, 0xa3030242), INIT(0x12835b01, 0x45706fbe), - INIT(0x243185be, 0x4ee4b28c), INIT(0x550c7dc3, 0xd5ffb4e2), - INIT(0x72be5d74, 0xf27b896f), INIT(0x80deb1fe, 0x3b1696b1), - INIT(0x9bdc06a7, 0x25c71235), INIT(0xc19bf174, 0xcf692694), - INIT(0xe49b69c1, 0x9ef14ad2), INIT(0xefbe4786, 0x384f25e3), - INIT(0x0fc19dc6, 0x8b8cd5b5), INIT(0x240ca1cc, 0x77ac9c65), - INIT(0x2de92c6f, 0x592b0275), INIT(0x4a7484aa, 0x6ea6e483), - INIT(0x5cb0a9dc, 0xbd41fbd4), INIT(0x76f988da, 0x831153b5), - INIT(0x983e5152, 0xee66dfab), INIT(0xa831c66d, 0x2db43210), - INIT(0xb00327c8, 0x98fb213f), INIT(0xbf597fc7, 0xbeef0ee4), - INIT(0xc6e00bf3, 0x3da88fc2), INIT(0xd5a79147, 0x930aa725), - INIT(0x06ca6351, 0xe003826f), INIT(0x14292967, 0x0a0e6e70), - INIT(0x27b70a85, 0x46d22ffc), INIT(0x2e1b2138, 0x5c26c926), - INIT(0x4d2c6dfc, 0x5ac42aed), INIT(0x53380d13, 0x9d95b3df), - INIT(0x650a7354, 0x8baf63de), INIT(0x766a0abb, 0x3c77b2a8), - INIT(0x81c2c92e, 0x47edaee6), INIT(0x92722c85, 0x1482353b), - INIT(0xa2bfe8a1, 0x4cf10364), INIT(0xa81a664b, 0xbc423001), - INIT(0xc24b8b70, 0xd0f89791), INIT(0xc76c51a3, 0x0654be30), - INIT(0xd192e819, 0xd6ef5218), INIT(0xd6990624, 0x5565a910), - INIT(0xf40e3585, 0x5771202a), INIT(0x106aa070, 0x32bbd1b8), - INIT(0x19a4c116, 0xb8d2d0c8), INIT(0x1e376c08, 0x5141ab53), - INIT(0x2748774c, 0xdf8eeb99), INIT(0x34b0bcb5, 0xe19b48a8), - INIT(0x391c0cb3, 0xc5c95a63), INIT(0x4ed8aa4a, 0xe3418acb), - INIT(0x5b9cca4f, 0x7763e373), INIT(0x682e6ff3, 0xd6b2b8a3), - INIT(0x748f82ee, 0x5defb2fc), INIT(0x78a5636f, 0x43172f60), - INIT(0x84c87814, 0xa1f0ab72), INIT(0x8cc70208, 0x1a6439ec), - INIT(0x90befffa, 0x23631e28), INIT(0xa4506ceb, 0xde82bde9), - INIT(0xbef9a3f7, 0xb2c67915), INIT(0xc67178f2, 0xe372532b), - INIT(0xca273ece, 0xea26619c), INIT(0xd186b8c7, 0x21c0c207), - INIT(0xeada7dd6, 0xcde0eb1e), INIT(0xf57d4f7f, 0xee6ed178), - INIT(0x06f067aa, 0x72176fba), INIT(0x0a637dc5, 0xa2c898a6), - INIT(0x113f9804, 0xbef90dae), INIT(0x1b710b35, 0x131c471b), - INIT(0x28db77f5, 0x23047d84), INIT(0x32caab7b, 0x40c72493), - INIT(0x3c9ebe0a, 0x15c9bebc), INIT(0x431d67c4, 0x9c100d4c), - INIT(0x4cc5d4be, 0xcb3e42b6), INIT(0x597f299c, 0xfc657e2a), - INIT(0x5fcb6fab, 0x3ad6faec), INIT(0x6c44198c, 0x4a475817), - }; +static const uint64_t sha512_initial_state[] = { + 0x6a09e667f3bcc908ULL, + 0xbb67ae8584caa73bULL, + 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, + 0x510e527fade682d1ULL, + 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, + 0x5be0cd19137e2179ULL, +}; - int t; +static const uint64_t sha384_initial_state[] = { + 0xcbbb9d5dc1059ed8ULL, + 0x629a292a367cd507ULL, + 0x9159015a3070dd17ULL, + 0x152fecd8f70e5939ULL, + 0x67332667ffc00b31ULL, + 0x8eb44a8768581511ULL, + 0xdb0c2e0d64f98fa7ULL, + 0x47b5481dbefa4fa4ULL, +}; - for (t = 0; t < 16; t++) - w[t] = block[t]; - - for (t = 16; t < 80; t++) { - uint64_t p, q, r, tmp; - smallsigma1(p, tmp, w[t-2]); - smallsigma0(q, tmp, w[t-15]); - add(r, p, q); - add(p, r, w[t-7]); - add(w[t], p, w[t-16]); - } +static const uint64_t sha512_round_constants[] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, + 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, + 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, + 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, + 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, + 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, + 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, + 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, + 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, + 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, + 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, + 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, + 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, + 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, +}; - a = s->h[0]; b = s->h[1]; c = s->h[2]; d = s->h[3]; - e = s->h[4]; f = s->h[5]; g = s->h[6]; h = s->h[7]; - - for (t = 0; t < 80; t+=8) { - uint64_t tmp, p, q, r; - -#define ROUND(j,a,b,c,d,e,f,g,h) \ - bigsigma1(p, tmp, e); \ - Ch(q, tmp, e, f, g); \ - add(r, p, q); \ - add(p, r, k[j]) ; \ - add(q, p, w[j]); \ - add(r, q, h); \ - bigsigma0(p, tmp, a); \ - Maj(tmp, q, a, b, c); \ - add(q, tmp, p); \ - add(p, r, d); \ - d = p; \ - add(h, q, r); - - ROUND(t+0, a,b,c,d,e,f,g,h); - ROUND(t+1, h,a,b,c,d,e,f,g); - ROUND(t+2, g,h,a,b,c,d,e,f); - ROUND(t+3, f,g,h,a,b,c,d,e); - ROUND(t+4, e,f,g,h,a,b,c,d); - ROUND(t+5, d,e,f,g,h,a,b,c); - ROUND(t+6, c,d,e,f,g,h,a,b); - ROUND(t+7, b,c,d,e,f,g,h,a); - } +#define SHA512_ROUNDS 80 + +typedef struct sha512_block sha512_block; +struct sha512_block { + uint8_t block[128]; + size_t used; + uint64_t lenhi, lenlo; +}; + +static inline void sha512_block_setup(sha512_block *blk) +{ + blk->used = 0; + blk->lenhi = blk->lenlo = 0; +} + +static inline bool sha512_block_write( + sha512_block *blk, const void **vdata, size_t *len) +{ + size_t blkleft = sizeof(blk->block) - blk->used; + size_t chunk = *len < blkleft ? *len : blkleft; + + const uint8_t *p = *vdata; + memcpy(blk->block + blk->used, p, chunk); + *vdata = p + chunk; + *len -= chunk; + blk->used += chunk; - { - uint64_t tmp; -#define UPDATE(state, local) ( tmp = state, add(state, tmp, local) ) - UPDATE(s->h[0], a); UPDATE(s->h[1], b); - UPDATE(s->h[2], c); UPDATE(s->h[3], d); - UPDATE(s->h[4], e); UPDATE(s->h[5], f); - UPDATE(s->h[6], g); UPDATE(s->h[7], h); + size_t chunkbits = chunk << 3; + + blk->lenlo += chunkbits; + blk->lenhi += (blk->lenlo < chunkbits); + + if (blk->used == sizeof(blk->block)) { + blk->used = 0; + return true; } + + return false; +} + +static inline void sha512_block_pad(sha512_block *blk, BinarySink *bs) +{ + uint64_t final_lenhi = blk->lenhi; + uint64_t final_lenlo = blk->lenlo; + size_t pad = 127 & (111 - blk->used); + + put_byte(bs, 0x80); + put_padding(bs, pad, 0); + put_uint64(bs, final_lenhi); + put_uint64(bs, final_lenlo); + + assert(blk->used == 0 && "Should have exactly hit a block boundary"); } /* ---------------------------------------------------------------------- - * Outer SHA512 algorithm: take an arbitrary length byte string, - * convert it into 16-doubleword blocks with the prescribed padding - * at the end, and pass those blocks to the core SHA512 algorithm. + * Software implementation of SHA-512. */ -static void SHA512_BinarySink_write(BinarySink *bs, - const void *p, size_t len); +static inline uint64_t ror(uint64_t x, unsigned y) +{ + return (x << (63 & -y)) | (x >> (63 & y)); +} -void SHA512_Init(SHA512_State *s) { - SHA512_Core_Init(s); - s->blkused = 0; - s->lenhi = s->lenlo = 0; - BinarySink_INIT(s, SHA512_BinarySink_write); +static inline uint64_t Ch(uint64_t ctrl, uint64_t if1, uint64_t if0) +{ + return if0 ^ (ctrl & (if1 ^ if0)); } -void SHA384_Init(SHA512_State *s) { - SHA384_Core_Init(s); - s->blkused = 0; - s->lenhi = s->lenlo = 0; - BinarySink_INIT(s, SHA512_BinarySink_write); +static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z) +{ + return (x & y) | (z & (x | y)); } -static void SHA512_BinarySink_write(BinarySink *bs, - const void *p, size_t len) +static inline uint64_t Sigma_0(uint64_t x) { - SHA512_State *s = BinarySink_DOWNCAST(bs, SHA512_State); - unsigned char *q = (unsigned char *)p; - uint64_t wordblock[16]; - int i; + return ror(x,28) ^ ror(x,34) ^ ror(x,39); +} - /* - * Update the length field. - */ - s->lenlo += len; - s->lenhi += (s->lenlo < len); - - if (s->blkused && s->blkused+len < BLKSIZE) { - /* - * Trivial case: just add to the block. - */ - memcpy(s->block + s->blkused, q, len); - s->blkused += len; - } else { - /* - * We must complete and process at least one block. - */ - while (s->blkused + len >= BLKSIZE) { - memcpy(s->block + s->blkused, q, BLKSIZE - s->blkused); - q += BLKSIZE - s->blkused; - len -= BLKSIZE - s->blkused; - /* Now process the block. Gather bytes big-endian into words */ - for (i = 0; i < 16; i++) - wordblock[i] = GET_64BIT_MSB_FIRST(s->block + i*8); - SHA512_Block(s, wordblock); - s->blkused = 0; - } - memcpy(s->block, q, len); - s->blkused = len; +static inline uint64_t Sigma_1(uint64_t x) +{ + return ror(x,14) ^ ror(x,18) ^ ror(x,41); +} + +static inline uint64_t sigma_0(uint64_t x) +{ + return ror(x,1) ^ ror(x,8) ^ (x >> 7); +} + +static inline uint64_t sigma_1(uint64_t x) +{ + return ror(x,19) ^ ror(x,61) ^ (x >> 6); +} + +static inline void sha512_sw_round( + unsigned round_index, const uint64_t *schedule, + uint64_t *a, uint64_t *b, uint64_t *c, uint64_t *d, + uint64_t *e, uint64_t *f, uint64_t *g, uint64_t *h) +{ + uint64_t t1 = *h + Sigma_1(*e) + Ch(*e,*f,*g) + + sha512_round_constants[round_index] + schedule[round_index]; + + uint64_t t2 = Sigma_0(*a) + Maj(*a,*b,*c); + + *d += t1; + *h = t1 + t2; +} + +static void sha512_sw_block(uint64_t *core, const uint8_t *block) +{ + uint64_t w[SHA512_ROUNDS]; + uint64_t a,b,c,d,e,f,g,h; + + int t; + + for (t = 0; t < 16; t++) + w[t] = GET_64BIT_MSB_FIRST(block + 8*t); + + for (t = 16; t < SHA512_ROUNDS; t++) + w[t] = w[t-16] + w[t-7] + sigma_0(w[t-15]) + sigma_1(w[t-2]); + + a = core[0]; b = core[1]; c = core[2]; d = core[3]; + e = core[4]; f = core[5]; g = core[6]; h = core[7]; + + for (t = 0; t < SHA512_ROUNDS; t+=8) { + sha512_sw_round(t+0, w, &a,&b,&c,&d,&e,&f,&g,&h); + sha512_sw_round(t+1, w, &h,&a,&b,&c,&d,&e,&f,&g); + sha512_sw_round(t+2, w, &g,&h,&a,&b,&c,&d,&e,&f); + sha512_sw_round(t+3, w, &f,&g,&h,&a,&b,&c,&d,&e); + sha512_sw_round(t+4, w, &e,&f,&g,&h,&a,&b,&c,&d); + sha512_sw_round(t+5, w, &d,&e,&f,&g,&h,&a,&b,&c); + sha512_sw_round(t+6, w, &c,&d,&e,&f,&g,&h,&a,&b); + sha512_sw_round(t+7, w, &b,&c,&d,&e,&f,&g,&h,&a); } + + core[0] += a; core[1] += b; core[2] += c; core[3] += d; + core[4] += e; core[5] += f; core[6] += g; core[7] += h; + + smemclr(w, sizeof(w)); } -void SHA512_Final(SHA512_State *s, unsigned char *digest) { - int i; - int pad; - unsigned char c[BLKSIZE]; - uint64_t lenhi, lenlo; +typedef struct sha512_sw { + uint64_t core[8]; + sha512_block blk; + BinarySink_IMPLEMENTATION; + ssh_hash hash; +} sha512_sw; + +static void sha512_sw_write(BinarySink *bs, const void *vp, size_t len); + +static ssh_hash *sha512_sw_new(const ssh_hashalg *alg) +{ + sha512_sw *s = snew(sha512_sw); - if (s->blkused >= BLKSIZE-16) - pad = (BLKSIZE-16) + BLKSIZE - s->blkused; - else - pad = (BLKSIZE-16) - s->blkused; + s->hash.vt = alg; + BinarySink_INIT(s, sha512_sw_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; +} - lenhi = (s->lenhi << 3) | (s->lenlo >> (32-3)); - lenlo = (s->lenlo << 3); +static void sha512_sw_reset(ssh_hash *hash) +{ + sha512_sw *s = container_of(hash, sha512_sw, hash); - memset(c, 0, pad); - c[0] = 0x80; - put_data(s, &c, pad); + /* The 'extra' field in the ssh_hashalg indicates which + * initialisation vector we're using */ + memcpy(s->core, hash->vt->extra, sizeof(s->core)); + sha512_block_setup(&s->blk); +} - put_uint64(s, lenhi); - put_uint64(s, lenlo); +static void sha512_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + sha512_sw *copy = container_of(hcopy, sha512_sw, hash); + sha512_sw *orig = container_of(horig, sha512_sw, hash); - for (i = 0; i < 8; i++) - PUT_64BIT_MSB_FIRST(digest + i*8, s->h[i]); + memcpy(copy, orig, sizeof(*copy)); + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); } -void SHA384_Final(SHA512_State *s, unsigned char *digest) { - unsigned char biggerDigest[512 / 8]; - SHA512_Final(s, biggerDigest); - memcpy(digest, biggerDigest, 384 / 8); +static void sha512_sw_free(ssh_hash *hash) +{ + sha512_sw *s = container_of(hash, sha512_sw, hash); + + smemclr(s, sizeof(*s)); + sfree(s); } -void SHA512_Simple(const void *p, int len, unsigned char *output) { - SHA512_State s; +static void sha512_sw_write(BinarySink *bs, const void *vp, size_t len) +{ + sha512_sw *s = BinarySink_DOWNCAST(bs, sha512_sw); - SHA512_Init(&s); - put_data(&s, p, len); - SHA512_Final(&s, output); - smemclr(&s, sizeof(s)); + while (len > 0) + if (sha512_block_write(&s->blk, &vp, &len)) + sha512_sw_block(s->core, s->blk.block); } -void SHA384_Simple(const void *p, int len, unsigned char *output) { - SHA512_State s; +static void sha512_sw_digest(ssh_hash *hash, uint8_t *digest) +{ + sha512_sw *s = container_of(hash, sha512_sw, hash); - SHA384_Init(&s); - put_data(&s, p, len); - SHA384_Final(&s, output); - smemclr(&s, sizeof(s)); + sha512_block_pad(&s->blk, BinarySink_UPCAST(s)); + for (size_t i = 0; i < hash->vt->hlen / 8; i++) + PUT_64BIT_MSB_FIRST(digest + 8*i, s->core[i]); } +const ssh_hashalg ssh_sha512_sw = { + .new = sha512_sw_new, + .reset = sha512_sw_reset, + .copyfrom = sha512_sw_copyfrom, + .digest = sha512_sw_digest, + .free = sha512_sw_free, + .hlen = 64, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-512", "unaccelerated"), + .extra = sha512_initial_state, +}; + +const ssh_hashalg ssh_sha384_sw = { + .new = sha512_sw_new, + .reset = sha512_sw_reset, + .copyfrom = sha512_sw_copyfrom, + .digest = sha512_sw_digest, + .free = sha512_sw_free, + .hlen = 48, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-384", "unaccelerated"), + .extra = sha384_initial_state, +}; + +/* ---------------------------------------------------------------------- + * Hardware-accelerated implementation of SHA-512 using Arm NEON. + */ + +#if HW_SHA512 == HW_SHA512_NEON + +/* + * Manually set the target architecture, if we decided above that we + * need to. + */ +#ifdef USE_CLANG_ATTR_TARGET_AARCH64 /* - * Thin abstraction for things where hashes are pluggable. + * A spot of cheating: redefine some ACLE feature macros before + * including arm_neon.h. Otherwise we won't get the SHA intrinsics + * defined by that header, because it will be looking at the settings + * for the whole translation unit rather than the ones we're going to + * put on some particular functions using __attribute__((target)). */ +#define __ARM_NEON 1 +#define __ARM_FEATURE_CRYPTO 1 +#define FUNC_ISA __attribute__ ((target("neon,sha3"))) +#endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ + +#ifndef FUNC_ISA +#define FUNC_ISA +#endif + +#ifdef USE_ARM64_NEON_H +#include +#else +#include +#endif + +static bool sha512_hw_available(void) +{ + /* + * For Arm, we delegate to a per-platform detection function (see + * explanation in sshaes.c). + */ + return platform_sha512_hw_available(); +} -struct sha512_hash { - SHA512_State state; - ssh_hash hash; +#if defined __clang__ +/* + * As of 2020-12-24, I've found that clang doesn't provide the SHA-512 + * NEON intrinsics. So I define my own set using inline assembler, and + * use #define to effectively rename them over the top of the standard + * names. + * + * The aim of that #define technique is that it should avoid a build + * failure if these intrinsics _are_ defined in . + * Obviously it would be better in that situation to switch back to + * using the real intrinsics, but until I see a version of clang that + * supports them, I won't know what version number to test in the + * ifdef. + */ +static inline FUNC_ISA +uint64x2_t vsha512su0q_u64_asm(uint64x2_t x, uint64x2_t y) { + __asm__("sha512su0 %0.2D,%1.2D" : "+w" (x) : "w" (y)); + return x; +} +static inline FUNC_ISA +uint64x2_t vsha512su1q_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) { + __asm__("sha512su1 %0.2D,%1.2D,%2.2D" : "+w" (x) : "w" (y), "w" (z)); + return x; +} +static inline FUNC_ISA +uint64x2_t vsha512hq_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) { + __asm__("sha512h %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z)); + return x; +} +static inline FUNC_ISA +uint64x2_t vsha512h2q_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) { + __asm__("sha512h2 %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z)); + return x; +} +#undef vsha512su0q_u64 +#define vsha512su0q_u64 vsha512su0q_u64_asm +#undef vsha512su1q_u64 +#define vsha512su1q_u64 vsha512su1q_u64_asm +#undef vsha512hq_u64 +#define vsha512hq_u64 vsha512hq_u64_asm +#undef vsha512h2q_u64 +#define vsha512h2q_u64 vsha512h2q_u64_asm +#endif /* defined __clang__ */ + +typedef struct sha512_neon_core sha512_neon_core; +struct sha512_neon_core { + uint64x2_t ab, cd, ef, gh; }; -static ssh_hash *sha512_new(const ssh_hashalg *alg) +FUNC_ISA +static inline uint64x2_t sha512_neon_load_input(const uint8_t *p) +{ + return vreinterpretq_u64_u8(vrev64q_u8(vld1q_u8(p))); +} + +FUNC_ISA +static inline uint64x2_t sha512_neon_schedule_update( + uint64x2_t m8, uint64x2_t m7, uint64x2_t m4, uint64x2_t m3, uint64x2_t m1) +{ + /* + * vsha512su0q_u64() takes words from a long way back in the + * schedule and performs the sigma_0 half of the computation of + * the next two 64-bit message-schedule words. + * + * vsha512su1q_u64() combines the result of that with the sigma_1 + * steps, to output the finished version of those two words. The + * total amount of input data it requires fits nicely into three + * 128-bit vector registers, but one of those registers is + * misaligned compared to the 128-bit chunks that the message + * schedule is stored in. So we use vextq_u64 to make one of its + * input words out of the second half of m4 and the first half of + * m3. + */ + return vsha512su1q_u64(vsha512su0q_u64(m8, m7), m1, vextq_u64(m4, m3, 1)); +} + +FUNC_ISA +static inline void sha512_neon_round2( + unsigned round_index, uint64x2_t schedule_words, + uint64x2_t *ab, uint64x2_t *cd, uint64x2_t *ef, uint64x2_t *gh) +{ + /* + * vsha512hq_u64 performs the Sigma_1 and Ch half of the + * computation of two rounds of SHA-512 (including feeding back + * one of the outputs from the first of those half-rounds into the + * second one). + * + * vsha512h2q_u64 combines the result of that with the Sigma_0 and + * Maj steps, and outputs one 128-bit vector that replaces the gh + * piece of the input hash state, and a second that updates cd by + * addition. + * + * Similarly to vsha512su1q_u64 above, some of the input registers + * expected by these instructions are misaligned by 64 bits + * relative to the chunks we've divided the hash state into, so we + * have to start by making 'de' and 'fg' words out of our input + * cd,ef,gh, using vextq_u64. + * + * Also, one of the inputs to vsha512hq_u64 is expected to contain + * the results of summing gh + two round constants + two words of + * message schedule, but the two words of the message schedule + * have to be the opposite way round in the vector register from + * the way that vsha512su1q_u64 output them. Hence, there's + * another vextq_u64 in here that swaps the two halves of the + * initial_sum vector register. + * + * (This also means that I don't have to prepare a specially + * reordered version of the sha512_round_constants[] array: as + * long as I'm unavoidably doing a swap at run time _anyway_, I + * can load from the normally ordered version of that array, and + * just take care to fold in that data _before_ the swap rather + * than after.) + */ + + /* Load two round constants, with the first one in the low half */ + uint64x2_t round_constants = vld1q_u64( + sha512_round_constants + round_index); + + /* Add schedule words to round constants */ + uint64x2_t initial_sum = vaddq_u64(schedule_words, round_constants); + + /* Swap that sum around so the word used in the first of the two + * rounds is in the _high_ half of the vector, matching where h + * lives in the gh vector */ + uint64x2_t swapped_initial_sum = vextq_u64(initial_sum, initial_sum, 1); + + /* Add gh to that, now that they're matching ways round */ + uint64x2_t sum = vaddq_u64(swapped_initial_sum, *gh); + + /* Make the misaligned de and fg words */ + uint64x2_t de = vextq_u64(*cd, *ef, 1); + uint64x2_t fg = vextq_u64(*ef, *gh, 1); + + /* Now we're ready to put all the pieces together. The output from + * vsha512h2q_u64 can be used directly as the new gh, and the + * output from vsha512hq_u64 is simultaneously the intermediate + * value passed to h2 and the thing you have to add on to cd. */ + uint64x2_t intermed = vsha512hq_u64(sum, fg, de); + *gh = vsha512h2q_u64(intermed, *cd, *ab); + *cd = vaddq_u64(*cd, intermed); +} + +FUNC_ISA +static inline void sha512_neon_block(sha512_neon_core *core, const uint8_t *p) { - struct sha512_hash *h = snew(struct sha512_hash); - SHA512_Init(&h->state); - h->hash.vt = alg; - BinarySink_DELEGATE_INIT(&h->hash, &h->state); - return &h->hash; + uint64x2_t s0, s1, s2, s3, s4, s5, s6, s7; + + uint64x2_t ab = core->ab, cd = core->cd, ef = core->ef, gh = core->gh; + + s0 = sha512_neon_load_input(p + 16*0); + sha512_neon_round2(0, s0, &ab, &cd, &ef, &gh); + s1 = sha512_neon_load_input(p + 16*1); + sha512_neon_round2(2, s1, &gh, &ab, &cd, &ef); + s2 = sha512_neon_load_input(p + 16*2); + sha512_neon_round2(4, s2, &ef, &gh, &ab, &cd); + s3 = sha512_neon_load_input(p + 16*3); + sha512_neon_round2(6, s3, &cd, &ef, &gh, &ab); + s4 = sha512_neon_load_input(p + 16*4); + sha512_neon_round2(8, s4, &ab, &cd, &ef, &gh); + s5 = sha512_neon_load_input(p + 16*5); + sha512_neon_round2(10, s5, &gh, &ab, &cd, &ef); + s6 = sha512_neon_load_input(p + 16*6); + sha512_neon_round2(12, s6, &ef, &gh, &ab, &cd); + s7 = sha512_neon_load_input(p + 16*7); + sha512_neon_round2(14, s7, &cd, &ef, &gh, &ab); + s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); + sha512_neon_round2(16, s0, &ab, &cd, &ef, &gh); + s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); + sha512_neon_round2(18, s1, &gh, &ab, &cd, &ef); + s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); + sha512_neon_round2(20, s2, &ef, &gh, &ab, &cd); + s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); + sha512_neon_round2(22, s3, &cd, &ef, &gh, &ab); + s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); + sha512_neon_round2(24, s4, &ab, &cd, &ef, &gh); + s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); + sha512_neon_round2(26, s5, &gh, &ab, &cd, &ef); + s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); + sha512_neon_round2(28, s6, &ef, &gh, &ab, &cd); + s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); + sha512_neon_round2(30, s7, &cd, &ef, &gh, &ab); + s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); + sha512_neon_round2(32, s0, &ab, &cd, &ef, &gh); + s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); + sha512_neon_round2(34, s1, &gh, &ab, &cd, &ef); + s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); + sha512_neon_round2(36, s2, &ef, &gh, &ab, &cd); + s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); + sha512_neon_round2(38, s3, &cd, &ef, &gh, &ab); + s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); + sha512_neon_round2(40, s4, &ab, &cd, &ef, &gh); + s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); + sha512_neon_round2(42, s5, &gh, &ab, &cd, &ef); + s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); + sha512_neon_round2(44, s6, &ef, &gh, &ab, &cd); + s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); + sha512_neon_round2(46, s7, &cd, &ef, &gh, &ab); + s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); + sha512_neon_round2(48, s0, &ab, &cd, &ef, &gh); + s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); + sha512_neon_round2(50, s1, &gh, &ab, &cd, &ef); + s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); + sha512_neon_round2(52, s2, &ef, &gh, &ab, &cd); + s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); + sha512_neon_round2(54, s3, &cd, &ef, &gh, &ab); + s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); + sha512_neon_round2(56, s4, &ab, &cd, &ef, &gh); + s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); + sha512_neon_round2(58, s5, &gh, &ab, &cd, &ef); + s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); + sha512_neon_round2(60, s6, &ef, &gh, &ab, &cd); + s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); + sha512_neon_round2(62, s7, &cd, &ef, &gh, &ab); + s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); + sha512_neon_round2(64, s0, &ab, &cd, &ef, &gh); + s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); + sha512_neon_round2(66, s1, &gh, &ab, &cd, &ef); + s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); + sha512_neon_round2(68, s2, &ef, &gh, &ab, &cd); + s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); + sha512_neon_round2(70, s3, &cd, &ef, &gh, &ab); + s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); + sha512_neon_round2(72, s4, &ab, &cd, &ef, &gh); + s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); + sha512_neon_round2(74, s5, &gh, &ab, &cd, &ef); + s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); + sha512_neon_round2(76, s6, &ef, &gh, &ab, &cd); + s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); + sha512_neon_round2(78, s7, &cd, &ef, &gh, &ab); + + core->ab = vaddq_u64(core->ab, ab); + core->cd = vaddq_u64(core->cd, cd); + core->ef = vaddq_u64(core->ef, ef); + core->gh = vaddq_u64(core->gh, gh); } -static ssh_hash *sha512_copy(ssh_hash *hashold) +typedef struct sha512_neon { + sha512_neon_core core; + sha512_block blk; + BinarySink_IMPLEMENTATION; + ssh_hash hash; +} sha512_neon; + +static void sha512_neon_write(BinarySink *bs, const void *vp, size_t len); + +static ssh_hash *sha512_neon_new(const ssh_hashalg *alg) { - struct sha512_hash *hold, *hnew; - ssh_hash *hashnew = sha512_new(hashold->vt); + if (!sha512_hw_available_cached()) + return NULL; + + sha512_neon *s = snew(sha512_neon); - hold = container_of(hashold, struct sha512_hash, hash); - hnew = container_of(hashnew, struct sha512_hash, hash); + s->hash.vt = alg; + BinarySink_INIT(s, sha512_neon_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; +} + +static void sha512_neon_reset(ssh_hash *hash) +{ + sha512_neon *s = container_of(hash, sha512_neon, hash); + const uint64_t *iv = (const uint64_t *)hash->vt->extra; - hnew->state = hold->state; - BinarySink_COPIED(&hnew->state); + s->core.ab = vld1q_u64(iv); + s->core.cd = vld1q_u64(iv+2); + s->core.ef = vld1q_u64(iv+4); + s->core.gh = vld1q_u64(iv+6); - return hashnew; + sha512_block_setup(&s->blk); } -static void sha512_free(ssh_hash *hash) +static void sha512_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { - struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); + sha512_neon *copy = container_of(hcopy, sha512_neon, hash); + sha512_neon *orig = container_of(horig, sha512_neon, hash); - smemclr(h, sizeof(*h)); - sfree(h); + *copy = *orig; /* structure copy */ + + BinarySink_COPIED(copy); + BinarySink_DELEGATE_INIT(©->hash, copy); } -static void sha512_final(ssh_hash *hash, unsigned char *output) +static void sha512_neon_free(ssh_hash *hash) { - struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); - SHA512_Final(&h->state, output); - sha512_free(hash); + sha512_neon *s = container_of(hash, sha512_neon, hash); + smemclr(s, sizeof(*s)); + sfree(s); } -const ssh_hashalg ssh_sha512 = { - sha512_new, sha512_copy, sha512_final, sha512_free, - 64, BLKSIZE, HASHALG_NAMES_BARE("SHA-512"), +static void sha512_neon_write(BinarySink *bs, const void *vp, size_t len) +{ + sha512_neon *s = BinarySink_DOWNCAST(bs, sha512_neon); + + while (len > 0) + if (sha512_block_write(&s->blk, &vp, &len)) + sha512_neon_block(&s->core, s->blk.block); +} + +static void sha512_neon_digest(ssh_hash *hash, uint8_t *digest) +{ + sha512_neon *s = container_of(hash, sha512_neon, hash); + + sha512_block_pad(&s->blk, BinarySink_UPCAST(s)); + + vst1q_u8(digest, vrev64q_u8(vreinterpretq_u8_u64(s->core.ab))); + vst1q_u8(digest+16, vrev64q_u8(vreinterpretq_u8_u64(s->core.cd))); + vst1q_u8(digest+32, vrev64q_u8(vreinterpretq_u8_u64(s->core.ef))); + vst1q_u8(digest+48, vrev64q_u8(vreinterpretq_u8_u64(s->core.gh))); +} + +static void sha384_neon_digest(ssh_hash *hash, uint8_t *digest) +{ + sha512_neon *s = container_of(hash, sha512_neon, hash); + + sha512_block_pad(&s->blk, BinarySink_UPCAST(s)); + + vst1q_u8(digest, vrev64q_u8(vreinterpretq_u8_u64(s->core.ab))); + vst1q_u8(digest+16, vrev64q_u8(vreinterpretq_u8_u64(s->core.cd))); + vst1q_u8(digest+32, vrev64q_u8(vreinterpretq_u8_u64(s->core.ef))); +} + +const ssh_hashalg ssh_sha512_hw = { + .new = sha512_neon_new, + .reset = sha512_neon_reset, + .copyfrom = sha512_neon_copyfrom, + .digest = sha512_neon_digest, + .free = sha512_neon_free, + .hlen = 64, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-512", "NEON accelerated"), + .extra = sha512_initial_state, }; -static ssh_hash *sha384_new(const ssh_hashalg *alg) +const ssh_hashalg ssh_sha384_hw = { + .new = sha512_neon_new, + .reset = sha512_neon_reset, + .copyfrom = sha512_neon_copyfrom, + .digest = sha384_neon_digest, + .free = sha512_neon_free, + .hlen = 48, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-384", "NEON accelerated"), + .extra = sha384_initial_state, +}; + +/* ---------------------------------------------------------------------- + * Stub functions if we have no hardware-accelerated SHA-512. In this + * case, sha512_hw_new returns NULL (though it should also never be + * selected by sha512_select, so the only thing that should even be + * _able_ to call it is testcrypt). As a result, the remaining vtable + * functions should never be called at all. + */ + +#elif HW_SHA512 == HW_SHA512_NONE + +static bool sha512_hw_available(void) { - struct sha512_hash *h = snew(struct sha512_hash); - SHA384_Init(&h->state); - h->hash.vt = alg; - BinarySink_DELEGATE_INIT(&h->hash, &h->state); - return &h->hash; + return false; } -static void sha384_final(ssh_hash *hash, unsigned char *output) +static ssh_hash *sha512_stub_new(const ssh_hashalg *alg) { - struct sha512_hash *h = container_of(hash, struct sha512_hash, hash); - SHA384_Final(&h->state, output); - sha512_free(hash); + return NULL; } -const ssh_hashalg ssh_sha384 = { - sha384_new, sha512_copy, sha384_final, sha512_free, - 48, BLKSIZE, HASHALG_NAMES_BARE("SHA-384"), +#define STUB_BODY { unreachable("Should never be called"); } + +static void sha512_stub_reset(ssh_hash *hash) STUB_BODY +static void sha512_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY +static void sha512_stub_free(ssh_hash *hash) STUB_BODY +static void sha512_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY + +const ssh_hashalg ssh_sha512_hw = { + .new = sha512_stub_new, + .reset = sha512_stub_reset, + .copyfrom = sha512_stub_copyfrom, + .digest = sha512_stub_digest, + .free = sha512_stub_free, + .hlen = 64, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-512", "!NONEXISTENT ACCELERATED VERSION!"), }; + +const ssh_hashalg ssh_sha384_hw = { + .new = sha512_stub_new, + .reset = sha512_stub_reset, + .copyfrom = sha512_stub_copyfrom, + .digest = sha512_stub_digest, + .free = sha512_stub_free, + .hlen = 48, + .blocklen = 128, + HASHALG_NAMES_ANNOTATED("SHA-384", "!NONEXISTENT ACCELERATED VERSION!"), +}; + +#endif /* HW_SHA512 */ diff --git a/sshsha.c b/sshsha.c index 0b8b58f..a5e79e6 100644 --- a/sshsha.c +++ b/sshsha.c @@ -98,8 +98,10 @@ static ssh_hash *sha1_select(const ssh_hashalg *alg) } const ssh_hashalg ssh_sha1 = { - sha1_select, NULL, NULL, NULL, - 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "dummy selector vtable"), + .new = sha1_select, + .hlen = 20, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-1", "dummy selector vtable"), }; /* ---------------------------------------------------------------------- @@ -259,26 +261,28 @@ static ssh_hash *sha1_sw_new(const ssh_hashalg *alg) { sha1_sw *s = snew(sha1_sw); - memcpy(s->core, sha1_initial_state, sizeof(s->core)); - - sha1_block_setup(&s->blk); - s->hash.vt = alg; BinarySink_INIT(s, sha1_sw_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } -static ssh_hash *sha1_sw_copy(ssh_hash *hash) +static void sha1_sw_reset(ssh_hash *hash) { sha1_sw *s = container_of(hash, sha1_sw, hash); - sha1_sw *copy = snew(sha1_sw); - memcpy(copy, s, sizeof(*copy)); + memcpy(s->core, sha1_initial_state, sizeof(s->core)); + sha1_block_setup(&s->blk); +} + +static void sha1_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + sha1_sw *copy = container_of(hcopy, sha1_sw, hash); + sha1_sw *orig = container_of(horig, sha1_sw, hash); + + memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); - - return ©->hash; } static void sha1_sw_free(ssh_hash *hash) @@ -298,19 +302,24 @@ static void sha1_sw_write(BinarySink *bs, const void *vp, size_t len) sha1_sw_block(s->core, s->blk.block); } -static void sha1_sw_final(ssh_hash *hash, uint8_t *digest) +static void sha1_sw_digest(ssh_hash *hash, uint8_t *digest) { sha1_sw *s = container_of(hash, sha1_sw, hash); sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); for (size_t i = 0; i < 5; i++) PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]); - sha1_sw_free(hash); } const ssh_hashalg ssh_sha1_sw = { - sha1_sw_new, sha1_sw_copy, sha1_sw_final, sha1_sw_free, - 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "unaccelerated"), + .new = sha1_sw_new, + .reset = sha1_sw_reset, + .copyfrom = sha1_sw_copyfrom, + .digest = sha1_sw_digest, + .free = sha1_sw_free, + .hlen = 20, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-1", "unaccelerated"), }; /* ---------------------------------------------------------------------- @@ -573,39 +582,42 @@ static sha1_ni *sha1_ni_alloc(void) return s; } -FUNC_ISA static ssh_hash *sha1_ni_new(const ssh_hashalg *alg) +static ssh_hash *sha1_ni_new(const ssh_hashalg *alg) { if (!sha1_hw_available_cached()) return NULL; sha1_ni *s = sha1_ni_alloc(); + s->hash.vt = alg; + BinarySink_INIT(s, sha1_ni_write); + BinarySink_DELEGATE_INIT(&s->hash, s); + return &s->hash; +} + +FUNC_ISA static void sha1_ni_reset(ssh_hash *hash) +{ + sha1_ni *s = container_of(hash, sha1_ni, hash); + /* Initialise the core vectors in their storage order */ s->core[0] = _mm_set_epi64x( 0x67452301efcdab89ULL, 0x98badcfe10325476ULL); s->core[1] = _mm_set_epi32(0xc3d2e1f0, 0, 0, 0); sha1_block_setup(&s->blk); - - s->hash.vt = alg; - BinarySink_INIT(s, sha1_ni_write); - BinarySink_DELEGATE_INIT(&s->hash, s); - return &s->hash; } -static ssh_hash *sha1_ni_copy(ssh_hash *hash) +static void sha1_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { - sha1_ni *s = container_of(hash, sha1_ni, hash); - sha1_ni *copy = sha1_ni_alloc(); + sha1_ni *copy = container_of(hcopy, sha1_ni, hash); + sha1_ni *orig = container_of(horig, sha1_ni, hash); void *ptf_save = copy->pointer_to_free; - *copy = *s; /* structure copy */ + *copy = *orig; /* structure copy */ copy->pointer_to_free = ptf_save; BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); - - return ©->hash; } static void sha1_ni_free(ssh_hash *hash) @@ -626,7 +638,7 @@ static void sha1_ni_write(BinarySink *bs, const void *vp, size_t len) sha1_ni_block(s->core, s->blk.block); } -FUNC_ISA static void sha1_ni_final(ssh_hash *hash, uint8_t *digest) +FUNC_ISA static void sha1_ni_digest(ssh_hash *hash, uint8_t *digest) { sha1_ni *s = container_of(hash, sha1_ni, hash); @@ -645,13 +657,17 @@ FUNC_ISA static void sha1_ni_final(ssh_hash *hash, uint8_t *digest) /* Finally, store the leftover word */ uint32_t e = _mm_extract_epi32(s->core[1], 3); PUT_32BIT_MSB_FIRST(digest + 16, e); - - sha1_ni_free(hash); } const ssh_hashalg ssh_sha1_hw = { - sha1_ni_new, sha1_ni_copy, sha1_ni_final, sha1_ni_free, - 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "SHA-NI accelerated"), + .new = sha1_ni_new, + .reset = sha1_ni_reset, + .copyfrom = sha1_ni_copyfrom, + .digest = sha1_ni_digest, + .free = sha1_ni_free, + .hlen = 20, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-1", "SHA-NI accelerated"), }; /* ---------------------------------------------------------------------- @@ -674,6 +690,7 @@ const ssh_hashalg ssh_sha1_hw = { */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 +#define __ARM_FEATURE_SHA2 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ @@ -813,28 +830,31 @@ static ssh_hash *sha1_neon_new(const ssh_hashalg *alg) sha1_neon *s = snew(sha1_neon); - s->core.abcd = vld1q_u32(sha1_initial_state); - s->core.e = sha1_initial_state[4]; - - sha1_block_setup(&s->blk); - s->hash.vt = alg; BinarySink_INIT(s, sha1_neon_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } -static ssh_hash *sha1_neon_copy(ssh_hash *hash) +static void sha1_neon_reset(ssh_hash *hash) { sha1_neon *s = container_of(hash, sha1_neon, hash); - sha1_neon *copy = snew(sha1_neon); - *copy = *s; /* structure copy */ + s->core.abcd = vld1q_u32(sha1_initial_state); + s->core.e = sha1_initial_state[4]; + + sha1_block_setup(&s->blk); +} + +static void sha1_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) +{ + sha1_neon *copy = container_of(hcopy, sha1_neon, hash); + sha1_neon *orig = container_of(horig, sha1_neon, hash); + + *copy = *orig; /* structure copy */ BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); - - return ©->hash; } static void sha1_neon_free(ssh_hash *hash) @@ -853,19 +873,24 @@ static void sha1_neon_write(BinarySink *bs, const void *vp, size_t len) sha1_neon_block(&s->core, s->blk.block); } -static void sha1_neon_final(ssh_hash *hash, uint8_t *digest) +static void sha1_neon_digest(ssh_hash *hash, uint8_t *digest) { sha1_neon *s = container_of(hash, sha1_neon, hash); sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd))); PUT_32BIT_MSB_FIRST(digest + 16, s->core.e); - sha1_neon_free(hash); } const ssh_hashalg ssh_sha1_hw = { - sha1_neon_new, sha1_neon_copy, sha1_neon_final, sha1_neon_free, - 20, 64, HASHALG_NAMES_ANNOTATED("SHA-1", "NEON accelerated"), + .new = sha1_neon_new, + .reset = sha1_neon_reset, + .copyfrom = sha1_neon_copyfrom, + .digest = sha1_neon_digest, + .free = sha1_neon_free, + .hlen = 20, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-1", "NEON accelerated"), }; /* ---------------------------------------------------------------------- @@ -890,14 +915,20 @@ static ssh_hash *sha1_stub_new(const ssh_hashalg *alg) #define STUB_BODY { unreachable("Should never be called"); } -static ssh_hash *sha1_stub_copy(ssh_hash *hash) STUB_BODY +static void sha1_stub_reset(ssh_hash *hash) STUB_BODY +static void sha1_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY static void sha1_stub_free(ssh_hash *hash) STUB_BODY -static void sha1_stub_final(ssh_hash *hash, uint8_t *digest) STUB_BODY +static void sha1_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY const ssh_hashalg ssh_sha1_hw = { - sha1_stub_new, sha1_stub_copy, sha1_stub_final, sha1_stub_free, - 20, 64, HASHALG_NAMES_ANNOTATED( - "SHA-1", "!NONEXISTENT ACCELERATED VERSION!"), + .new = sha1_stub_new, + .reset = sha1_stub_reset, + .copyfrom = sha1_stub_copyfrom, + .digest = sha1_stub_digest, + .free = sha1_stub_free, + .hlen = 20, + .blocklen = 64, + HASHALG_NAMES_ANNOTATED("SHA-1", "!NONEXISTENT ACCELERATED VERSION!"), }; #endif /* HW_SHA1 */ diff --git a/sshsha3.c b/sshsha3.c new file mode 100644 index 0000000..83d136b --- /dev/null +++ b/sshsha3.c @@ -0,0 +1,329 @@ +/* + * SHA-3, as defined in FIPS PUB 202. + */ + +#include +#include +#include "ssh.h" + +static inline uint64_t rol(uint64_t x, unsigned shift) +{ + unsigned L = (+shift) & 63; + unsigned R = (-shift) & 63; + return (x << L) | (x >> R); +} + +/* + * General Keccak is defined such that its state is a 5x5 array of + * words which can be any power-of-2 size from 1 up to 64. SHA-3 fixes + * on 64, and so do we. + * + * The number of rounds is defined as 12 + 2k if the word size is 2^k. + * Here we have 64-bit words only, so k=6, so 24 rounds always. + */ +typedef uint64_t keccak_core_state[5][5]; +#define NROUNDS 24 /* would differ for other word sizes */ +static const uint64_t round_constants[NROUNDS]; +static const unsigned rotation_counts[5][5]; + +/* + * Core Keccak transform: just squodge the state around internally, + * without adding or extracting any data from it. + */ +static void keccak_transform(keccak_core_state A) +{ + union { + uint64_t C[5]; + uint64_t B[5][5]; + } u; + + for (unsigned round = 0; round < NROUNDS; round++) { + /* theta step */ + for (unsigned x = 0; x < 5; x++) + u.C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4]; + for (unsigned x = 0; x < 5; x++) { + uint64_t D = rol(u.C[(x+1) % 5], 1) ^ u.C[(x+4) % 5]; + for (unsigned y = 0; y < 5; y++) + A[x][y] ^= D; + } + + /* rho and pi steps */ + for (unsigned x = 0; x < 5; x++) + for (unsigned y = 0; y < 5; y++) + u.B[y][(2*x+3*y) % 5] = rol(A[x][y], rotation_counts[x][y]); + + /* chi step */ + for (unsigned x = 0; x < 5; x++) + for (unsigned y = 0; y < 5; y++) + A[x][y] = u.B[x][y] ^ (u.B[(x+2)%5][y] & ~u.B[(x+1)%5][y]); + + /* iota step */ + A[0][0] ^= round_constants[round]; + } + + smemclr(&u, sizeof(u)); +} + +typedef struct { + keccak_core_state A; + unsigned char bytes[25*8]; + unsigned char first_pad_byte; + size_t bytes_got, bytes_wanted, hash_bytes; +} keccak_state; + +/* + * Keccak accumulation function: given a piece of message, add it to + * the hash. + */ +static void keccak_accumulate(keccak_state *s, const void *vdata, size_t len) +{ + const unsigned char *data = (const unsigned char *)vdata; + + while (len >= s->bytes_wanted - s->bytes_got) { + size_t b = s->bytes_wanted - s->bytes_got; + memcpy(s->bytes + s->bytes_got, data, b); + len -= b; + data += b; + + size_t n = 0; + for (unsigned y = 0; y < 5; y++) { + for (unsigned x = 0; x < 5; x++) { + if (n >= s->bytes_wanted) + break; + + s->A[x][y] ^= GET_64BIT_LSB_FIRST(s->bytes + n); + n += 8; + } + } + keccak_transform(s->A); + + s->bytes_got = 0; + } + + memcpy(s->bytes + s->bytes_got, data, len); + s->bytes_got += len; +} + +/* + * Keccak output function. + */ +static void keccak_output(keccak_state *s, void *voutput) +{ + unsigned char *output = (unsigned char *)voutput; + + /* + * Add message padding. + */ + { + unsigned char padding[25*8]; + size_t len = s->bytes_wanted - s->bytes_got; + if (len == 0) + len = s->bytes_wanted; + memset(padding, 0, len); + padding[0] |= s->first_pad_byte; + padding[len-1] |= 0x80; + keccak_accumulate(s, padding, len); + } + + size_t n = 0; + for (unsigned y = 0; y < 5; y++) { + for (unsigned x = 0; x < 5; x++) { + size_t to_copy = s->hash_bytes - n; + if (to_copy == 0) + break; + if (to_copy > 8) + to_copy = 8; + unsigned char outbytes[8]; + PUT_64BIT_LSB_FIRST(outbytes, s->A[x][y]); + memcpy(output + n, outbytes, to_copy); + n += to_copy; + } + } +} + +static void keccak_init(keccak_state *s, unsigned hashbits, unsigned ratebits, + unsigned char first_pad_byte) +{ + int x, y; + + assert(hashbits % 8 == 0); + assert(ratebits % 8 == 0); + + s->hash_bytes = hashbits / 8; + s->bytes_wanted = (25 * 64 - ratebits) / 8; + s->bytes_got = 0; + s->first_pad_byte = first_pad_byte; + + assert(s->bytes_wanted % 8 == 0); + + for (y = 0; y < 5; y++) + for (x = 0; x < 5; x++) + s->A[x][y] = 0; +} + +static void keccak_sha3_init(keccak_state *s, int hashbits) +{ + keccak_init(s, hashbits, hashbits * 2, 0x06); +} + +static void keccak_shake_init(keccak_state *s, int parambits, int hashbits) +{ + keccak_init(s, hashbits, parambits * 2, 0x1f); +} + +/* + * Keccak round constants, generated via the LFSR specified in the + * Keccak reference by the following piece of Python: + +import textwrap +from functools import reduce + +rbytes = [1] +while len(rbytes) < 7*24: + k = rbytes[-1] * 2 + rbytes.append(k ^ (0x171 * (k >> 8))) + +rbits = [byte & 1 for byte in rbytes] + +rwords = [sum(rbits[i+j] << ((1 << j) - 1) for j in range(7)) + for i in range(0, len(rbits), 7)] + +print(textwrap.indent("\n".join(textwrap.wrap(", ".join( + map("0x{:016x}".format, rwords)))), " "*4)) + +*/ + +static const uint64_t round_constants[24] = { + 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, + 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, + 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, + 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, + 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, + 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, + 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 +}; + +/* + * Keccak per-element rotation counts, generated from the matrix + * formula in the Keccak reference by the following piece of Python: + +coords = [1, 0] +while len(coords) < 26: + coords.append((2*coords[-2] + 3*coords[-1]) % 5) + +matrix = { (coords[i], coords[i+1]) : i for i in range(24) } +matrix[0,0] = -1 + +f = lambda t: (t+1) * (t+2) // 2 % 64 + +for y in range(5): + print(" {{{}}},".format(", ".join("{:2d}".format(f(matrix[y,x])) + for x in range(5)))) + +*/ +static const unsigned rotation_counts[5][5] = { + { 0, 36, 3, 41, 18}, + { 1, 44, 10, 45, 2}, + {62, 6, 43, 15, 61}, + {28, 55, 25, 21, 56}, + {27, 20, 39, 8, 14}, +}; + +/* + * The PuTTY ssh_hashalg abstraction. + */ +struct keccak_hash { + keccak_state state; + ssh_hash hash; + BinarySink_IMPLEMENTATION; +}; + +static void keccak_BinarySink_write(BinarySink *bs, const void *p, size_t len) +{ + struct keccak_hash *kh = BinarySink_DOWNCAST(bs, struct keccak_hash); + keccak_accumulate(&kh->state, p, len); +} + +static ssh_hash *keccak_new(const ssh_hashalg *alg) +{ + struct keccak_hash *kh = snew(struct keccak_hash); + kh->hash.vt = alg; + BinarySink_INIT(kh, keccak_BinarySink_write); + BinarySink_DELEGATE_INIT(&kh->hash, kh); + return ssh_hash_reset(&kh->hash); +} + +static void keccak_free(ssh_hash *hash) +{ + struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); + smemclr(kh, sizeof(*kh)); + sfree(kh); +} + +static void keccak_copyfrom(ssh_hash *hnew, ssh_hash *hold) +{ + struct keccak_hash *khold = container_of(hold, struct keccak_hash, hash); + struct keccak_hash *khnew = container_of(hnew, struct keccak_hash, hash); + khnew->state = khold->state; +} + +static void keccak_digest(ssh_hash *hash, unsigned char *output) +{ + struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); + keccak_output(&kh->state, output); +} + +static void sha3_reset(ssh_hash *hash) +{ + struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); + keccak_sha3_init(&kh->state, hash->vt->hlen * 8); +} + +#define DEFINE_SHA3(bits) \ + const ssh_hashalg ssh_sha3_##bits = { \ + .new = keccak_new, \ + .reset = sha3_reset, \ + .copyfrom = keccak_copyfrom, \ + .digest = keccak_digest, \ + .free = keccak_free, \ + .hlen = bits/8, \ + .blocklen = 200 - 2*(bits/8), \ + HASHALG_NAMES_BARE("SHA3-" #bits), \ + } + +DEFINE_SHA3(224); +DEFINE_SHA3(256); +DEFINE_SHA3(384); +DEFINE_SHA3(512); + +static void shake256_reset(ssh_hash *hash) +{ + struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); + keccak_shake_init(&kh->state, 256, hash->vt->hlen * 8); +} + +/* + * There is some confusion over the output length parameter for the + * SHAKE functions. By my reading, FIPS PUB 202 defines SHAKE256(M,d) + * to generate d _bits_ of output. But RFC 8032 (defining Ed448) talks + * about "SHAKE256(x,114)" in a context where it definitely means + * generating 114 _bytes_ of output. + * + * Our internal ID therefore suffixes the output length with "bytes", + * to be clear which we're talking about + */ + +#define DEFINE_SHAKE(param, hashbytes) \ + const ssh_hashalg ssh_shake##param##_##hashbytes##bytes = { \ + .new = keccak_new, \ + .reset = shake##param##_reset, \ + .copyfrom = keccak_copyfrom, \ + .digest = keccak_digest, \ + .free = keccak_free, \ + .hlen = hashbytes, \ + .blocklen = 0, \ + HASHALG_NAMES_BARE("SHAKE" #param), \ + } + +DEFINE_SHAKE(256, 114); diff --git a/sshshare.c b/sshshare.c index 4d12418..b5da865 100644 --- a/sshshare.c +++ b/sshshare.c @@ -705,8 +705,8 @@ static void share_remove_forwarding(struct ssh_sharing_connstate *cs, sfree(fwd); } -static void log_downstream(struct ssh_sharing_connstate *cs, - const char *logfmt, ...) +static PRINTF_LIKE(2, 3) void log_downstream(struct ssh_sharing_connstate *cs, + const char *logfmt, ...) { va_list ap; char *buf; @@ -719,8 +719,8 @@ static void log_downstream(struct ssh_sharing_connstate *cs, sfree(buf); } -static void log_general(struct ssh_sharing_state *sharestate, - const char *logfmt, ...) +static PRINTF_LIKE(2, 3) void log_general(struct ssh_sharing_state *sharestate, + const char *logfmt, ...) { va_list ap; char *buf; @@ -1794,8 +1794,9 @@ static void share_receive(Plug *plug, int urgent, const char *data, size_t len) } if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015') cs->recvlen--; /* trim off \r before \n */ + ptrlen verstring = make_ptrlen(cs->recvbuf, cs->recvlen); log_downstream(cs, "Downstream version string: %.*s", - cs->recvlen, cs->recvbuf); + PTRLEN_PRINTF(verstring)); cs->got_verstring = true; /* @@ -1858,7 +1859,7 @@ static void share_listen_closing(Plug *plug, const char *error_msg, static void share_send_verstring(ssh_sharing_connstate *cs) { char *fullstring = dupcat("SSHCONNECTION@putty.projects.tartarus.org-2.0-", - cs->parent->server_verstring, "\015\012", NULL); + cs->parent->server_verstring, "\015\012"); sk_write(cs->sock, fullstring, strlen(fullstring)); sfree(fullstring); @@ -1901,11 +1902,9 @@ void share_activate(ssh_sharing_state *sharestate, } static const PlugVtable ssh_sharing_conn_plugvt = { - NULL, /* no log function, because that's for outgoing connections */ - share_closing, - share_receive, - share_sent, - NULL /* no accepting function, because we've already done it */ + .closing = share_closing, + .receive = share_receive, + .sent = share_sent, }; static int share_listen_accepting(Plug *plug, @@ -1939,7 +1938,7 @@ static int share_listen_accepting(Plug *plug, return err != NULL; } - sk_set_frozen(cs->sock, 0); + sk_set_frozen(cs->sock, false); add234(cs->parent->connections, cs); @@ -1992,9 +1991,14 @@ static int share_listen_accepting(Plug *plug, */ char *ssh_share_sockname(const char *host, int port, Conf *conf) { - char *username = get_remote_username(conf); + char *username = NULL; char *sockname; + /* Include the username we're logging in as in the hash, unless + * we're using a protocol for which it's completely irrelevant. */ + if (conf_get_int(conf, CONF_protocol) != PROT_SSHCONN) + username = get_remote_username(conf); + if (port == 22) { if (username) sockname = dupprintf("%s@%s", username, host); @@ -2040,11 +2044,8 @@ bool ssh_share_test_for_upstream(const char *host, int port, Conf *conf) } static const PlugVtable ssh_sharing_listen_plugvt = { - NULL, /* no log function, because that's for outgoing connections */ - share_listen_closing, - NULL, /* no receive function on a listening socket */ - NULL, /* no sent function on a listening socket */ - share_listen_accepting + .closing = share_listen_closing, + .accepting = share_listen_accepting, }; void ssh_connshare_provide_connlayer(ssh_sharing_state *sharestate, diff --git a/sshutils.c b/sshutils.c new file mode 100644 index 0000000..1ee3342 --- /dev/null +++ b/sshutils.c @@ -0,0 +1,128 @@ +/* + * Supporting routines used in common by all the various components of + * the SSH system. + */ + +#include +#include + +#include "putty.h" +#include "ssh.h" +#include "sshchan.h" + +/* ---------------------------------------------------------------------- + * Centralised standard methods for other channel implementations to + * borrow. + */ + +void chan_remotely_opened_confirmation(Channel *chan) +{ + unreachable("this channel type should never receive OPEN_CONFIRMATION"); +} + +void chan_remotely_opened_failure(Channel *chan, const char *errtext) +{ + unreachable("this channel type should never receive OPEN_FAILURE"); +} + +bool chan_default_want_close( + Channel *chan, bool sent_local_eof, bool rcvd_remote_eof) +{ + /* + * Default close policy: we start initiating the CHANNEL_CLOSE + * procedure as soon as both sides of the channel have seen EOF. + */ + return sent_local_eof && rcvd_remote_eof; +} + +bool chan_no_exit_status(Channel *chan, int status) +{ + return false; +} + +bool chan_no_exit_signal( + Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg) +{ + return false; +} + +bool chan_no_exit_signal_numeric( + Channel *chan, int signum, bool core_dumped, ptrlen msg) +{ + return false; +} + +bool chan_no_run_shell(Channel *chan) +{ + return false; +} + +bool chan_no_run_command(Channel *chan, ptrlen command) +{ + return false; +} + +bool chan_no_run_subsystem(Channel *chan, ptrlen subsys) +{ + return false; +} + +bool chan_no_enable_x11_forwarding( + Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, + unsigned screen_number) +{ + return false; +} + +bool chan_no_enable_agent_forwarding(Channel *chan) +{ + return false; +} + +bool chan_no_allocate_pty( + Channel *chan, ptrlen termtype, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) +{ + return false; +} + +bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value) +{ + return false; +} + +bool chan_no_send_break(Channel *chan, unsigned length) +{ + return false; +} + +bool chan_no_send_signal(Channel *chan, ptrlen signame) +{ + return false; +} + +bool chan_no_change_window_size( + Channel *chan, unsigned width, unsigned height, + unsigned pixwidth, unsigned pixheight) +{ + return false; +} + +void chan_no_request_response(Channel *chan, bool success) +{ + unreachable("this channel type should never send a want-reply request"); +} + +/* ---------------------------------------------------------------------- + * Other miscellaneous utility functions. + */ + +void free_rportfwd(struct ssh_rportfwd *rpf) +{ + if (rpf) { + sfree(rpf->log_description); + sfree(rpf->shost); + sfree(rpf->dhost); + sfree(rpf); + } +} diff --git a/sshverstring.c b/sshverstring.c index 4828c37..8951e4c 100644 --- a/sshverstring.c +++ b/sshverstring.c @@ -45,13 +45,13 @@ static PktOut *ssh_verstring_new_pktout(int type); static void ssh_verstring_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category); -static const struct BinaryPacketProtocolVtable ssh_verstring_vtable = { - ssh_verstring_free, - ssh_verstring_handle_input, - ssh_verstring_handle_output, - ssh_verstring_new_pktout, - ssh_verstring_queue_disconnect, - 0xFFFFFFFF, /* no special packet size limit for this bpp */ +static const BinaryPacketProtocolVtable ssh_verstring_vtable = { + .free = ssh_verstring_free, + .handle_input = ssh_verstring_handle_input, + .handle_output = ssh_verstring_handle_output, + .new_pktout = ssh_verstring_new_pktout, + .queue_disconnect = ssh_verstring_queue_disconnect, + .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ }; static void ssh_detect_bugs(struct ssh_verstring_state *s); @@ -303,8 +303,7 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp) while (s->vstring->len > 0 && (s->vstring->s[s->vstring->len-1] == '\r' || s->vstring->s[s->vstring->len-1] == '\n')) - s->vstring->len--; - s->vstring->s[s->vstring->len] = '\0'; + strbuf_shrink_by(s->vstring, 1); bpp_logevent("Remote version: %s", s->vstring->s); diff --git a/sshzlib.c b/sshzlib.c index af559d6..9ad04ed 100644 --- a/sshzlib.c +++ b/sshzlib.c @@ -730,7 +730,7 @@ static struct zlib_table *zlib_mkonetab(int *codes, unsigned char *lengths, int pfxmask = (1 << pfxbits) - 1; int nbits, i, j, code; - tab->table = snewn(1 << bits, struct zlib_tableentry); + tab->table = snewn((size_t)1 << bits, struct zlib_tableentry); tab->mask = (1 << bits) - 1; for (code = 0; code <= tab->mask; code++) { @@ -1241,13 +1241,13 @@ bool zlib_decompress_block(ssh_decompressor *dc, } const ssh_compression_alg ssh_zlib = { - "zlib", - "zlib@openssh.com", /* delayed version */ - zlib_compress_init, - zlib_compress_cleanup, - zlib_compress_block, - zlib_decompress_init, - zlib_decompress_cleanup, - zlib_decompress_block, - "zlib (RFC1950)" + .name = "zlib", + .delayed_name = "zlib@openssh.com", /* delayed version */ + .compress_new = zlib_compress_init, + .compress_free = zlib_compress_cleanup, + .compress = zlib_compress_block, + .decompress_new = zlib_decompress_init, + .decompress_free = zlib_decompress_cleanup, + .decompress = zlib_decompress_block, + .text_name = "zlib (RFC1950)", }; diff --git a/supdup.c b/supdup.c new file mode 100644 index 0000000..f210ebe --- /dev/null +++ b/supdup.c @@ -0,0 +1,923 @@ +/* +* Supdup backend +*/ + +#include +#include +#include + +#include "putty.h" + +/* + * TTYOPT FUNCTION BITS (36-bit bitmasks) +*/ +#define TOALT 0200000000000LL // Characters 0175 and 0176 are converted to altmode (0033) on input +#define TOCLC 0100000000000LL // (user option bit) Convert lower-case input to upper-case +#define TOERS 0040000000000LL // Selective erase is supported +#define TOMVB 0010000000000LL // Backspacing is supported +#define TOSAI 0004000000000LL // Stanford/ITS extended ASCII graphics character set is supported +#define TOSA1 0002000000000LL // (user option bit) Characters 0001-0037 displayed using Stanford/ITS chars +#define TOOVR 0001000000000LL // Overprinting is supported +#define TOMVU 0000400000000LL // Moving cursor upwards is supported +#define TOMOR 0000200000000LL // (user option bit) System should provide **MORE** processing +#define TOROL 0000100000000LL // (user option bit) Terminal should scroll instead of wrapping +#define TOLWR 0000020000000LL // Lowercase characters are supported +#define TOFCI 0000010000000LL // Terminal can generate CONTROL and META characters +#define TOLID 0000002000000LL // Line insert/delete operations supported +#define TOCID 0000001000000LL // Character insert/delete operations supported +#define TPCBS 0000000000040LL // Terminal is using the "intelligent terminal protocol" (must be on) +#define TPORS 0000000000010LL // Server should process output resets + +// Initialization words (36-bit constants) +#define WORDS 0777773000000 // Negative number of config words to send (6) in high 18 bits +#define TCTYP 0000000000007 // Defines the terminal type (MUST be 7) +#define TTYROL 0000000000001 // Scroll amount for terminal (1 line at a time) + + +// %TD opcodes +// +#define TDMOV 0200 // Cursor positioning +#define TDMV1 0201 // Internal cursor positioning +#define TDEOF 0202 // Erase to end of screen +#define TDEOL 0203 // Erase to end of line +#define TDDLF 0204 // Clear the character the cursor is on +#define TDCRL 0207 // Carriage return +#define TDNOP 0210 // No-op; should be ignored. +#define TDBS 0211 // Backspace (not in official SUPDUP spec) +#define TDLF 0212 // Linefeed (not in official SUPDUP spec) +#define TDCR 0213 // Carriage Return (ditto) +#define TDORS 0214 // Output reset +#define TDQOT 0215 // Quotes the following character +#define TDFS 0216 // Non-destructive forward space +#define TDMV0 0217 // General cursor positioning code +#define TDCLR 0220 // Erase the screen, home cursor +#define TDBEL 0221 // Generate an audio tone, bell, whatever +#define TDILP 0223 // Insert blank lines at the cursor +#define TDDLP 0224 // Delete lines at the cursor +#define TDICP 0225 // Insert blanks at cursor +#define TDDCP 0226 // Delete characters at cursor +#define TDBOW 0227 // Display black chars on white screen +#define TDRST 0230 // Reset %TDBOW + +/* Maximum number of octets following a %TD code. */ +#define TD_ARGS_MAX 4 + +typedef struct supdup_tag Supdup; +struct supdup_tag +{ + Socket *s; + bool closed_on_socket_error; + + Seat *seat; + LogContext *logctx; + int term_width, term_height; + + long long ttyopt; + long tcmxv; + long tcmxh; + + bool sent_location; + + Conf *conf; + + int bufsize; + + enum { + CONNECTING, // waiting for %TDNOP from server after sending connection params + CONNECTED // %TDNOP received, connected. + } state; + + enum { + TD_TOPLEVEL, + TD_ARGS, + TD_ARGSDONE + } tdstate; + + int td_code; + int td_argcount; + char td_args[TD_ARGS_MAX]; + int td_argindex; + + void (*print) (strbuf *outbuf, int c); + + Pinger *pinger; + + Plug plug; + Backend backend; +}; + +#define SUPDUP_MAX_BACKLOG 4096 + +static void c_write(Supdup *supdup, unsigned char *buf, int len) +{ + size_t backlog = seat_stdout(supdup->seat, buf, len); + sk_set_frozen(supdup->s, backlog > SUPDUP_MAX_BACKLOG); +} + +static void supdup_send_location(Supdup *supdup) +{ + char locHeader[] = { 0300, 0302 }; + char* locString = conf_get_str(supdup->conf, CONF_supdup_location); + + sk_write(supdup->s, locHeader, sizeof(locHeader)); + sk_write(supdup->s, locString, strlen(locString) + 1); // include NULL terminator +} + +static void print_ascii(strbuf *outbuf, int c) +{ + /* In ASCII mode, ignore control characters. The server shouldn't + send them. */ + if (c >= 040 && c < 0177) + put_byte (outbuf, c); +} + +static void print_its(strbuf *outbuf, int c) +{ + /* The ITS character set is documented in RFC 734. */ + static const char *map[] = { + "\xc2\xb7", "\342\206\223", "\316\261", "\316\262", + "\342\210\247", "\302\254", "\316\265", "\317\200", + "\316\273", "\xce\xb3", "\xce\xb4", "\xe2\x86\x91", + "\xc2\xb1", "\xe2\x8a\x95", "\342\210\236", "\342\210\202", + "\342\212\202", "\342\212\203", "\342\210\251", "\342\210\252", + "\342\210\200", "\342\210\203", "\xe2\x8a\x97", "\342\206\224", + "\xe2\x86\x90", "\342\206\222", "\xe2\x89\xa0", "\xe2\x97\x8a", + "\342\211\244", "\342\211\245", "\342\211\241", "\342\210\250", + " ", "!", "\"", "#", "$", "%", "&", "'", + "(", ")", "*", "+", ",", "-", ".", "/", + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", ":", ";", "<", "=", ">", "?", + "@", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "[", "\\", "]", "^", "_", + "`", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "{", "|", "}", "~", "\xe2\x88\xab" + }; + + put_data (outbuf, map[c], strlen(map[c])); +} + +static void print_waits(strbuf *outbuf, int c) +{ + /* The WAITS character set used at the Stanford AI Lab is documented + here: https://www.saildart.org/allow/sail-charset-utf8.html */ + static const char *map[] = { + "", "\342\206\223", "\316\261", "\316\262", + "\342\210\247", "\302\254", "\316\265", "\317\200", + "\316\273", "", "", "", + "", "", "\342\210\236", "\342\210\202", + "\342\212\202", "\342\212\203", "\342\210\251", "\342\210\252", + "\342\210\200", "\342\210\203", "\xe2\x8a\x97", "\342\206\224", + "_", "\342\206\222", "~", "\xe2\x89\xa0", + "\342\211\244", "\342\211\245", "\342\211\241", "\342\210\250", + " ", "!", "\"", "#", "$", "%", "&", "'", + "(", ")", "*", "+", ",", "-", ".", "/", + "0", "1", "2", "3", "4", "5", "6", "7", + "8", "9", ":", ";", "<", "=", ">", "?", + "@", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", + "X", "Y", "Z", "[", "\\", "]", "\xe2\x86\x91", "\xe2\x86\x90", + "`", "a", "b", "c", "d", "e", "f", "g", + "h", "i", "j", "k", "l", "m", "n", "o", + "p", "q", "r", "s", "t", "u", "v", "w", + "x", "y", "z", "{", "|", "\xe2\x97\x8a", "}", "" + }; + + put_data (outbuf, map[c], strlen(map[c])); +} + +static void do_toplevel(Supdup *supdup, strbuf *outbuf, int c) +{ + // Toplevel: Waiting for a %TD code or a printable character + if (c >= 0200) { + // Handle SUPDUP %TD codes (codes greater than or equal to 200) + supdup->td_argindex = 0; + supdup->td_code = c; + switch (c) { + case TDMOV: + // %TD codes using 4 arguments + supdup->td_argcount = 4; + supdup->tdstate = TD_ARGS; + break; + + case TDMV0: + case TDMV1: + // %TD codes using 2 arguments + supdup->td_argcount = 2; + supdup->tdstate = TD_ARGS; + break; + + case TDQOT: + case TDILP: + case TDDLP: + case TDICP: + case TDDCP: + // %TD codes using 1 argument + supdup->td_argcount = 1; + supdup->tdstate = TD_ARGS; + break; + + case TDEOF: + case TDEOL: + case TDDLF: + case TDCRL: + case TDNOP: + case TDORS: + case TDFS: + case TDCLR: + case TDBEL: + case TDBOW: + case TDRST: + case TDBS: + case TDCR: + case TDLF: + // %TD codes using 0 arguments + supdup->td_argcount = 0; + supdup->tdstate = TD_ARGSDONE; + break; + + default: + // Unhandled, ignore + break; + } + } else { + supdup->print(outbuf, c); + } +} + +static void do_args(Supdup *supdup, strbuf *outbuf, int c) +{ + // Collect up args for %TD code + if (supdup->td_argindex < TD_ARGS_MAX) { + supdup->td_args[supdup->td_argindex] = c; + supdup->td_argindex++; + + if (supdup->td_argcount == supdup->td_argindex) { + // No more args, %TD code is ready to go. + supdup->tdstate = TD_ARGSDONE; + } + } else { + // Should never hit this state, if we do we will just + // return to TOPLEVEL. + supdup->tdstate = TD_TOPLEVEL; + } +} + +static void do_argsdone(Supdup *supdup, strbuf *outbuf, int c) +{ + char buf[4]; + int x, y; + + // Arguments for %TD code have been collected; dispatch based + // on the %TD code we're handling. + switch (supdup->td_code) { + case TDMOV: + /* + General cursor position code. Followed by four bytes; + the first two are the "old" vertical and horizontal + positions and may be ignored. The next two are the new + vertical and horizontal positions. The cursor should be + moved to this position. + */ + + // We only care about the new position. + strbuf_catf(outbuf, "\033[%d;%dH", supdup->td_args[2]+1, supdup->td_args[3]+1); + break; + + case TDMV0: + case TDMV1: + /* + General cursor position code. Followed by two bytes; + the new vertical and horizontal positions. + */ + strbuf_catf(outbuf, "\033[%d;%dH", supdup->td_args[0]+1, supdup->td_args[1]+1); + break; + + case TDEOF: + /* + Erase to end of screen. This is an optional function + since many terminals do not support this. If the + terminal does not support this function, it should be + treated the same as %TDEOL. + + %TDEOF does an erase to end of line, then erases all + lines lower on the screen than the cursor. The cursor + does not move. + */ + strbuf_catf(outbuf, "\033[J"); + break; + + case TDEOL: + /* + Erase to end of line. This erases the character + position the cursor is at and all positions to the right + on the same line. The cursor does not move. + */ + strbuf_catf(outbuf, "\033[K"); + break; + + case TDDLF: + /* + Clear the character position the cursor is on. The + cursor does not move. + */ + strbuf_catf(outbuf, "\033[X"); + break; + + case TDCRL: + /* + If the cursor is not on the bottom line of the screen, + move cursor to the beginning of the next line and clear + that line. If the cursor is at the bottom line, scroll + up. + */ + strbuf_catf(outbuf, "\015\012"); + break; + + case TDNOP: + /* + No-op; should be ignored. + */ + break; + + case TDORS: + /* + Output reset. This code serves as a data mark for + aborting output much as IAC DM does in the ordinary + TELNET protocol. + */ + outbuf->len = 0; + if (!seat_get_cursor_position(supdup->seat, &x, &y)) + x = y = 0; + buf[0] = 034; + buf[1] = 020; + buf[2] = y; + buf[3] = x; + sk_write(supdup->s, buf, 4); + break; + + case TDQOT: + /* + Quotes the following character. This is used when + sending 8-bit codes which are not %TD codes, for + instance when loading programs into an intelligent + terminal. The following character should be passed + through intact to the terminal. + */ + + put_byte(outbuf, supdup->td_args[0]); + break; + + case TDFS: + /* + Non-destructive forward space. The cursor moves right + one position; this code will not be sent at the end of a + line. + */ + + strbuf_catf(outbuf, "\033[C"); + break; + + case TDCLR: + /* + Erase the screen. Home the cursor to the top left hand + corner of the screen. + */ + strbuf_catf(outbuf, "\033[2J\033[H"); + break; + + case TDBEL: + /* + Generate an audio tone, bell, whatever. + */ + + strbuf_catf(outbuf, "\007"); + break; + + case TDILP: + /* + Insert blank lines at the cursor; followed by a byte + containing a count of the number of blank lines to + insert. The cursor is unmoved. The line the cursor is + on and all lines below it move down; lines moved off the + bottom of the screen are lost. + */ + strbuf_catf(outbuf, "\033[%dL", supdup->td_args[0]); + break; + + case TDDLP: + /* + Delete lines at the cursor; followed by a count. The + cursor is unmoved. The first line deleted is the one + the cursor is on. Lines below those deleted move up. + Newly- created lines at the bottom of the screen are + blank. + */ + strbuf_catf(outbuf, "\033[%dM", supdup->td_args[0]); + break; + + case TDICP: + /* + Insert blank character positions at the cursor; followed + by a count. The cursor is unmoved. The character the + cursor is on and all characters to the right on the + current line move to the right; characters moved off the + end of the line are lost. + */ + strbuf_catf(outbuf, "\033[%d@", supdup->td_args[0]); + break; + + case TDDCP: + /* + Delete characters at the cursor; followed by a count. + The cursor is unmoved. The first character deleted is + the one the cursor is on. Newly-created characters at + the end of the line are blank. + */ + strbuf_catf(outbuf, "\033[%dP", supdup->td_args[0]); + break; + + case TDBOW: + case TDRST: + /* + Display black characters on white screen. + HIGHLY OPTIONAL. + */ + + // Since this is HIGHLY OPTIONAL, I'm not going + // to implement it yet. + break; + + /* + * Non-standard (whatever "standard" means here) SUPDUP + * commands. These are used (at the very least) by + * Genera's SUPDUP implementation. Cannot find any + * official documentation, behavior is based on UNIX + * SUPDUP implementation from MIT. + */ + case TDBS: + /* + * Backspace -- move cursor back one character (does not + * appear to wrap...) + */ + put_byte(outbuf, '\010'); + break; + + case TDLF: + /* + * Linefeed -- move cursor down one line (again, no wrapping) + */ + put_byte(outbuf, '\012'); + break; + + case TDCR: + /* + * Carriage return -- move cursor to start of current line. + */ + put_byte(outbuf, '\015'); + break; + } + + // Return to top level to pick up the next %TD code or + // printable character. + supdup->tdstate = TD_TOPLEVEL; +} + +static void term_out_supdup(Supdup *supdup, strbuf *outbuf, int c) +{ + if (supdup->tdstate == TD_TOPLEVEL) { + do_toplevel (supdup, outbuf, c); + } else if (supdup->tdstate == TD_ARGS) { + do_args (supdup, outbuf, c); + } + + // If all arguments for a %TD code are ready, we will execute the code now. + if (supdup->tdstate == TD_ARGSDONE) { + do_argsdone (supdup, outbuf, c); + } +} + +static void do_supdup_read(Supdup *supdup, const char *buf, size_t len) +{ + strbuf *outbuf = strbuf_new(); + + while (len--) { + int c = (unsigned char)*buf++; + switch (supdup->state) { + case CONNECTING: + // "Following the transmission of the terminal options by + // the user, the server should respond with an ASCII + // greeting message, terminated with a %TDNOP code..." + if (TDNOP == c) { + // Greeting done, switch to the CONNECTED state. + supdup->state = CONNECTED; + supdup->tdstate = TD_TOPLEVEL; + } else { + // Forward the greeting message (which is straight + // ASCII, no controls) on so it gets displayed TODO: + // filter out only printable chars? + put_byte(outbuf, c); + } + break; + + case CONNECTED: + // "All transmissions from the server after the %TDNOP + // [see above] are either printing characters or virtual + // terminal display codes." Forward these on to the + // frontend which will decide what to do with them. + term_out_supdup(supdup, outbuf, c); + /* + * Hack to make Symbolics Genera SUPDUP happy: Wait until + * after we're connected (finished the initial handshake + * and have gotten additional data) before sending the + * location string. For some reason doing so earlier + * causes the Symbolics SUPDUP to end up in an odd state. + */ + if (!supdup->sent_location) { + supdup_send_location(supdup); + supdup->sent_location = true; + } + break; + } + + if (outbuf->len >= 4096) { + c_write(supdup, outbuf->u, outbuf->len); + outbuf->len = 0; + } + } + + if (outbuf->len) + c_write(supdup, outbuf->u, outbuf->len); + strbuf_free(outbuf); +} + +static void supdup_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, + const char *error_msg, int error_code) +{ + Supdup *supdup = container_of(plug, Supdup, plug); + backend_socket_log(supdup->seat, supdup->logctx, type, addr, port, + error_msg, error_code, + supdup->conf, supdup->state != CONNECTING); +} + +static void supdup_closing(Plug *plug, const char *error_msg, int error_code, + bool calling_back) +{ + Supdup *supdup = container_of(plug, Supdup, plug); + + /* + * We don't implement independent EOF in each direction for Telnet + * connections; as soon as we get word that the remote side has + * sent us EOF, we wind up the whole connection. + */ + + if (supdup->s) { + sk_close(supdup->s); + supdup->s = NULL; + if (error_msg) + supdup->closed_on_socket_error = true; + seat_notify_remote_exit(supdup->seat); + } + if (error_msg) { + logevent(supdup->logctx, error_msg); + seat_connection_fatal(supdup->seat, "%s", error_msg); + } + /* Otherwise, the remote side closed the connection normally. */ +} + +static void supdup_receive(Plug *plug, int urgent, const char *data, size_t len) +{ + Supdup *supdup = container_of(plug, Supdup, plug); + do_supdup_read(supdup, data, len); +} + +static void supdup_sent(Plug *plug, size_t bufsize) +{ + Supdup *supdup = container_of(plug, Supdup, plug); + supdup->bufsize = bufsize; +} + +static void supdup_send_36bits(Supdup *supdup, unsigned long long thirtysix) +{ + // + // From RFC734: + // "Each word is sent through the 8-bit connection as six + // 6-bit bytes, most-significant first." + // + // Split the 36-bit word into 6 6-bit "bytes", packed into + // 8-bit bytes and send, most-significant byte first. + // + for (int i = 5; i >= 0; i--) { + char sixBits = (thirtysix >> (i * 6)) & 077; + sk_write(supdup->s, &sixBits, 1); + } +} + +static void supdup_send_config(Supdup *supdup) +{ + supdup_send_36bits(supdup, WORDS); // negative length + supdup_send_36bits(supdup, TCTYP); // terminal type + supdup_send_36bits(supdup, supdup->ttyopt); // options + supdup_send_36bits(supdup, supdup->tcmxv); // height + supdup_send_36bits(supdup, supdup->tcmxh); // width + supdup_send_36bits(supdup, TTYROL); // scroll amount +} + +/* +* Called to set up the Supdup connection. +* +* Returns an error message, or NULL on success. +* +* Also places the canonical host name into `realhost'. It must be +* freed by the caller. +*/ +static char *supdup_init(const BackendVtable *x, Seat *seat, + Backend **backend_handle, + LogContext *logctx, Conf *conf, + const char *host, int port, char **realhost, + bool nodelay, bool keepalive) +{ + static const PlugVtable fn_table = { + .log = supdup_log, + .closing = supdup_closing, + .receive = supdup_receive, + .sent = supdup_sent, + }; + SockAddr *addr; + const char *err; + Supdup *supdup; + char *loghost; + int addressfamily; + const char *utf8 = "\033%G"; + + supdup = snew(struct supdup_tag); + supdup->plug.vt = &fn_table; + supdup->backend.vt = &supdup_backend; + supdup->logctx = logctx; + supdup->conf = conf_copy(conf); + supdup->s = NULL; + supdup->closed_on_socket_error = false; + supdup->seat = seat; + supdup->term_width = conf_get_int(supdup->conf, CONF_width); + supdup->term_height = conf_get_int(supdup->conf, CONF_height); + supdup->pinger = NULL; + supdup->sent_location = false; + *backend_handle = &supdup->backend; + + switch (conf_get_int(supdup->conf, CONF_supdup_ascii_set)) { + case SUPDUP_CHARSET_ASCII: + supdup->print = print_ascii; + break; + case SUPDUP_CHARSET_ITS: + supdup->print = print_its; + break; + case SUPDUP_CHARSET_WAITS: + supdup->print = print_waits; + break; + } + + /* + * Try to find host. + */ + { + char *buf; + addressfamily = conf_get_int(supdup->conf, CONF_addressfamily); + buf = dupprintf("Looking up host \"%s\"%s", host, + (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : + (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : + ""))); + logevent(supdup->logctx, buf); + sfree(buf); + } + addr = name_lookup(host, port, realhost, supdup->conf, addressfamily, NULL, ""); + if ((err = sk_addr_error(addr)) != NULL) { + sk_addr_free(addr); + return dupstr(err); + } + + if (port < 0) + port = 0137; /* default supdup port */ + + /* + * Open socket. + */ + supdup->s = new_connection(addr, *realhost, port, false, true, + nodelay, keepalive, &supdup->plug, supdup->conf); + if ((err = sk_socket_error(supdup->s)) != NULL) + return dupstr(err); + + supdup->pinger = pinger_new(supdup->conf, &supdup->backend); + + /* + * We can send special commands from the start. + */ + seat_update_specials_menu(supdup->seat); + + /* + * loghost overrides realhost, if specified. + */ + loghost = conf_get_str(supdup->conf, CONF_loghost); + if (*loghost) { + char *colon; + + sfree(*realhost); + *realhost = dupstr(loghost); + + colon = host_strrchr(*realhost, ':'); + if (colon) + *colon++ = '\0'; + } + + /* + * Set up TTYOPTS based on config + */ + int ascii_set = conf_get_int(supdup->conf, CONF_supdup_ascii_set); + int more_processing = conf_get_bool(supdup->conf, CONF_supdup_more); + int scrolling = conf_get_bool(supdup->conf, CONF_supdup_scroll); + supdup->ttyopt = + TOERS | + TOMVB | + (ascii_set == SUPDUP_CHARSET_ASCII ? 0 : TOSAI | TOSA1) | + TOMVU | + TOLWR | + TOLID | + TOCID | + TPCBS | + (scrolling ? TOROL : 0) | + (more_processing ? TOMOR : 0) | + TPORS; + + supdup->tcmxh = supdup->term_width - 1; // -1 "..one column is used to indicate line continuation." + supdup->tcmxv = supdup->term_height; + + /* + * Send our configuration words to the server + */ + supdup_send_config(supdup); + + /* + * We next expect a connection message followed by %TDNOP from the server + */ + supdup->state = CONNECTING; + seat_set_trust_status(supdup->seat, false); + + /* Make sure the terminal is in UTF-8 mode. */ + c_write(supdup, (unsigned char *)utf8, strlen(utf8)); + + return NULL; +} + + +static void supdup_free(Backend *be) +{ + Supdup *supdup = container_of(be, Supdup, backend); + + if (supdup->s) + sk_close(supdup->s); + if (supdup->pinger) + pinger_free(supdup->pinger); + conf_free(supdup->conf); + sfree(supdup); +} + +/* +* Reconfigure the Supdup backend. +*/ +static void supdup_reconfig(Backend *be, Conf *conf) +{ + /* Nothing to do; SUPDUP cannot be reconfigured while running. */ +} + +/* +* Called to send data down the Supdup connection. +*/ +static size_t supdup_send(Backend *be, const char *buf, size_t len) +{ + Supdup *supdup = container_of(be, Supdup, backend); + char c; + int i; + + if (supdup->s == NULL) + return 0; + + for (i = 0; i < len; i++) { + if (buf[i] == 034) + supdup->bufsize = sk_write(supdup->s, "\034\034", 2); + else { + c = buf[i] & 0177; + supdup->bufsize = sk_write(supdup->s, &c, 1); + } + } + return supdup->bufsize; +} + +/* +* Called to query the current socket sendability status. +*/ +static size_t supdup_sendbuffer(Backend *be) +{ + Supdup *supdup = container_of(be, Supdup, backend); + return supdup->bufsize; +} + +/* +* Called to set the size of the window from Supdup's POV. +*/ +static void supdup_size(Backend *be, int width, int height) +{ + Supdup *supdup = container_of(be, Supdup, backend); + + supdup->term_width = width; + supdup->term_height = height; + + // + // SUPDUP does not support resizing the terminal after connection + // establishment. + // +} + +/* +* Send Telnet special codes. +*/ +static void supdup_special(Backend *be, SessionSpecialCode code, int arg) +{ +} + +static const SessionSpecial *supdup_get_specials(Backend *be) +{ + return NULL; +} + +static bool supdup_connected(Backend *be) +{ + Supdup *supdup = container_of(be, Supdup, backend); + return supdup->s != NULL; +} + +static bool supdup_sendok(Backend *be) +{ + return 1; +} + +static void supdup_unthrottle(Backend *be, size_t backlog) +{ + Supdup *supdup = container_of(be, Supdup, backend); + sk_set_frozen(supdup->s, backlog > SUPDUP_MAX_BACKLOG); +} + +static bool supdup_ldisc(Backend *be, int option) +{ + /* No support for echoing or local editing. */ + return false; +} + +static void supdup_provide_ldisc(Backend *be, Ldisc *ldisc) +{ +} + +static int supdup_exitcode(Backend *be) +{ + Supdup *supdup = container_of(be, Supdup, backend); + if (supdup->s != NULL) + return -1; /* still connected */ + else if (supdup->closed_on_socket_error) + return INT_MAX; /* a socket error counts as an unclean exit */ + else + /* Supdup doesn't transmit exit codes back to the client */ + return 0; +} + +/* +* cfg_info for Dupdup does nothing at all. +*/ +static int supdup_cfg_info(Backend *be) +{ + return 0; +} + +const BackendVtable supdup_backend = { + .init = supdup_init, + .free = supdup_free, + .reconfig = supdup_reconfig, + .send = supdup_send, + .sendbuffer = supdup_sendbuffer, + .size = supdup_size, + .special = supdup_special, + .get_specials = supdup_get_specials, + .connected = supdup_connected, + .exitcode = supdup_exitcode, + .sendok = supdup_sendok, + .ldisc_option_state = supdup_ldisc, + .provide_ldisc = supdup_provide_ldisc, + .unthrottle = supdup_unthrottle, + .cfg_info = supdup_cfg_info, + .id = "supdup", + .displayname = "SUPDUP", + .protocol = PROT_SUPDUP, + .default_port = 0137, + .flags = BACKEND_RESIZE_FORBIDDEN | BACKEND_NEEDS_TERMINAL, +}; diff --git a/telnet.c b/telnet.c index 034de3a..3a60e64 100644 --- a/telnet.c +++ b/telnet.c @@ -580,7 +580,7 @@ static void do_telnet_read(Telnet *telnet, const char *buf, size_t len) break; case SEENSB: telnet->sb_opt = c; - telnet->sb_buf->len = 0; + strbuf_clear(telnet->sb_buf); telnet->state = SUBNEGOT; break; case SUBNEGOT: @@ -604,7 +604,7 @@ static void do_telnet_read(Telnet *telnet, const char *buf, size_t len) if (outbuf->len >= 4096) { c_write(telnet, outbuf->u, outbuf->len); - outbuf->len = 0; + strbuf_clear(outbuf); } } @@ -613,7 +613,7 @@ static void do_telnet_read(Telnet *telnet, const char *buf, size_t len) strbuf_free(outbuf); } -static void telnet_log(Plug *plug, int type, SockAddr *addr, int port, +static void telnet_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Telnet *telnet = container_of(plug, Telnet, plug); @@ -664,10 +664,10 @@ static void telnet_sent(Plug *plug, size_t bufsize) } static const PlugVtable Telnet_plugvt = { - telnet_log, - telnet_closing, - telnet_receive, - telnet_sent + .log = telnet_log, + .closing = telnet_closing, + .receive = telnet_receive, + .sent = telnet_sent, }; /* @@ -678,10 +678,10 @@ static const PlugVtable Telnet_plugvt = { * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *telnet_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, - char **realhost, bool nodelay, bool keepalive) +static char *telnet_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; @@ -694,7 +694,7 @@ static const char *telnet_init(Seat *seat, Backend **backend_handle, telnet = snew(Telnet); telnet->plug.vt = &Telnet_plugvt; - telnet->backend.vt = &telnet_backend; + telnet->backend.vt = vt; telnet->conf = conf_copy(conf); telnet->s = NULL; telnet->closed_on_socket_error = false; @@ -720,7 +720,7 @@ static const char *telnet_init(Seat *seat, Backend **backend_handle, telnet->logctx, "Telnet connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); - return err; + return dupstr(err); } if (port < 0) @@ -732,7 +732,7 @@ static const char *telnet_init(Seat *seat, Backend **backend_handle, telnet->s = new_connection(addr, *realhost, port, false, true, nodelay, keepalive, &telnet->plug, telnet->conf); if ((err = sk_socket_error(telnet->s)) != NULL) - return err; + return dupstr(err); telnet->pinger = pinger_new(telnet->conf, &telnet->backend); @@ -1047,24 +1047,24 @@ static int telnet_cfg_info(Backend *be) return 0; } -const struct BackendVtable telnet_backend = { - telnet_init, - telnet_free, - telnet_reconfig, - telnet_send, - telnet_sendbuffer, - telnet_size, - telnet_special, - telnet_get_specials, - telnet_connected, - telnet_exitcode, - telnet_sendok, - telnet_ldisc, - telnet_provide_ldisc, - telnet_unthrottle, - telnet_cfg_info, - NULL /* test_for_upstream */, - "telnet", - PROT_TELNET, - 23 +const BackendVtable telnet_backend = { + .init = telnet_init, + .free = telnet_free, + .reconfig = telnet_reconfig, + .send = telnet_send, + .sendbuffer = telnet_sendbuffer, + .size = telnet_size, + .special = telnet_special, + .get_specials = telnet_get_specials, + .connected = telnet_connected, + .exitcode = telnet_exitcode, + .sendok = telnet_sendok, + .ldisc_option_state = telnet_ldisc, + .provide_ldisc = telnet_provide_ldisc, + .unthrottle = telnet_unthrottle, + .cfg_info = telnet_cfg_info, + .id = "telnet", + .displayname = "Telnet", + .protocol = PROT_TELNET, + .default_port = 23, }; diff --git a/terminal.c b/terminal.c index 3de3f99..481026b 100644 --- a/terminal.c +++ b/terminal.c @@ -14,8 +14,8 @@ #include "terminal.h" /* far2l base64 */ -#include -#include +#include +#include #define VT52_PLUS @@ -59,12 +59,12 @@ #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 ) -const char *EMPTY_WINDOW_TITLE = ""; +static const char *const EMPTY_WINDOW_TITLE = ""; -const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; +static const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t)) -const wchar_t sel_nl[] = SEL_NL; +static const wchar_t sel_nl[] = SEL_NL; /* * Fetch the character at a particular position in a line array, @@ -103,6 +103,7 @@ static void term_print_finish(Terminal *); static void scroll(Terminal *, int, int, int, bool); static void parse_optionalrgb(optionalrgb *out, unsigned *values); static void term_added_data(Terminal *term); +static void term_update_raw_mouse_mode(Terminal *term); static termline *newtermline(Terminal *term, int cols, bool bce) { @@ -136,6 +137,24 @@ static void unlineptr(termline *line) freetermline(line); } +const int colour_indices_conf_to_oscp[CONF_NCOLOURS] = { + #define COLOUR_ENTRY(id,name) OSCP_COLOUR_##id, + CONF_COLOUR_LIST(COLOUR_ENTRY) + #undef COLOUR_ENTRY +}; + +const int colour_indices_conf_to_osc4[CONF_NCOLOURS] = { + #define COLOUR_ENTRY(id,name) OSC4_COLOUR_##id, + CONF_COLOUR_LIST(COLOUR_ENTRY) + #undef COLOUR_ENTRY +}; + +const int colour_indices_oscp_to_osc4[OSCP_NCOLOURS] = { + #define COLOUR_ENTRY(id) OSC4_COLOUR_##id, + OSCP_COLOUR_LIST(COLOUR_ENTRY) + #undef COLOUR_ENTRY +}; + #ifdef TERM_CC_DIAGS /* * Diagnostic function: verify that a termline has a correct @@ -444,11 +463,11 @@ static void makerle(strbuf *b, termline *ldata, if (hdrsize == 0) { assert(prevpos == hdrpos + 1); runpos = hdrpos; - b->len = prevpos+prevlen; + strbuf_shrink_to(b, prevpos+prevlen); } else { memmove(b->u + prevpos+1, b->u + prevpos, prevlen); runpos = prevpos; - b->len = prevpos+prevlen+1; + strbuf_shrink_to(b, prevpos+prevlen+1); /* * Terminate the previous run of ordinary * literals. @@ -465,9 +484,10 @@ static void makerle(strbuf *b, termline *ldata, oldstate = state; makeliteral(b, c, &state); tmplen = b->len - tmppos; - b->len = tmppos; - if (tmplen != thislen || - memcmp(b->u + runpos+1, b->u + tmppos, tmplen)) { + bool match = tmplen == thislen && + !memcmp(b->u + runpos+1, b->u + tmppos, tmplen); + strbuf_shrink_to(b, tmppos); + if (!match) { state = oldstate; break; /* run over */ } @@ -523,7 +543,7 @@ static void makerle(strbuf *b, termline *ldata, assert(hdrsize <= 128); b->u[hdrpos] = hdrsize - 1; } else { - b->len = hdrpos; + strbuf_shrink_to(b, hdrpos); } } static void makeliteral_chr(strbuf *b, termchar *c, unsigned long *state) @@ -727,6 +747,11 @@ static compressed_scrollback_line *compressline(termline *ldata) makerle(b, ldata, makeliteral_truecolour); makerle(b, ldata, makeliteral_cc); + size_t linelen = b->len - sizeof(compressed_scrollback_line); + compressed_scrollback_line *line = + (compressed_scrollback_line *)strbuf_to_str(b); + line->len = linelen; + /* * Diagnostics: ensure that the compressed data really does * decompress to the right thing. @@ -746,7 +771,7 @@ static compressed_scrollback_line *compressline(termline *ldata) printf("\n"); #endif - dcl = decompressline((compressed_scrollback_line *)b->u); + dcl = decompressline(line); assert(ldata->cols == dcl->cols); assert(ldata->lattr == dcl->lattr); for (i = 0; i < ldata->cols; i++) @@ -763,10 +788,6 @@ static compressed_scrollback_line *compressline(termline *ldata) #endif #endif /* TERM_CC_DIAGS */ - size_t linelen = b->len - sizeof(compressed_scrollback_line); - compressed_scrollback_line *line = - (compressed_scrollback_line *)strbuf_to_str(b); - line->len = linelen; return line; } @@ -1170,41 +1191,58 @@ static void check_line_size(Terminal *term, termline *line) static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); +static void term_update_callback(void *ctx); static void term_timer(void *ctx, unsigned long now) { Terminal *term = (Terminal *)ctx; - bool update = false; if (term->tblink_pending && now == term->next_tblink) { term->tblinker = !term->tblinker; term->tblink_pending = false; term_schedule_tblink(term); - update = true; + term->window_update_pending = true; } if (term->cblink_pending && now == term->next_cblink) { term->cblinker = !term->cblinker; term->cblink_pending = false; term_schedule_cblink(term); - update = true; + term->window_update_pending = true; } if (term->in_vbell && now == term->vbell_end) { term->in_vbell = false; - update = true; + term->window_update_pending = true; + } + + if (term->window_update_cooldown && + now == term->window_update_cooldown_end) { + term->window_update_cooldown = false; } - if (update || - (term->window_update_pending && now == term->next_update)) + if (term->window_update_pending) + term_update_callback(term); +} + +static void term_update_callback(void *ctx) +{ + Terminal *term = (Terminal *)ctx; + if (!term->window_update_pending) + return; + if (!term->window_update_cooldown) { term_update(term); + term->window_update_cooldown = true; + term->window_update_cooldown_end = schedule_timer( + UPDATE_DELAY, term_timer, term); + } } static void term_schedule_update(Terminal *term) { if (!term->window_update_pending) { term->window_update_pending = true; - term->next_update = schedule_timer(UPDATE_DELAY, term_timer, term); + queue_toplevel_callback(term_update_callback, term); } } @@ -1340,6 +1378,8 @@ static void power_on(Terminal *term, bool clear) term->xterm_extended_mouse = false; term->urxvt_extended_mouse = false; win_set_raw_mouse_mode(term->win, false); + term->win_pointer_shape_pending = true; + term->win_pointer_shape_raw = false; term->bracketed_paste = false; term->srm_echo = false; { @@ -1364,6 +1404,7 @@ static void power_on(Terminal *term, bool clear) term->curs.x = 0; term_schedule_tblink(term); term_schedule_cblink(term); + term_schedule_update(term); } /* @@ -1373,8 +1414,55 @@ void term_update(Terminal *term) { term->window_update_pending = false; + if (term->win_move_pending) { + win_move(term->win, term->win_move_pending_x, + term->win_move_pending_y); + term->win_move_pending = false; + } + if (term->win_resize_pending) { + win_request_resize(term->win, term->win_resize_pending_w, + term->win_resize_pending_h); + term->win_resize_pending = false; + } + if (term->win_zorder_pending) { + win_set_zorder(term->win, term->win_zorder_top); + term->win_zorder_pending = false; + } + if (term->win_minimise_pending) { + win_set_minimised(term->win, term->win_minimise_enable); + term->win_minimise_pending = false; + } + if (term->win_maximise_pending) { + win_set_maximised(term->win, term->win_maximise_enable); + term->win_maximise_pending = false; + } + if (term->win_title_pending) { + win_set_title(term->win, term->window_title); + term->win_title_pending = false; + } + if (term->win_icon_title_pending) { + win_set_icon_title(term->win, term->icon_title); + term->win_icon_title_pending = false; + } + if (term->win_pointer_shape_pending) { + win_set_raw_mouse_mode_pointer(term->win, term->win_pointer_shape_raw); + term->win_pointer_shape_pending = false; + } + if (term->win_refresh_pending) { + win_refresh(term->win); + term->win_refresh_pending = false; + } + if (term->win_palette_pending) { + unsigned start = term->win_palette_pending_min; + unsigned ncolours = term->win_palette_pending_limit - start; + win_palette_set(term->win, start, ncolours, term->palette + start); + term->win_palette_pending = false; + } + if (win_setup_draw_ctx(term->win)) { - bool need_sbar_update = term->seen_disp_event; + bool need_sbar_update = term->seen_disp_event || + term->win_scrollbar_update_pending; + term->win_scrollbar_update_pending = false; if (term->seen_disp_event && term->scroll_on_disp) { term->disptop = 0; /* return to main screen */ term->seen_disp_event = false; @@ -1490,6 +1578,7 @@ void term_copy_stuff_from_conf(Terminal *term) term->rxvt_homeend = conf_get_bool(term->conf, CONF_rxvt_homeend); term->scroll_on_disp = conf_get_bool(term->conf, CONF_scroll_on_disp); term->scroll_on_key = conf_get_bool(term->conf, CONF_scroll_on_key); + term->xterm_mouse_forbidden = conf_get_bool(term->conf, CONF_no_mouse_rep); term->xterm_256_colour = conf_get_bool(term->conf, CONF_xterm_256_colour); term->true_colour = conf_get_bool(term->conf, CONF_true_colour); @@ -1517,6 +1606,17 @@ void term_copy_stuff_from_conf(Terminal *term) } } +void term_pre_reconfig(Terminal *term, Conf *conf) +{ + + /* + * Copy the current window title into the stored previous + * configuration, so that doing nothing to the window title field + * in the config box doesn't reset the title to its startup state. + */ + conf_set_str(conf, CONF_wintitle, term->window_title); +} + /* * When the user reconfigures us, we need to check the forbidden- * alternate-screen config option, disable raw mouse mode if the @@ -1533,6 +1633,7 @@ void term_reconfig(Terminal *term, Conf *conf) * Mode, BCE, blinking text, character classes. */ bool reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass; + bool palette_changed = false; int i; reset_wrap = (conf_get_bool(term->conf, CONF_wrap_mode) != @@ -1567,6 +1668,40 @@ void term_reconfig(Terminal *term, Conf *conf) } } + { + const char *old_title = conf_get_str(term->conf, CONF_wintitle); + const char *new_title = conf_get_str(conf, CONF_wintitle); + if (strcmp(old_title, new_title)) { + sfree(term->window_title); + term->window_title = dupstr(new_title); + term->win_title_pending = true; + term_schedule_update(term); + } + } + + /* + * Just setting conf is sufficient to cause colour setting changes + * to appear on the next ESC]R palette reset. But we should also + * check whether any colour settings have been changed, so that + * they can be updated immediately if they haven't been overridden + * by some escape sequence. + */ + { + int i, j; + for (i = 0; i < CONF_NCOLOURS; i++) { + for (j = 0; j < 3; j++) + if (conf_get_int_int(term->conf, CONF_colours, i*3+j) != + conf_get_int_int(conf, CONF_colours, i*3+j)) + break; + if (j < 3) { + /* Actually enacting the change has to be deferred + * until the new conf is installed. */ + palette_changed = true; + break; + } + } + } + conf_free(term->conf); term->conf = conf_copy(conf); @@ -1587,10 +1722,6 @@ void term_reconfig(Terminal *term, Conf *conf) if (conf_get_bool(term->conf, CONF_no_alt_screen)) swap_screen(term, 0, false, false); - if (conf_get_bool(term->conf, CONF_no_mouse_rep)) { - term->xterm_mouse = 0; - win_set_raw_mouse_mode(term->win, 0); - } if (conf_get_bool(term->conf, CONF_no_remote_charset)) { term->cset_attr[0] = term->cset_attr[1] = CSET_ASCII; term->sco_acs = term->alt_sco_acs = 0; @@ -1599,9 +1730,12 @@ void term_reconfig(Terminal *term, Conf *conf) if (!conf_get_str(term->conf, CONF_printer)) { term_print_finish(term); } + if (palette_changed) + term_notify_palette_changed(term); term_schedule_tblink(term); term_schedule_cblink(term); term_copy_stuff_from_conf(term); + term_update_raw_mouse_mode(term); } /* @@ -1653,13 +1787,161 @@ void term_clrsb(Terminal *term) term->alt_sblines = 0; /* - * Update the scrollbar to reflect the new state of the world. + * The scrollbar will need updating to reflect the new state of + * the world. */ - update_sbar(term); + term->win_scrollbar_update_pending = true; + term_schedule_update(term); } const optionalrgb optionalrgb_none = {0, 0, 0, 0}; +void term_setup_window_titles(Terminal *term, const char *title_hostname) +{ + const char *conf_title = conf_get_str(term->conf, CONF_wintitle); + sfree(term->window_title); + sfree(term->icon_title); + if (*conf_title) { + term->window_title = dupstr(conf_title); + term->icon_title = dupstr(conf_title); + } else { + if (title_hostname && *title_hostname) + term->window_title = dupcat(title_hostname, " - ", appname); + else + term->window_title = dupstr(appname); + term->icon_title = dupstr(term->window_title); + } + term->win_title_pending = true; + term->win_icon_title_pending = true; +} + +static void palette_rebuild(Terminal *term) +{ + unsigned min_changed = OSC4_NCOLOURS, max_changed = 0; + + if (term->win_palette_pending) { + /* Possibly extend existing range. */ + min_changed = term->win_palette_pending_min; + max_changed = term->win_palette_pending_limit - 1; + } else { + /* Start with empty range. */ + min_changed = OSC4_NCOLOURS; + max_changed = 0; + } + + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) { + rgb new_value; + bool found = false; + + for (unsigned j = lenof(term->subpalettes); j-- > 0 ;) { + if (term->subpalettes[j].present[i]) { + new_value = term->subpalettes[j].values[i]; + found = true; + break; + } + } + + assert(found); /* we expect SUBPAL_CONF to always be set */ + + if (new_value.r != term->palette[i].r || + new_value.g != term->palette[i].g || + new_value.b != term->palette[i].b) { + term->palette[i] = new_value; + if (min_changed > i) + min_changed = i; + if (max_changed < i) + max_changed = i; + } + } + + if (min_changed <= max_changed) { + /* + * At least one colour changed (or we had an update scheduled + * already). Schedule a redraw event to pass the result back + * to the TermWin. This also requires invalidating the rest + * of the window, because usually all the text will need + * redrawing in the new colours. + * (If there was an update pending and this palette rebuild + * didn't actually change anything, we'll harmlessly reinforce + * the existing update request.) + */ + term->win_palette_pending = true; + term->win_palette_pending_min = min_changed; + term->win_palette_pending_limit = max_changed + 1; + term_invalidate(term); + term_schedule_update(term); + } +} + +/* + * Rebuild the palette from configuration and platform colours. + * If 'keep_overrides' set, any escape-sequence-specified overrides will + * remain in place. + */ +static void palette_reset(Terminal *term, bool keep_overrides) +{ + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + term->subpalettes[SUBPAL_CONF].present[i] = true; + + /* + * Copy all the palette information out of the Conf. + */ + for (unsigned i = 0; i < CONF_NCOLOURS; i++) { + rgb *col = &term->subpalettes[SUBPAL_CONF].values[ + colour_indices_conf_to_osc4[i]]; + col->r = conf_get_int_int(term->conf, CONF_colours, i*3+0); + col->g = conf_get_int_int(term->conf, CONF_colours, i*3+1); + col->b = conf_get_int_int(term->conf, CONF_colours, i*3+2); + } + + /* + * Directly invent the rest of the xterm-256 colours. + */ + for (unsigned i = 0; i < 216; i++) { + rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 16]; + int r = i / 36, g = (i / 6) % 6, b = i % 6; + col->r = r ? r * 40 + 55 : 0; + col->g = g ? g * 40 + 55 : 0; + col->b = b ? b * 40 + 55 : 0; + } + for (unsigned i = 0; i < 24; i++) { + rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 232]; + int shade = i * 10 + 8; + col->r = col->g = col->b = shade; + } + + /* + * Re-fetch any OS-local overrides. + */ + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + term->subpalettes[SUBPAL_PLATFORM].present[i] = false; + win_palette_get_overrides(term->win, term); + + if (!keep_overrides) { + /* + * Get rid of all escape-sequence configuration. + */ + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + term->subpalettes[SUBPAL_SESSION].present[i] = false; + } + + /* + * Rebuild the composite palette. + */ + palette_rebuild(term); +} + +void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb) +{ + /* + * We never expect to be called except as re-entry from our own + * call to win_palette_get_overrides above, so we need not mess + * about calling palette_rebuild. + */ + term->subpalettes[SUBPAL_PLATFORM].present[osc4_index] = true; + term->subpalettes[SUBPAL_PLATFORM].values[osc4_index] = rgb; +} + /* * Initialise the terminal. */ @@ -1730,6 +2012,7 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, TermWin *win) term->wcFromTo_size = 0; term->window_update_pending = false; + term->window_update_cooldown = false; term->bidi_cache_size = 0; term->pre_bidi_cache = term->post_bidi_cache = NULL; @@ -1758,6 +2041,26 @@ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, TermWin *win) term->bracketed_paste_active = false; + term->window_title = dupstr(""); + term->icon_title = dupstr(""); + term->minimised = false; + term->winpos_x = term->winpos_y = 0; + term->winpixsize_x = term->winpixsize_y = 0; + + term->win_move_pending = false; + term->win_resize_pending = false; + term->win_zorder_pending = false; + term->win_minimise_pending = false; + term->win_maximise_pending = false; + term->win_title_pending = false; + term->win_icon_title_pending = false; + term->win_pointer_shape_pending = false; + term->win_refresh_pending = false; + term->win_scrollbar_update_pending = false; + term->win_palette_pending = false; + + palette_reset(term, false); + return term; } @@ -1812,6 +2115,9 @@ void term_free(Terminal *term) conf_free(term->conf); + sfree(term->window_title); + sfree(term->icon_title); + sfree(term); } @@ -1820,6 +2126,12 @@ void term_set_trust_status(Terminal *term, bool trusted) term->trusted = trusted; } +void term_get_cursor_position(Terminal *term, int *x, int *y) +{ + *x = term->curs.x; + *y = term->curs.y; +} + /* * Set up the terminal for a given size. */ @@ -2002,8 +2314,8 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) swap_screen(term, save_alt_which, false, false); - update_sbar(term); - term_update(term); + term->win_scrollbar_update_pending = true; + term_schedule_update(term); if (term->backend) backend_size(term->backend, term->cols, term->rows); } @@ -2056,12 +2368,29 @@ static void swap_screen(Terminal *term, int which, reset = false; /* do no weird resetting if which==0 */ if (which != term->alt_which) { + if (term->erase_to_scrollback && term->alt_screen && + term->alt_which && term->disptop < 0) { + /* + * We're swapping away from the alternate screen, so some + * lines are about to vanish from the virtual scrollback. + * Adjust disptop by that much, so that (if we're not + * resetting the scrollback anyway on a display event) the + * current scroll position still ends up pointing at the + * same text. + */ + term->disptop += term->alt_sblines; + if (term->disptop > 0) + term->disptop = 0; + } + term->alt_which = which; ttr = term->alt_screen; term->alt_screen = term->screen; term->screen = ttr; - term->alt_sblines = find_last_nonempty_line(term, term->alt_screen) + 1; + term->alt_sblines = ( + term->alt_screen ? + find_last_nonempty_line(term, term->alt_screen) + 1 : 0); t = term->curs.x; if (!reset && !keep_cur_pos) term->curs.x = term->alt_x; @@ -2099,37 +2428,57 @@ static void swap_screen(Terminal *term, int which, term->alt_sco_acs = t; tp = term->savecurs; - if (!reset && !keep_cur_pos) + if (!reset) term->savecurs = term->alt_savecurs; term->alt_savecurs = tp; t = term->save_cset; - if (!reset && !keep_cur_pos) + if (!reset) term->save_cset = term->alt_save_cset; term->alt_save_cset = t; t = term->save_csattr; - if (!reset && !keep_cur_pos) + if (!reset) term->save_csattr = term->alt_save_csattr; term->alt_save_csattr = t; t = term->save_attr; - if (!reset && !keep_cur_pos) + if (!reset) term->save_attr = term->alt_save_attr; term->alt_save_attr = t; ttc = term->save_truecolour; - if (!reset && !keep_cur_pos) + if (!reset) term->save_truecolour = term->alt_save_truecolour; term->alt_save_truecolour = ttc; bt = term->save_utf; - if (!reset && !keep_cur_pos) + if (!reset) term->save_utf = term->alt_save_utf; term->alt_save_utf = bt; bt = term->save_wnext; - if (!reset && !keep_cur_pos) + if (!reset) term->save_wnext = term->alt_save_wnext; term->alt_save_wnext = bt; t = term->save_sco_acs; - if (!reset && !keep_cur_pos) + if (!reset) term->save_sco_acs = term->alt_save_sco_acs; term->alt_save_sco_acs = t; + + if (term->erase_to_scrollback && term->alt_screen && + term->alt_which && term->disptop < 0) { + /* + * Inverse of the adjustment at the top of this function. + * This time, we're swapping _to_ the alternate screen, so + * some lines are about to _appear_ in the virtual + * scrollback, and we adjust disptop in the other + * direction. + * + * Both these adjustments depend on the value stored in + * term->alt_sblines while the alt screen is selected, + * which is why we had to do one _before_ switching away + * from it and the other _after_ switching to it. + */ + term->disptop -= term->alt_sblines; + int limit = -sblines(term); + if (term->disptop < limit) + term->disptop = limit; + } } if (reset && term->screen) { @@ -2615,6 +2964,15 @@ static void insch(Terminal *term, int n) } } +static void term_update_raw_mouse_mode(Terminal *term) +{ + bool want_raw = (term->xterm_mouse != 0 && !term->xterm_mouse_forbidden); + win_set_raw_mouse_mode(term->win, want_raw); + term->win_pointer_shape_pending = true; + term->win_pointer_shape_raw = want_raw; + term_schedule_update(term); +} + /* * Toggle terminal mode `mode' to state `state'. (`query' indicates * whether the mode is a DEC private one or a normal one.) @@ -2638,8 +2996,12 @@ static void toggle_mode(Terminal *term, int mode, int query, bool state) break; case 3: /* DECCOLM: 80/132 columns */ deselect(term); - if (!term->no_remote_resize) - win_request_resize(term->win, state ? 132 : 80, term->rows); + if (!term->no_remote_resize) { + term->win_resize_pending = true; + term->win_resize_pending_w = state ? 132 : 80; + term->win_resize_pending_h = term->rows; + term_schedule_update(term); + } term->reset_132 = state; term->alt_t = term->marg_t = 0; term->alt_b = term->marg_b = term->rows - 1; @@ -2686,11 +3048,11 @@ static void toggle_mode(Terminal *term, int mode, int query, bool state) break; case 1000: /* xterm mouse 1 (normal) */ term->xterm_mouse = state ? 1 : 0; - win_set_raw_mouse_mode(term->win, state); + term_update_raw_mouse_mode(term); break; case 1002: /* xterm mouse 2 (inc. button drags) */ term->xterm_mouse = state ? 2 : 0; - win_set_raw_mouse_mode(term->win, state); + term_update_raw_mouse_mode(term); break; case 1006: /* xterm extended mouse */ term->xterm_extended_mouse = state; @@ -2812,7 +3174,7 @@ static void do_osc(Terminal *term) // it's possibly too large clipboard #ifdef _WINDOWS - MessageBox(hwnd, "Too large clipboard :(", "Error", MB_OK); + MessageBox(NULL, "Too large clipboard :(", "Error", MB_OK); #endif // correct request id is lost forever @@ -2881,14 +3243,14 @@ static void do_osc(Terminal *term) // todo: do this on window focus also pnid.cbSize = sizeof(pnid); - pnid.hWnd = hwnd; + pnid.hWnd = NULL; pnid.hIcon = LoadIcon(0, IDI_APPLICATION); pnid.uID = 200; Shell_NotifyIconW(NIM_DELETE, &pnid); // todo: use putty icon pnid.cbSize = sizeof(pnid); - pnid.hWnd = hwnd; + pnid.hWnd = NULL; pnid.hIcon = LoadIcon(0, IDI_APPLICATION); pnid.uID = 200; pnid.uFlags = NIF_ICON | NIF_INFO | NIF_MESSAGE; @@ -2962,7 +3324,7 @@ static void do_osc(Terminal *term) #ifdef _WINDOWS char ec_status = 0; if (term->clip_allowed == 1) { - OpenClipboard(hwnd); + OpenClipboard(NULL); ec_status = EmptyClipboard() ? 1 : 0; CloseClipboard(); } @@ -3012,7 +3374,7 @@ static void do_osc(Terminal *term) #ifdef _WINDOWS if (term->clip_allowed == -1) { - int status = MessageBox(hwnd, + int status = MessageBox(NULL, "Allow far2l clipboard sync?", "PyTTY", MB_OKCANCEL); if (status == IDOK) { term->clip_allowed = 1; @@ -3096,7 +3458,7 @@ static void do_osc(Terminal *term) memcpy(GData,buffer,BufferSize); GlobalUnlock(hData); - if (OpenClipboard(hwnd)) { + if (OpenClipboard(NULL)) { if (!SetClipboardData(fmt, (HANDLE)hData)) { GlobalFree(hData); @@ -3155,7 +3517,7 @@ static void do_osc(Terminal *term) int32_t ClipTextSize = 0; if ((gfmt == CF_TEXT || gfmt == CF_UNICODETEXT || gfmt >= 0xC000) && - OpenClipboard(hwnd)) + OpenClipboard(NULL)) { HANDLE hClipData = GetClipboardData((gfmt == CF_TEXT) ? CF_UNICODETEXT : gfmt); @@ -3361,27 +3723,34 @@ static void do_osc(Terminal *term) switch (term->esc_args[0]) { case 0: case 1: - if (!term->no_remote_wintitle) - win_set_icon_title(term->win, term->osc_string); + if (!term->no_remote_wintitle) { + sfree(term->icon_title); + term->icon_title = dupstr(term->osc_string); + term->win_icon_title_pending = true; + term_schedule_update(term); + } if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: - if (!term->no_remote_wintitle) - win_set_title(term->win, term->osc_string); + if (!term->no_remote_wintitle) { + sfree(term->window_title); + term->window_title = dupstr(term->osc_string); + term->win_title_pending = true; + term_schedule_update(term); + } break; case 4: if (term->ldisc && !strcmp(term->osc_string, "?")) { - int r, g, b; - if (win_palette_get(term->win, toint(term->esc_args[1]), - &r, &g, &b)) { + unsigned index = term->esc_args[1]; + if (index < OSC4_NCOLOURS) { + rgb colour = term->palette[index]; char *reply_buf = dupprintf( - "\033]4;%u;rgb:%04x/%04x/%04x\007", - term->esc_args[1], - (unsigned)r * 0x0101, - (unsigned)g * 0x0101, - (unsigned)b * 0x0101); + "\033]4;%u;rgb:%04x/%04x/%04x\007", index, + (unsigned)colour.r * 0x0101, + (unsigned)colour.g * 0x0101, + (unsigned)colour.b * 0x0101); ldisc_send(term->ldisc, reply_buf, strlen(reply_buf), false); sfree(reply_buf); @@ -3634,7 +4003,7 @@ static strbuf *term_input_data_from_unicode( int rv; rv = wc_to_mb(term->ucsdata->line_codepage, 0, widebuf, len, bufptr, len + 1, NULL, term->ucsdata); - buf->len = rv < 0 ? 0 : rv; + strbuf_shrink_to(buf, rv < 0 ? 0 : rv); } return buf; @@ -3967,7 +4336,7 @@ static void term_out(Terminal *term) term->curs.x, &term->erase_char); } } else - /* Or normal C0 controls. */ + /* Or normal C0 controls. */ if ((c & ~0x1F) == 0 && term->termstate < DO_CTRLS) { switch (c) { case '\005': /* ENQ: terminal type query */ @@ -3987,71 +4356,70 @@ static void term_out(Terminal *term) strbuf_free(buf); } break; - case '\007': /* BEL: Bell */ - { - struct beeptime *newbeep; - unsigned long ticks; - - ticks = GETTICKCOUNT(); + case '\007': { /* BEL: Bell */ + struct beeptime *newbeep; + unsigned long ticks; + + ticks = GETTICKCOUNT(); + + if (!term->beep_overloaded) { + newbeep = snew(struct beeptime); + newbeep->ticks = ticks; + newbeep->next = NULL; + if (!term->beephead) + term->beephead = newbeep; + else + term->beeptail->next = newbeep; + term->beeptail = newbeep; + term->nbeeps++; + } - if (!term->beep_overloaded) { - newbeep = snew(struct beeptime); - newbeep->ticks = ticks; - newbeep->next = NULL; - if (!term->beephead) - term->beephead = newbeep; - else - term->beeptail->next = newbeep; - term->beeptail = newbeep; - term->nbeeps++; - } + /* + * Throw out any beeps that happened more than + * t seconds ago. + */ + while (term->beephead && + term->beephead->ticks < ticks - term->bellovl_t) { + struct beeptime *tmp = term->beephead; + term->beephead = tmp->next; + sfree(tmp); + if (!term->beephead) + term->beeptail = NULL; + term->nbeeps--; + } + if (term->bellovl && term->beep_overloaded && + ticks - term->lastbeep >= (unsigned)term->bellovl_s) { /* - * Throw out any beeps that happened more than - * t seconds ago. + * If we're currently overloaded and the + * last beep was more than s seconds ago, + * leave overload mode. */ - while (term->beephead && - term->beephead->ticks < ticks - term->bellovl_t) { - struct beeptime *tmp = term->beephead; - term->beephead = tmp->next; - sfree(tmp); - if (!term->beephead) - term->beeptail = NULL; - term->nbeeps--; - } - - if (term->bellovl && term->beep_overloaded && - ticks - term->lastbeep >= (unsigned)term->bellovl_s) { - /* - * If we're currently overloaded and the - * last beep was more than s seconds ago, - * leave overload mode. - */ - term->beep_overloaded = false; - } else if (term->bellovl && !term->beep_overloaded && - term->nbeeps >= term->bellovl_n) { - /* - * Now, if we have n or more beeps - * remaining in the queue, go into overload - * mode. - */ - term->beep_overloaded = true; - } - term->lastbeep = ticks; - + term->beep_overloaded = false; + } else if (term->bellovl && !term->beep_overloaded && + term->nbeeps >= term->bellovl_n) { /* - * Perform an actual beep if we're not overloaded. + * Now, if we have n or more beeps + * remaining in the queue, go into overload + * mode. */ - if (!term->bellovl || !term->beep_overloaded) { - win_bell(term->win, term->beep); + term->beep_overloaded = true; + } + term->lastbeep = ticks; - if (term->beep == BELL_VISUAL) { - term_schedule_vbell(term, false, 0); - } + /* + * Perform an actual beep if we're not overloaded. + */ + if (!term->bellovl || !term->beep_overloaded) { + win_bell(term->win, term->beep); + + if (term->beep == BELL_VISUAL) { + term_schedule_vbell(term, false, 0); } - seen_disp_event(term); } + seen_disp_event(term); break; + } case '\b': /* BS: Back space */ if (term->curs.x == 0 && (term->curs.y == 0 || !term->wrap)) /* do nothing */ ; @@ -4118,28 +4486,27 @@ static void term_out(Terminal *term) if (term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); break; - case '\t': /* HT: Character tabulation */ - { - pos old_curs = term->curs; - termline *ldata = scrlineptr(term->curs.y); - - do { - term->curs.x++; - } while (term->curs.x < term->cols - 1 && - !term->tabs[term->curs.x]); - - if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) { - if (term->curs.x >= term->cols / 2) - term->curs.x = term->cols / 2 - 1; - } else { - if (term->curs.x >= term->cols) - term->curs.x = term->cols - 1; - } - - check_selection(term, old_curs, term->curs); + case '\t': { /* HT: Character tabulation */ + pos old_curs = term->curs; + termline *ldata = scrlineptr(term->curs.y); + + do { + term->curs.x++; + } while (term->curs.x < term->cols - 1 && + !term->tabs[term->curs.x]); + + if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) { + if (term->curs.x >= term->cols / 2) + term->curs.x = term->cols / 2 - 1; + } else { + if (term->curs.x >= term->cols) + term->curs.x = term->cols - 1; } + + check_selection(term, old_curs, term->curs); seen_disp_event(term); break; + } } } else switch (term->termstate) { @@ -4234,7 +4601,7 @@ static void term_out(Terminal *term) break; case 'Z': /* DECID: terminal type query */ compatibility(VT100); - if (term->ldisc && term->id_string[0]) + if (term->ldisc) ldisc_send(term->ldisc, term->id_string, strlen(term->id_string), false); break; @@ -4244,8 +4611,12 @@ static void term_out(Terminal *term) if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); if (term->reset_132) { - if (!term->no_remote_resize) - win_request_resize(term->win, 80, term->rows); + if (!term->no_remote_resize) { + term->win_resize_pending = true; + term->win_resize_pending_w = 80; + term->win_resize_pending_h = term->rows; + term_schedule_update(term); + } term->reset_132 = false; } if (term->scroll_on_disp) @@ -4257,62 +4628,60 @@ static void term_out(Terminal *term) term->tabs[term->curs.x] = true; break; - case ANSI('8', '#'): /* DECALN: fills screen with Es :-) */ + case ANSI('8', '#'): { /* DECALN: fills screen with Es :-) */ compatibility(VT100); - { - termline *ldata; - int i, j; - pos scrtop, scrbot; - - for (i = 0; i < term->rows; i++) { - ldata = scrlineptr(i); - check_line_size(term, ldata); - for (j = 0; j < term->cols; j++) { - copy_termchar(ldata, j, - &term->basic_erase_char); - ldata->chars[j].chr = 'E'; - } - ldata->lattr = LATTR_NORM; + termline *ldata; + int i, j; + pos scrtop, scrbot; + + for (i = 0; i < term->rows; i++) { + ldata = scrlineptr(i); + check_line_size(term, ldata); + for (j = 0; j < term->cols; j++) { + copy_termchar(ldata, j, + &term->basic_erase_char); + ldata->chars[j].chr = 'E'; } - if (term->scroll_on_disp) - term->disptop = 0; - seen_disp_event(term); - scrtop.x = scrtop.y = 0; - scrbot.x = 0; - scrbot.y = term->rows; - check_selection(term, scrtop, scrbot); + ldata->lattr = LATTR_NORM; } + if (term->scroll_on_disp) + term->disptop = 0; + seen_disp_event(term); + scrtop.x = scrtop.y = 0; + scrbot.x = 0; + scrbot.y = term->rows; + check_selection(term, scrtop, scrbot); break; + } case ANSI('3', '#'): case ANSI('4', '#'): case ANSI('5', '#'): - case ANSI('6', '#'): + case ANSI('6', '#'): { compatibility(VT100); - { - int nlattr; - termline *ldata; + int nlattr; + termline *ldata; - switch (ANSI(c, term->esc_query)) { - case ANSI('3', '#'): /* DECDHL: 2*height, top */ - nlattr = LATTR_TOP; - break; - case ANSI('4', '#'): /* DECDHL: 2*height, bottom */ - nlattr = LATTR_BOT; - break; - case ANSI('5', '#'): /* DECSWL: normal */ - nlattr = LATTR_NORM; - break; - default: /* case ANSI('6', '#'): DECDWL: 2*width */ - nlattr = LATTR_WIDE; - break; - } - ldata = scrlineptr(term->curs.y); - check_line_size(term, ldata); - check_trust_status(term, ldata); - ldata->lattr = nlattr; + switch (ANSI(c, term->esc_query)) { + case ANSI('3', '#'): /* DECDHL: 2*height, top */ + nlattr = LATTR_TOP; + break; + case ANSI('4', '#'): /* DECDHL: 2*height, bottom */ + nlattr = LATTR_BOT; + break; + case ANSI('5', '#'): /* DECSWL: normal */ + nlattr = LATTR_NORM; + break; + default: /* case ANSI('6', '#'): DECDWL: 2*width */ + nlattr = LATTR_WIDE; + break; } + ldata = scrlineptr(term->curs.y); + check_line_size(term, ldata); + check_trust_status(term, ldata); + ldata->lattr = nlattr; break; + } /* GZD4: G0 designate 94-set */ case ANSI('A', '('): compatibility(VT100); @@ -4491,34 +4860,32 @@ static void term_out(Terminal *term) (term->dec_om ? 2 : 0)); seen_disp_event(term); break; - case 'J': /* ED: erase screen or parts of it */ - { - unsigned int i = def(term->esc_args[0], 0); - if (i == 3) { - /* Erase Saved Lines (xterm) - * This follows Thomas Dickey's xterm. */ - if (!term->no_remote_clearscroll) - term_clrsb(term); - } else { - i++; - if (i > 3) - i = 0; - erase_lots(term, false, !!(i & 2), !!(i & 1)); - } + case 'J': { /* ED: erase screen or parts of it */ + unsigned int i = def(term->esc_args[0], 0); + if (i == 3) { + /* Erase Saved Lines (xterm) + * This follows Thomas Dickey's xterm. */ + if (!term->no_remote_clearscroll) + term_clrsb(term); + } else { + i++; + if (i > 3) + i = 0; + erase_lots(term, false, !!(i & 2), !!(i & 1)); } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); break; - case 'K': /* EL: erase line or parts of it */ - { - unsigned int i = def(term->esc_args[0], 0) + 1; - if (i > 3) - i = 0; - erase_lots(term, true, !!(i & 2), !!(i & 1)); - } + } + case 'K': { /* EL: erase line or parts of it */ + unsigned int i = def(term->esc_args[0], 0) + 1; + if (i > 3) + i = 0; + erase_lots(term, true, !!(i & 2), !!(i & 1)); seen_disp_event(term); break; + } case 'L': /* IL: insert lines */ compatibility(VT102); CLAMP(term->esc_args[0], term->rows); @@ -4552,7 +4919,7 @@ static void term_out(Terminal *term) case 'c': /* DA: terminal type query */ compatibility(VT100); /* This is the response for a VT102 */ - if (term->ldisc && term->id_string[0]) + if (term->ldisc) ldisc_send(term->ldisc, term->id_string, strlen(term->id_string), false); break; @@ -4572,41 +4939,34 @@ static void term_out(Terminal *term) case 'h': /* SM: toggle modes to high */ case ANSI_QUE('h'): compatibility(VT100); - { - int i; - for (i = 0; i < term->esc_nargs; i++) - toggle_mode(term, term->esc_args[i], - term->esc_query, true); - } + for (int i = 0; i < term->esc_nargs; i++) + toggle_mode(term, term->esc_args[i], + term->esc_query, true); break; case 'i': /* MC: Media copy */ - case ANSI_QUE('i'): + case ANSI_QUE('i'): { compatibility(VT100); - { - char *printer; - if (term->esc_nargs != 1) break; - if (term->esc_args[0] == 5 && - (printer = conf_get_str(term->conf, - CONF_printer))[0]) { - term->printing = true; - term->only_printing = !term->esc_query; - term->print_state = 0; - term_print_setup(term, printer); - } else if (term->esc_args[0] == 4 && - term->printing) { - term_print_finish(term); - } + char *printer; + if (term->esc_nargs != 1) break; + if (term->esc_args[0] == 5 && + (printer = conf_get_str(term->conf, + CONF_printer))[0]) { + term->printing = true; + term->only_printing = !term->esc_query; + term->print_state = 0; + term_print_setup(term, printer); + } else if (term->esc_args[0] == 4 && + term->printing) { + term_print_finish(term); } break; + } case 'l': /* RM: toggle modes to low */ case ANSI_QUE('l'): compatibility(VT100); - { - int i; - for (i = 0; i < term->esc_nargs; i++) - toggle_mode(term, term->esc_args[i], - term->esc_query, false); - } + for (int i = 0; i < term->esc_nargs; i++) + toggle_mode(term, term->esc_args[i], + term->esc_query, false); break; case 'g': /* TBC: clear tabs */ compatibility(VT100); @@ -4656,222 +5016,222 @@ static void term_out(Terminal *term) } break; case 'm': /* SGR: set graphics rendition */ - { + /* + * A VT100 without the AVO only had one + * attribute, either underline or reverse + * video depending on the cursor type, this + * was selected by CSI 7m. + * + * case 2: + * This is sometimes DIM, eg on the GIGI and + * Linux + * case 8: + * This is sometimes INVIS various ANSI. + * case 21: + * This like 22 disables BOLD, DIM and INVIS + * + * The ANSI colours appear on any terminal + * that has colour (obviously) but the + * interaction between sgr0 and the colours + * varies but is usually related to the + * background colour erase item. The + * interaction between colour attributes and + * the mono ones is also very implementation + * dependent. + * + * The 39 and 49 attributes are likely to be + * unimplemented. + */ + for (int i = 0; i < term->esc_nargs; i++) + switch (def(term->esc_args[i], 0)) { + case 0: /* restore defaults */ + term->curr_attr = term->default_attr; + term->curr_truecolour = + term->basic_erase_char.truecolour; + break; + case 1: /* enable bold */ + compatibility(VT100AVO); + term->curr_attr |= ATTR_BOLD; + break; + case 2: /* enable dim */ + compatibility(OTHER); + term->curr_attr |= ATTR_DIM; + break; + case 21: /* (enable double underline) */ + compatibility(OTHER); + case 4: /* enable underline */ + compatibility(VT100AVO); + term->curr_attr |= ATTR_UNDER; + break; + case 5: /* enable blink */ + compatibility(VT100AVO); + term->curr_attr |= ATTR_BLINK; + break; + case 6: /* SCO light bkgrd */ + compatibility(SCOANSI); + term->blink_is_real = false; + term->curr_attr |= ATTR_BLINK; + term_schedule_tblink(term); + break; + case 7: /* enable reverse video */ + term->curr_attr |= ATTR_REVERSE; + break; + case 9: /* enable strikethrough */ + term->curr_attr |= ATTR_STRIKE; + break; + case 10: /* SCO acs off */ + compatibility(SCOANSI); + if (term->no_remote_charset) break; + term->sco_acs = 0; break; + case 11: /* SCO acs on */ + compatibility(SCOANSI); + if (term->no_remote_charset) break; + term->sco_acs = 1; break; + case 12: /* SCO acs on, |0x80 */ + compatibility(SCOANSI); + if (term->no_remote_charset) break; + term->sco_acs = 2; break; + case 22: /* disable bold and dim */ + compatibility2(OTHER, VT220); + term->curr_attr &= ~(ATTR_BOLD | ATTR_DIM); + break; + case 24: /* disable underline */ + compatibility2(OTHER, VT220); + term->curr_attr &= ~ATTR_UNDER; + break; + case 25: /* disable blink */ + compatibility2(OTHER, VT220); + term->curr_attr &= ~ATTR_BLINK; + break; + case 27: /* disable reverse video */ + compatibility2(OTHER, VT220); + term->curr_attr &= ~ATTR_REVERSE; + break; + case 29: /* disable strikethrough */ + term->curr_attr &= ~ATTR_STRIKE; + break; + case 30: + case 31: + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + /* foreground */ + term->curr_truecolour.fg.enabled = false; + term->curr_attr &= ~ATTR_FGMASK; + term->curr_attr |= + (term->esc_args[i] - 30)<curr_truecolour.fg.enabled = false; + term->curr_attr &= ~ATTR_FGMASK; + term->curr_attr |= + ((term->esc_args[i] - 90 + 8) + << ATTR_FGSHIFT); + break; + case 39: /* default-foreground */ + term->curr_truecolour.fg.enabled = false; + term->curr_attr &= ~ATTR_FGMASK; + term->curr_attr |= ATTR_DEFFG; + break; + case 40: + case 41: + case 42: + case 43: + case 44: + case 45: + case 46: + case 47: + /* background */ + term->curr_truecolour.bg.enabled = false; + term->curr_attr &= ~ATTR_BGMASK; + term->curr_attr |= + (term->esc_args[i] - 40)<curr_truecolour.bg.enabled = false; + term->curr_attr &= ~ATTR_BGMASK; + term->curr_attr |= + ((term->esc_args[i] - 100 + 8) + << ATTR_BGSHIFT); + break; + case 49: /* default-background */ + term->curr_truecolour.bg.enabled = false; + term->curr_attr &= ~ATTR_BGMASK; + term->curr_attr |= ATTR_DEFBG; + break; + /* - * A VT100 without the AVO only had one - * attribute, either underline or - * reverse video depending on the - * cursor type, this was selected by - * CSI 7m. - * - * case 2: - * This is sometimes DIM, eg on the - * GIGI and Linux - * case 8: - * This is sometimes INVIS various ANSI. - * case 21: - * This like 22 disables BOLD, DIM and INVIS - * - * The ANSI colours appear on any - * terminal that has colour (obviously) - * but the interaction between sgr0 and - * the colours varies but is usually - * related to the background colour - * erase item. The interaction between - * colour attributes and the mono ones - * is also very implementation - * dependent. - * - * The 39 and 49 attributes are likely - * to be unimplemented. + * 256-colour and true-colour + * sequences. A 256-colour + * foreground is selected by a + * sequence of 3 arguments in the + * form 38;5;n, where n is in the + * range 0-255. A true-colour RGB + * triple is selected by 5 args of + * the form 38;2;r;g;b. Replacing + * the initial 38 with 48 in both + * cases selects the same colour + * as the background. */ - int i; - for (i = 0; i < term->esc_nargs; i++) { - switch (def(term->esc_args[i], 0)) { - case 0: /* restore defaults */ - term->curr_attr = term->default_attr; - term->curr_truecolour = - term->basic_erase_char.truecolour; - break; - case 1: /* enable bold */ - compatibility(VT100AVO); - term->curr_attr |= ATTR_BOLD; - break; - case 2: /* enable dim */ - compatibility(OTHER); - term->curr_attr |= ATTR_DIM; - break; - case 21: /* (enable double underline) */ - compatibility(OTHER); - case 4: /* enable underline */ - compatibility(VT100AVO); - term->curr_attr |= ATTR_UNDER; - break; - case 5: /* enable blink */ - compatibility(VT100AVO); - term->curr_attr |= ATTR_BLINK; - break; - case 6: /* SCO light bkgrd */ - compatibility(SCOANSI); - term->blink_is_real = false; - term->curr_attr |= ATTR_BLINK; - term_schedule_tblink(term); - break; - case 7: /* enable reverse video */ - term->curr_attr |= ATTR_REVERSE; - break; - case 10: /* SCO acs off */ - compatibility(SCOANSI); - if (term->no_remote_charset) break; - term->sco_acs = 0; break; - case 11: /* SCO acs on */ - compatibility(SCOANSI); - if (term->no_remote_charset) break; - term->sco_acs = 1; break; - case 12: /* SCO acs on, |0x80 */ - compatibility(SCOANSI); - if (term->no_remote_charset) break; - term->sco_acs = 2; break; - case 22: /* disable bold and dim */ - compatibility2(OTHER, VT220); - term->curr_attr &= ~(ATTR_BOLD | ATTR_DIM); - break; - case 24: /* disable underline */ - compatibility2(OTHER, VT220); - term->curr_attr &= ~ATTR_UNDER; - break; - case 25: /* disable blink */ - compatibility2(OTHER, VT220); - term->curr_attr &= ~ATTR_BLINK; - break; - case 27: /* disable reverse video */ - compatibility2(OTHER, VT220); - term->curr_attr &= ~ATTR_REVERSE; - break; - case 30: - case 31: - case 32: - case 33: - case 34: - case 35: - case 36: - case 37: - /* foreground */ - term->curr_truecolour.fg.enabled = false; - term->curr_attr &= ~ATTR_FGMASK; - term->curr_attr |= - (term->esc_args[i] - 30)<curr_truecolour.fg.enabled = false; - term->curr_attr &= ~ATTR_FGMASK; - term->curr_attr |= - ((term->esc_args[i] - 90 + 8) - << ATTR_FGSHIFT); - break; - case 39: /* default-foreground */ - term->curr_truecolour.fg.enabled = false; - term->curr_attr &= ~ATTR_FGMASK; - term->curr_attr |= ATTR_DEFFG; - break; - case 40: - case 41: - case 42: - case 43: - case 44: - case 45: - case 46: - case 47: - /* background */ - term->curr_truecolour.bg.enabled = false; - term->curr_attr &= ~ATTR_BGMASK; - term->curr_attr |= - (term->esc_args[i] - 40)<curr_truecolour.bg.enabled = false; - term->curr_attr &= ~ATTR_BGMASK; - term->curr_attr |= - ((term->esc_args[i] - 100 + 8) - << ATTR_BGSHIFT); - break; - case 49: /* default-background */ - term->curr_truecolour.bg.enabled = false; - term->curr_attr &= ~ATTR_BGMASK; - term->curr_attr |= ATTR_DEFBG; - break; - - /* - * 256-colour and true-colour - * sequences. A 256-colour - * foreground is selected by a - * sequence of 3 arguments in the - * form 38;5;n, where n is in the - * range 0-255. A true-colour RGB - * triple is selected by 5 args of - * the form 38;2;r;g;b. Replacing - * the initial 38 with 48 in both - * cases selects the same colour - * as the background. - */ - case 38: - if (i+2 < term->esc_nargs && - term->esc_args[i+1] == 5) { - term->curr_attr &= ~ATTR_FGMASK; - term->curr_attr |= - ((term->esc_args[i+2] & 0xFF) - << ATTR_FGSHIFT); - term->curr_truecolour.fg = - optionalrgb_none; - i += 2; - } - if (i + 4 < term->esc_nargs && - term->esc_args[i + 1] == 2) { - parse_optionalrgb( - &term->curr_truecolour.fg, - term->esc_args + (i+2)); - i += 4; - } - break; - case 48: - if (i+2 < term->esc_nargs && - term->esc_args[i+1] == 5) { - term->curr_attr &= ~ATTR_BGMASK; - term->curr_attr |= - ((term->esc_args[i+2] & 0xFF) - << ATTR_BGSHIFT); - term->curr_truecolour.bg = - optionalrgb_none; - i += 2; - } - if (i + 4 < term->esc_nargs && - term->esc_args[i+1] == 2) { - parse_optionalrgb( - &term->curr_truecolour.bg, - term->esc_args + (i+2)); - i += 4; - } - break; - } + case 38: + if (i+2 < term->esc_nargs && + term->esc_args[i+1] == 5) { + term->curr_attr &= ~ATTR_FGMASK; + term->curr_attr |= + ((term->esc_args[i+2] & 0xFF) + << ATTR_FGSHIFT); + term->curr_truecolour.fg = + optionalrgb_none; + i += 2; } - set_erase_char(term); + if (i + 4 < term->esc_nargs && + term->esc_args[i + 1] == 2) { + parse_optionalrgb( + &term->curr_truecolour.fg, + term->esc_args + (i+2)); + i += 4; + } + break; + case 48: + if (i+2 < term->esc_nargs && + term->esc_args[i+1] == 5) { + term->curr_attr &= ~ATTR_BGMASK; + term->curr_attr |= + ((term->esc_args[i+2] & 0xFF) + << ATTR_BGSHIFT); + term->curr_truecolour.bg = + optionalrgb_none; + i += 2; + } + if (i + 4 < term->esc_nargs && + term->esc_args[i+1] == 2) { + parse_optionalrgb( + &term->curr_truecolour.bg, + term->esc_args + (i+2)); + i += 4; + } + break; } + set_erase_char(term); break; case 's': /* save cursor */ save_cursor(term, true); @@ -4891,9 +5251,13 @@ static void term_out(Terminal *term) && (term->esc_args[0] < 1 || term->esc_args[0] >= 24)) { compatibility(VT340TEXT); - if (!term->no_remote_resize) - win_request_resize(term->win, term->cols, - def(term->esc_args[0], 24)); + if (!term->no_remote_resize) { + term->win_resize_pending = true; + term->win_resize_pending_w = term->cols; + term->win_resize_pending_h = + def(term->esc_args[0], 24); + term_schedule_update(term); + } deselect(term); } else if (term->esc_nargs >= 1 && term->esc_args[0] >= 1 && @@ -4901,21 +5265,29 @@ static void term_out(Terminal *term) compatibility(OTHER); switch (term->esc_args[0]) { - int x, y, len; + int len; char buf[80]; const char *p; case 1: - win_set_minimised(term->win, false); + term->win_minimise_pending = true; + term->win_minimise_enable = false; + term_schedule_update(term); break; case 2: - win_set_minimised(term->win, true); + term->win_minimise_pending = true; + term->win_minimise_enable = true; + term_schedule_update(term); break; case 3: if (term->esc_nargs >= 3) { - if (!term->no_remote_resize) - win_move(term->win, - def(term->esc_args[1], 0), - def(term->esc_args[2], 0)); + if (!term->no_remote_resize) { + term->win_move_pending = true; + term->win_move_pending_x = + def(term->esc_args[1], 0); + term->win_move_pending_y = + def(term->esc_args[2], 0); + term_schedule_update(term); + } } break; case 4: @@ -4926,52 +5298,60 @@ static void term_out(Terminal *term) break; case 5: /* move to top */ - win_set_zorder(term->win, true); + term->win_zorder_pending = true; + term->win_zorder_top = true; + term_schedule_update(term); break; case 6: /* move to bottom */ - win_set_zorder(term->win, false); + term->win_zorder_pending = true; + term->win_zorder_top = false; + term_schedule_update(term); break; case 7: - win_refresh(term->win); + term->win_refresh_pending = true; + term_schedule_update(term); break; case 8: - if (term->esc_nargs >= 3) { - if (!term->no_remote_resize) - win_request_resize( - term->win, - def(term->esc_args[2], - term->conf_width), - def(term->esc_args[1], - term->conf_height)); + if (term->esc_nargs >= 3 && + !term->no_remote_resize) { + term->win_resize_pending = true; + term->win_resize_pending_w = + def(term->esc_args[2], + term->conf_width); + term->win_resize_pending_h = + def(term->esc_args[1], + term->conf_height); + term_schedule_update(term); } break; case 9: - if (term->esc_nargs >= 2) - win_set_maximised( - term->win, - term->esc_args[1] ? true : false); + if (term->esc_nargs >= 2) { + term->win_maximise_pending = true; + term->win_maximise_enable = + term->esc_args[1]; + term_schedule_update(term); + } break; case 11: if (term->ldisc) - ldisc_send(term->ldisc, - win_is_minimised(term->win) ? + ldisc_send(term->ldisc, term->minimised ? "\033[2t" : "\033[1t", 4, false); break; case 13: if (term->ldisc) { - win_get_pos(term->win, &x, &y); len = sprintf(buf, "\033[3;%u;%ut", - (unsigned)x, - (unsigned)y); + term->winpos_x, + term->winpos_y); ldisc_send(term->ldisc, buf, len, false); } break; case 14: if (term->ldisc) { - win_get_pixels(term->win, &x, &y); - len = sprintf(buf, "\033[4;%d;%dt", y, x); + len = sprintf(buf, "\033[4;%u;%ut", + term->winpixsize_y, + term->winpixsize_x); ldisc_send(term->ldisc, buf, len, false); } break; @@ -5003,14 +5383,13 @@ static void term_out(Terminal *term) if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) - p = win_get_title(term->win, true); + p = term->icon_title; else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]L", 3, false); - if (len > 0) - ldisc_send(term->ldisc, p, len, false); + ldisc_send(term->ldisc, p, len, false); ldisc_send(term->ldisc, "\033\\", 2, false); } @@ -5019,14 +5398,13 @@ static void term_out(Terminal *term) if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) - p = win_get_title(term->win, false); + p = term->window_title; else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]l", 3, false); - if (len > 0) - ldisc_send(term->ldisc, p, len, false); + ldisc_send(term->ldisc, p, len, false); ldisc_send(term->ldisc, "\033\\", 2, false); } @@ -5059,10 +5437,13 @@ static void term_out(Terminal *term) */ compatibility(VT420); if (term->esc_nargs == 1 && term->esc_args[0] > 0) { - if (!term->no_remote_resize) - win_request_resize(term->win, term->cols, - def(term->esc_args[0], - term->conf_height)); + if (!term->no_remote_resize) { + term->win_resize_pending = true; + term->win_resize_pending_w = term->cols; + term->win_resize_pending_h = + def(term->esc_args[0], term->conf_height); + term_schedule_update(term); + } deselect(term); } break; @@ -5074,39 +5455,40 @@ static void term_out(Terminal *term) */ compatibility(VT340TEXT); if (term->esc_nargs <= 1) { - if (!term->no_remote_resize) - win_request_resize( - term->win, - def(term->esc_args[0], term->conf_width), - term->rows); + if (!term->no_remote_resize) { + term->win_resize_pending = true; + term->win_resize_pending_w = + def(term->esc_args[0], term->conf_width); + term->win_resize_pending_h = term->rows; + term_schedule_update(term); + } deselect(term); } break; - case 'X': /* ECH: write N spaces w/o moving cursor */ + case 'X': { /* ECH: write N spaces w/o moving cursor */ /* XXX VTTEST says this is vt220, vt510 manual * says vt100 */ compatibility(ANSIMIN); CLAMP(term->esc_args[0], term->cols); - { - int n = def(term->esc_args[0], 1); - pos cursplus; - int p = term->curs.x; - termline *cline = scrlineptr(term->curs.y); - - check_trust_status(term, cline); - if (n > term->cols - term->curs.x) - n = term->cols - term->curs.x; - cursplus = term->curs; - cursplus.x += n; - check_boundary(term, term->curs.x, term->curs.y); - check_boundary(term, term->curs.x+n, term->curs.y); - check_selection(term, term->curs, cursplus); - while (n--) - copy_termchar(cline, p++, - &term->erase_char); - seen_disp_event(term); - } + int n = def(term->esc_args[0], 1); + pos cursplus; + int p = term->curs.x; + termline *cline = scrlineptr(term->curs.y); + + check_trust_status(term, cline); + if (n > term->cols - term->curs.x) + n = term->cols - term->curs.x; + cursplus = term->curs; + cursplus.x += n; + check_boundary(term, term->curs.x, term->curs.y); + check_boundary(term, term->curs.x+n, term->curs.y); + check_selection(term, term->curs, cursplus); + while (n--) + copy_termchar(cline, p++, + &term->erase_char); + seen_disp_event(term); break; + } case 'x': /* DECREQTPARM: report terminal characteristics */ compatibility(VT100); if (term->ldisc) { @@ -5119,22 +5501,21 @@ static void term_out(Terminal *term) } } break; - case 'Z': /* CBT */ + case 'Z': { /* CBT */ compatibility(OTHER); CLAMP(term->esc_args[0], term->cols); - { - int i = def(term->esc_args[0], 1); - pos old_curs = term->curs; - - for(;i>0 && term->curs.x>0; i--) { - do { - term->curs.x--; - } while (term->curs.x >0 && - !term->tabs[term->curs.x]); - } - check_selection(term, old_curs, term->curs); + int i = def(term->esc_args[0], 1); + pos old_curs = term->curs; + + for(;i>0 && term->curs.x>0; i--) { + do { + term->curs.x--; + } while (term->curs.x >0 && + !term->tabs[term->curs.x]); } + check_selection(term, old_curs, term->curs); break; + } case ANSI('c', '='): /* Hide or Show Cursor */ compatibility(SCOANSI); switch(term->esc_args[0]) { @@ -5286,22 +5667,17 @@ static void term_out(Terminal *term) */ if (!has_compat(VT420) && has_compat(VT100)) { if (!term->no_remote_resize) { - if (term->reset_132) - win_request_resize(term->win, 132, 24); - else - win_request_resize(term->win, 80, 24); + term->win_resize_pending = true; + term->win_resize_pending_w = + term->reset_132 ? 132 : 80; + term->win_resize_pending_h = 24; + term_schedule_update(term); } } #endif break; } break; - //case SEEN_APC: - /* todo */ - // if (!WriteStr2TC(fdout, enable ? "\x1b_far2l1\x1b\\\x1b[5n" : "\x1b_far2l0\x07\x1b[5n")) - // if (!WriteStr2TC(fdout, enable ? "\x1b_far2l1\x1b\\\x1b[5n" : "\x1b_far2l0\x07\x1b[5n")) - - //break; case SEEN_OSC: term->osc_w = false; switch (c) { @@ -5310,7 +5686,7 @@ static void term_out(Terminal *term) term->osc_strlen = 0; break; case 'R': /* Linux palette reset */ - win_palette_reset(term->win); + palette_reset(term, false); term_invalidate(term); term->termstate = TOPLEVEL; break; @@ -5397,32 +5773,40 @@ static void term_out(Terminal *term) else if (term->osc_strlen < OSC_STR_MAX) term->osc_string[term->osc_strlen++] = (char)c; break; - case SEEN_OSC_P: - { - int max = (term->osc_strlen == 0 ? 21 : 15); - int val; - if ((int)c >= '0' && (int)c <= '9') - val = c - '0'; - else if ((int)c >= 'A' && (int)c <= 'A' + max - 10) - val = c - 'A' + 10; - else if ((int)c >= 'a' && (int)c <= 'a' + max - 10) - val = c - 'a' + 10; - else { - term->termstate = TOPLEVEL; - break; - } - term->osc_string[term->osc_strlen++] = val; - if (term->osc_strlen >= 7) { - win_palette_set( - term->win, term->osc_string[0], - term->osc_string[1] * 16 + term->osc_string[2], - term->osc_string[3] * 16 + term->osc_string[4], - term->osc_string[5] * 16 + term->osc_string[6]); - term_invalidate(term); - term->termstate = TOPLEVEL; - } + case SEEN_OSC_P: { + int max = (term->osc_strlen == 0 ? 21 : 15); + int val; + if ((int)c >= '0' && (int)c <= '9') + val = c - '0'; + else if ((int)c >= 'A' && (int)c <= 'A' + max - 10) + val = c - 'A' + 10; + else if ((int)c >= 'a' && (int)c <= 'a' + max - 10) + val = c - 'a' + 10; + else { + term->termstate = TOPLEVEL; + break; + } + term->osc_string[term->osc_strlen++] = val; + if (term->osc_strlen >= 7) { + unsigned oscp_index = term->osc_string[0]; + assert(oscp_index < OSCP_NCOLOURS); + unsigned osc4_index = + colour_indices_oscp_to_osc4[oscp_index]; + + rgb *value = &term->subpalettes[SUBPAL_SESSION].values[ + osc4_index]; + value->r = term->osc_string[1] * 16 + term->osc_string[2]; + value->g = term->osc_string[3] * 16 + term->osc_string[4]; + value->b = term->osc_string[5] * 16 + term->osc_string[6]; + term->subpalettes[SUBPAL_SESSION].present[ + osc4_index] = true; + + palette_rebuild(term); + + term->termstate = TOPLEVEL; } break; + } case SEEN_OSC_W: switch (c) { case '0': @@ -6443,8 +6827,8 @@ void term_scroll(Terminal *term, int rel, int where) term->disptop = sbtop; if (term->disptop > 0) term->disptop = 0; - update_sbar(term); - term_update(term); + term->win_scrollbar_update_pending = true; + term_schedule_update(term); } /* @@ -7400,7 +7784,7 @@ void term_mouse(Terminal *term, Mouse_Button braw, Mouse_Button bcooked, */ if (term->selstate != DRAGGING) term_out(term); - term_update(term); + term_schedule_update(term); } int format_arrow_key(char *buf, Terminal *term, int xkey, bool ctrl) @@ -7735,7 +8119,7 @@ char *term_get_ttymode(Terminal *term, const char *mode) if (strcmp(mode, "ERASE") == 0) { val = term->bksp_is_delete ? "^?" : "^H"; } else if (strcmp(mode, "IUTF8") == 0) { - val = win_is_utf8(term->win) ? "yes" : "no"; + val = (term->ucsdata->line_codepage == CP_UTF8) ? "yes" : "no"; } /* FIXME: perhaps we should set ONLCR based on lfhascr as well? */ /* FIXME: or ECHO and friends based on local echo state? */ @@ -7745,7 +8129,6 @@ char *term_get_ttymode(Terminal *term, const char *mode) struct term_userpass_state { size_t curr_prompt; bool done_prompt; /* printed out prompt yet? */ - size_t pos; /* cursor position */ }; /* Tiny wrapper to make it easier to write lots of little strings */ @@ -7800,7 +8183,6 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) if (!s->done_prompt) { term_write(term, ptrlen_from_asciz(pr->prompt)); s->done_prompt = true; - s->pos = 0; } /* Breaking out here ensures that the prompt is printed even @@ -7815,8 +8197,6 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) case 10: case 13: term_write(term, PTRLEN_LITERAL("\r\n")); - prompt_ensure_result_size(pr, s->pos + 1); - pr->result[s->pos] = '\0'; /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = false; @@ -7824,18 +8204,18 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) break; case 8: case 127: - if (s->pos > 0) { + if (pr->result->len > 0) { if (pr->echo) term_write(term, PTRLEN_LITERAL("\b \b")); - s->pos--; + strbuf_shrink_by(pr->result, 1); } break; case 21: case 27: - while (s->pos > 0) { + while (pr->result->len > 0) { if (pr->echo) term_write(term, PTRLEN_LITERAL("\b \b")); - s->pos--; + strbuf_shrink_by(pr->result, 1); } break; case 3: @@ -7853,8 +8233,7 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) */ if (!pr->echo || (c >= ' ' && c <= '~') || ((unsigned char) c >= 160)) { - prompt_ensure_result_size(pr, s->pos + 1); - pr->result[s->pos++] = c; + put_byte(pr->result, c); if (pr->echo) term_write(term, make_ptrlen(&c, 1)); } @@ -7873,3 +8252,24 @@ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) } } +void term_notify_minimised(Terminal *term, bool minimised) +{ + term->minimised = minimised; +} + +void term_notify_palette_changed(Terminal *term) +{ + palette_reset(term, true); +} + +void term_notify_window_pos(Terminal *term, int x, int y) +{ + term->winpos_x = x; + term->winpos_y = y; +} + +void term_notify_window_size_pixels(Terminal *term, int x, int y) +{ + term->winpixsize_x = x; + term->winpixsize_y = y; +} diff --git a/terminal.h b/terminal.h index c0f30ea..873fed6 100644 --- a/terminal.h +++ b/terminal.h @@ -150,6 +150,7 @@ struct terminal_tag { bool seen_disp_event; bool big_cursor; + bool xterm_mouse_forbidden; int xterm_mouse; /* send mouse messages to host */ bool xterm_extended_mouse; bool urxvt_extended_mouse; @@ -183,6 +184,7 @@ struct terminal_tag { /* far2l */ //#define OSC_STR_MAX 2048 #define OSC_STR_MAX 1048576 + int osc_strlen; char osc_string[OSC_STR_MAX + 1]; bool osc_w; @@ -207,10 +209,6 @@ struct terminal_tag { SEEN_OSC_P, OSC_STRING, OSC_MAYBE_ST, - - /* far2l */ - SEEN_APC, - VT52_ESC, VT52_Y1, VT52_Y2, @@ -269,11 +267,17 @@ struct terminal_tag { bool in_term_out; /* - * We schedule a window update shortly after receiving terminal - * data. This tracks whether one is currently pending. + * We don't permit window updates too close together, to avoid CPU + * churn pointlessly redrawing the window faster than the user can + * read. So after an update, we set window_update_cooldown = true + * and schedule a timer to reset it to false. In between those + * times, window updates are not performed, and instead we set + * window_update_pending = true, which will remind us to perform + * the deferred redraw when the cooldown period ends and + * window_update_cooldown is reset to false. */ - bool window_update_pending; - long next_update; + bool window_update_pending, window_update_cooldown; + long window_update_cooldown_end; /* * Track pending blinks and tblinks. @@ -353,6 +357,50 @@ struct terminal_tag { int mouse_select_clipboards[N_CLIPBOARDS]; int n_mouse_select_clipboards; int mouse_paste_clipboard; + + char *window_title, *icon_title; + bool minimised; + + /* Multi-layered colour palette. The colours from Conf (plus the + * default xterm-256 ones that don't have Conf ids at all) have + * lowest priority, followed by platform overrides if any, + * followed by escape-sequence overrides during the session. */ + struct term_subpalette { + rgb values[OSC4_NCOLOURS]; + bool present[OSC4_NCOLOURS]; + } subpalettes[3]; +#define SUBPAL_CONF 0 +#define SUBPAL_PLATFORM 1 +#define SUBPAL_SESSION 2 + + /* The composite palette that we make out of the above */ + rgb palette[OSC4_NCOLOURS]; + + unsigned winpos_x, winpos_y, winpixsize_x, winpixsize_y; + + /* + * Assorted 'pending' flags for ancillary window changes performed + * in term_update. Generally, to trigger one of these operations, + * you set the pending flag and/or the parameters here, then call + * term_schedule_update. + */ + bool win_move_pending; + int win_move_pending_x, win_move_pending_y; + bool win_resize_pending; + int win_resize_pending_w, win_resize_pending_h; + bool win_zorder_pending; + bool win_zorder_top; + bool win_minimise_pending; + bool win_minimise_enable; + bool win_maximise_pending; + bool win_maximise_enable; + bool win_title_pending, win_icon_title_pending; + bool win_pointer_shape_pending; + bool win_pointer_shape_raw; + bool win_refresh_pending; + bool win_scrollbar_update_pending; + bool win_palette_pending; + unsigned win_palette_pending_min, win_palette_pending_limit; }; static inline bool in_utf(Terminal *term) diff --git a/test/agenttest.py b/test/agenttest.py new file mode 100755 index 0000000..0e06301 --- /dev/null +++ b/test/agenttest.py @@ -0,0 +1,254 @@ +#!/usr/bin/python3 + +import sys +import os +import socket +import base64 +import itertools +import collections + +from ssh import * +import agenttestdata + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + +test_session_id = b'Test16ByteSessId' +assert len(test_session_id) == 16 +test_message_to_sign = b'test message to sign' + +TestSig2 = collections.namedtuple("TestSig2", "flags sig") + +class Key2(collections.namedtuple("Key2", "comment public sigs openssh")): + def public_only(self): + return Key2(self.comment, self.public, None, None) + + def Add(self): + alg = ssh_decode_string(self.public) + msg = (ssh_byte(SSH2_AGENTC_ADD_IDENTITY) + + ssh_string(alg) + + self.openssh + + ssh_string(self.comment)) + return agent_query(msg) + + verb = "sign" + def Use(self, flags): + msg = (ssh_byte(SSH2_AGENTC_SIGN_REQUEST) + + ssh_string(self.public) + + ssh_string(test_message_to_sign)) + if flags is not None: + msg += ssh_uint32(flags) + rsp = agent_query(msg) + t, rsp = ssh_decode_byte(rsp, True) + assert t == SSH2_AGENT_SIGN_RESPONSE + sig, rsp = ssh_decode_string(rsp, True) + assert len(rsp) == 0 + return sig + + def Del(self): + msg = (ssh_byte(SSH2_AGENTC_REMOVE_IDENTITY) + + ssh_string(self.public)) + return agent_query(msg) + + @staticmethod + def DelAll(): + msg = (ssh_byte(SSH2_AGENTC_REMOVE_ALL_IDENTITIES)) + return agent_query(msg) + + @staticmethod + def List(): + msg = (ssh_byte(SSH2_AGENTC_REQUEST_IDENTITIES)) + rsp = agent_query(msg) + t, rsp = ssh_decode_byte(rsp, True) + assert t == SSH2_AGENT_IDENTITIES_ANSWER + nk, rsp = ssh_decode_uint32(rsp, True) + keylist = [] + for _ in range(nk): + p, rsp = ssh_decode_string(rsp, True) + c, rsp = ssh_decode_string(rsp, True) + keylist.append(Key2(c, p, None, None)) + assert len(rsp) == 0 + return keylist + + @classmethod + def make_examples(cls): + cls.examples = agenttestdata.key2examples(cls, TestSig2) + + def iter_testsigs(self): + for testsig in self.sigs: + if testsig.flags == 0: + yield testsig._replace(flags=None) + yield testsig + + def iter_tests(self): + for testsig in self.iter_testsigs(): + yield ([testsig.flags], + " (flags={})".format(testsig.flags), + testsig.sig) + +class Key1(collections.namedtuple( + "Key1", "comment public challenge response private")): + def public_only(self): + return Key1(self.comment, self.public, None, None, None) + + def Add(self): + msg = (ssh_byte(SSH1_AGENTC_ADD_RSA_IDENTITY) + + self.private + + ssh_string(self.comment)) + return agent_query(msg) + + verb = "decrypt" + def Use(self, challenge): + msg = (ssh_byte(SSH1_AGENTC_RSA_CHALLENGE) + + self.public + + ssh1_mpint(challenge) + + test_session_id + + ssh_uint32(1)) + rsp = agent_query(msg) + t, rsp = ssh_decode_byte(rsp, True) + assert t == SSH1_AGENT_RSA_RESPONSE + assert len(rsp) == 16 + return rsp + + def Del(self): + msg = (ssh_byte(SSH1_AGENTC_REMOVE_RSA_IDENTITY) + + self.public) + return agent_query(msg) + + @staticmethod + def DelAll(): + msg = (ssh_byte(SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES)) + return agent_query(msg) + + @staticmethod + def List(): + msg = (ssh_byte(SSH1_AGENTC_REQUEST_RSA_IDENTITIES)) + rsp = agent_query(msg) + t, rsp = ssh_decode_byte(rsp, True) + assert t == SSH1_AGENT_RSA_IDENTITIES_ANSWER + nk, rsp = ssh_decode_uint32(rsp, True) + keylist = [] + for _ in range(nk): + b, rsp = ssh_decode_uint32(rsp, True) + e, rsp = ssh1_get_mpint(rsp, True) + m, rsp = ssh1_get_mpint(rsp, True) + c, rsp = ssh_decode_string(rsp, True) + keylist.append(Key1(c, ssh_uint32(b)+e+m, None, None, None)) + assert len(rsp) == 0 + return keylist + + @classmethod + def make_examples(cls): + cls.examples = agenttestdata.key1examples(cls) + + def iter_tests(self): + yield [self.challenge], "", self.response + +def agent_query(msg): + msg = ssh_string(msg) + s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + s.connect(os.environ["SSH_AUTH_SOCK"]) + s.send(msg) + length = ssh_decode_uint32(s.recv(4)) + assert length < AGENT_MAX_MSGLEN + return s.recv(length) + +def enumerate_bits(iterable): + return ((1<>1) + diff = new ^ old + assert diff != 0 and (diff & (diff-1)) == 0 + yield old, new, diff + old = new + assert old == 0 + +class TestRunner: + def __init__(self): + self.ok = True + + @staticmethod + def fmt_response(response): + return "'{}'".format( + base64.encodebytes(response).decode("ASCII").replace("\n","")) + + @staticmethod + def fmt_keylist(keys): + return "{{{}}}".format( + ",".join(key.comment.decode("ASCII") for key in sorted(keys))) + + def expect_success(self, text, response): + if response == ssh_byte(SSH_AGENT_SUCCESS): + print(text, "=> success") + elif response == ssh_byte(SSH_AGENT_FAILURE): + print("FAIL!", text, "=> failure") + self.ok = False + else: + print("FAIL!", text, "=>", self.fmt_response(response)) + self.ok = False + + def check_keylist(self, K, expected_keys): + keys = K.List() + print("list keys =>", self.fmt_keylist(keys)) + if set(keys) != set(expected_keys): + print("FAIL! Should have been", self.fmt_keylist(expected_keys)) + self.ok = False + + def gray_code_test(self, K): + bks = list(enumerate_bits(K.examples)) + + self.check_keylist(K, {}) + + for old, new, diff in gray_code(len(K.examples)): + bit, key = next((bit, key) for bit, key in bks if diff & bit) + + if new & bit: + self.expect_success("insert " + key.comment.decode("ASCII"), + key.Add()) + else: + self.expect_success("delete " + key.comment.decode("ASCII"), + key.Del()) + + self.check_keylist(K, [key.public_only() for bit, key in bks + if new & bit]) + + def sign_test(self, K): + for key in K.examples: + for params, message, expected_answer in key.iter_tests(): + key.Add() + actual_answer = key.Use(*params) + key.Del() + record = "{} with {}{}".format( + K.verb, key.comment.decode("ASCII"), message) + if actual_answer == expected_answer: + print(record, "=> success") + else: + print("FAIL!", record, "=> {} but expected {}".format( + self.fmt_response(actual_answer), + self.fmt_response(expected_answer))) + self.ok = False + + def run(self): + self.expect_success("init: delete all ssh2 keys", Key2.DelAll()) + + for K in [Key2, Key1]: + self.gray_code_test(K) + self.sign_test(K) + + # TODO: negative tests of all kinds. + +def main(): + Key2.make_examples() + Key1.make_examples() + + tr = TestRunner() + tr.run() + if tr.ok: + print("Test run passed") + else: + sys.exit("Test run failed!") + +if __name__ == "__main__": + main() diff --git a/test/agenttestdata.py b/test/agenttestdata.py new file mode 100644 index 0000000..596c069 --- /dev/null +++ b/test/agenttestdata.py @@ -0,0 +1,14 @@ +# DO NOT EDIT DIRECTLY! Autogenerated by agenttestgen.py +# +# To regenerate, run +# python3 agenttestgen.py > agenttestdata.py +# +# agenttestgen.py depends on the testcrypt system, so you must also +# have built testcrypt in the parent directory, or else set +# PUTTY_TESTCRYPT to point at a working implementation of it. + + +def key2examples(Key2, TestSig2): + return [Key2(comment=b'RSA-1024', public=b'\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01%\x00\x00\x00\x81\x00\x87B\x16\x99\x8c96\x92\xe7\x00-\xc5\xf0%\x13:\xe5a?_\x14\xb5\x15{%\xed\xd4\rB\x98\x02~\xb0\xfdWc\xa6\x8fSz\xa7\xfd\x94\xe1\xcegx\xe3\x14\xba\x87A4\xef\xb0\x056\x9c\x80r\x18\xd7Q\xb69\xed\x9a5\xba\x8b\xf8\xee\x84F\xceD\xfa\xccn\xd6\x9ba8\x8f\xb5\x9dz\x0b\xf1\xa3\xe9vH\x1dr\r[x\xbb\xd9\xd6\xf3\xcb~W\x8fYu\xd5|G)\xa9\xa8_\x91A\x1f\xef\x80\x83\xb3jp)\xef\xe8\x05', sigs=[TestSig2(flags=0, sig=b'\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x801\xaa\xb4t\xe10\x83Q\xe4\x18\x84\x1e\xdeN$\xde;\t\xf9\xae"H\r>\xa9\x91B"\xfd\x01\\19\xee*\xb9\xc1\x8a\x1b*:\xc3\t\x91\x85\xae\x7f\xf8\x84\x08\xbd\x89P\xa9\xdb\t\x8fc\x95\xa4\xcb\xda\x19<\x14\xc4\x1a|\n\xef)\xf4\xc8\xfb\xc1\x04"\xf6\x8a9\xac\xec\xa6x>\xd3-\xb1\xf7\x1e\x04\x8a\xd4k\xcb\x12\xf9\xc1\xaa\x1aV\xc3\xb8\xdd\xf8\x01\x10Z\xdd\x8c\xcd\x12w\x83pJOr\xb8\xed\x84\xa5\xf5&\r:\xd7H'), TestSig2(flags=2, sig=b"\x00\x00\x00\x0crsa-sha2-256\x00\x00\x00\x80J)H\xfe\x18t\xc8\xa9$\x07*\xc9\x16\xb3\\\x8cK\x7f\xdd\xd8\xb0g\xed\x10o\xac\xe8\xd3\xefQj\xe2\x9fi\x13\x9b\x93\x07`}\x12\x9f\xc1Y\x19{\xb8\xc0\x8c\xe6\x03\xfd\x8d\xc1\xfat\xf0T\x055\x02r:AOM\x18x\xb6:\xb7g\xe5k\x12$\xabX)\xf8\xe9\x12\xa2\x04\xff\xfa^\xc7 G\x9c7\x92\x03>^\x00,\x1e\x063\x16\x9b\xd4.'\x01\xa4Lv\xd2\xae\xf0\xc0\xed\x8d\xf6'aj\x1aq`\xc3\x85\x08\xc2\x8a"), TestSig2(flags=4, sig=b'\x00\x00\x00\x0crsa-sha2-512\x00\x00\x00\x80;k\xd5\xf4\xe8\xec\xeb\x8b\xe13L}\x96\x918?\xd9\x90\t\x9dN\xec+|<\xc1\xc6\x10\xf6]y\x8c\xf9a\xd5\x07c:\x90\x7fzBQ\xe49\x87s\x1d\x81Kz\xe9\xee\n\xf3|\xee6\x84A\xd0\xec\xc0\xf9h\xc5$\x13\xd8r\xa2j;\xb6$?*\xd59\xcb\xdf\x85\x19U\x1f\x10\xb4\xd8C\xbb"`\x8a)\x0fy`|\xd7\xa3\xec`tw\x8a>(\x0fj\x08\xbc\x92\xd8K\xb0qP\xbe\xd9\xaf\x91\x16\xd7\x9a\x9a\r\x9d\xe5')], openssh=b'\x00\x00\x00\x81\x00\x87B\x16\x99\x8c96\x92\xe7\x00-\xc5\xf0%\x13:\xe5a?_\x14\xb5\x15{%\xed\xd4\rB\x98\x02~\xb0\xfdWc\xa6\x8fSz\xa7\xfd\x94\xe1\xcegx\xe3\x14\xba\x87A4\xef\xb0\x056\x9c\x80r\x18\xd7Q\xb69\xed\x9a5\xba\x8b\xf8\xee\x84F\xceD\xfa\xccn\xd6\x9ba8\x8f\xb5\x9dz\x0b\xf1\xa3\xe9vH\x1dr\r[x\xbb\xd9\xd6\xf3\xcb~W\x8fYu\xd5|G)\xa9\xa8_\x91A\x1f\xef\x80\x83\xb3jp)\xef\xe8\x05\x00\x00\x00\x01%\x00\x00\x00\x80 \xe6\x8f\xe0)\x06\xffo\xd7S\x12\r\x8dp\xcdS\x83\xe78\xed\x9d@\xcd\xdf\xafG\xb0\x1e\xe6\xafZ\x8d\x84\xffZr/n\xf8\xa1Ku\x08\x89\xf3\xef\xa7\xc8\x88\x80f\x16\xc7\xaf\xec\x8b\xa5\x80\x03\x91_\xfd\x06\t_\xc5\xbf\xc1\xcb\xe3\r12k\xaeY\xe4RA\xab\xf1\xba\x8a\x99\xcf\xaf\x06\xf1\x82\xc6\x9d\xd5g[\xb2\xfb\xd8\xbc\x9b]\xb9l\t\xb2\x0b\xc9\x98JK\xfe\x8a\xd6\xfc[\xd19\xe7N|\xa6m\x9ei\xce&\xc6\xfcM\x00\x00\x00A\x00\x95\xc13\x16\xd2O*\xbc8\xc6{D\xb2\xe5\x85\x9ao\xf9\xfc.\xea\xe7\x9d\xca4}\x9c7\xca\xe1\x1e\xdb`o\x88w\x0c\xa3\xba\xde\nF\xb7\xf2x}\xd9\x00\x00\x00A\x00\xa3h\x11\x1a\x99\xc6\xa4\x0eAj\x93\xff\xa0&D(\x05#MoE\xdb\x8d\xf4\x99\xc8\xd2\xffv\xf1\x90C~\xe4\xed\xce\x1f\x85\x9f\x92\xce\xacMR\xd7\x0c\xb9z\x87\xea\xe97/\x97\xbd\x19q:TLB\xb7$\r'), Key2(comment=b'DSA-1024', public=b'\x00\x00\x00\x07ssh-dss\x00\x00\x00\x81\x00\xbc\x0c\xb0L\xfc<\x03TyZQ\xe1\xef\xd4\xd5\xe4\xa2\xb3\xaf\x14t\x0f\\,!E\xdbf\x01\x9e\x95\xddr\xeb\xab\xae;\xc1\xe3\x0c\xe9\xd9\x15\xc2\xa9\xc3g\x04\xa5\xf1\x965\xf1\x81\x9dS\x9c\x83en\x93\x11\xe0p\n)\xdaZ\x17y\xff\xf2\xbf\x9b[;"E1\xf0\xde\xbd\xe1;\x9a6Xnc\x8f\xd3\x1dg\xd1\x80\xa9\x8em\x86t\xc8\xa9!\xcd\xb3\xe4mx\xd5\x93%R\xbb9u\xd2\x99p\xe2\xbe\xf3\xfb%\xebd\xc4\x86\xe3\x00\x00\x00\x15\x00\xec^\x98\x84x\xc1\xa8`\xcfB\xc7\x1e\xf0\x8d\xd3\x89\xa3\xa8\xec\xc7\x00\x00\x00\x80Q\xecf\xfd\xcc\x9a+\xcclxu\x1b\x0b\xd7\xfd\xedDP\xd1\x82~H\x0eqn\x1e\xed\xd8\xad\xe9\xe3\xf8!\x1d\\\xb4\xde\xc1e\xd7\xc0(\x1dpQ\xee\xad\xeez\xe1\xb3\xa4\x12d\x92\x1a\x89\x82u\x99\xa3$\x85\x9c\xb840\xe7\xd6&O\x85~\xd6\xac\x1eq\xa1\x06\xa2\xd1ro\xd0}>\xc0O\xaf\x8a\xf8@B\r7\xf6\x89\xda\xd0\xb9\x0e\xb6\xae\xcdh\x1a\x86%\xdcN\x8cE\xd8\xcd(\x19\xa6y\x9a\xc0\xc2\xd6;\xc3\xc9{\x104\x00\x00\x00\x80`\xc1\xe8\x18\xe4\xd0\x16\xf9[l\xce\\L*\x19\x14"20K\xc6\x18*\xe5\x91\x80\xbf+\xec\xfe\xd3D\xf7\xa6\xf9Y0x#sl\x88BU\x7f\x1c\r\xb3\x08EL\x86\xa2\x8c\x81\x15pD\xac\x9c\xa3\x8e\x02\x89\xb9\xb6\x9f\xaa\xd0\xc8\x89o\x81Qm\x18f0\\\x92\x1f\xbf\xa1\x8d8\x8a\xb1\xec\xb8G\xbd9b\x8d\x7f\x9bk\xb9x\xe5\xde\xce/\'f\xc4\x8bmX\xd9 \xb4\xd5\xe8\xd1\xe1\xc8\xeb\xe7\xbc2LG\x90]\\%\x9f', sigs=[TestSig2(flags=0, sig=b"\x00\x00\x00\x07ssh-dss\x00\x00\x00(\xeb\xf2\xb0 2(\x93a\xfc\x0f\xad\x1al\xd5\xd0n\xd5\x10\x9d\\G\x18]?\xf4h5D\x12WL\xe6#\xa0\x89'\x17\xd3;\xb7")], openssh=b'\x00\x00\x00\x81\x00\xbc\x0c\xb0L\xfc<\x03TyZQ\xe1\xef\xd4\xd5\xe4\xa2\xb3\xaf\x14t\x0f\\,!E\xdbf\x01\x9e\x95\xddr\xeb\xab\xae;\xc1\xe3\x0c\xe9\xd9\x15\xc2\xa9\xc3g\x04\xa5\xf1\x965\xf1\x81\x9dS\x9c\x83en\x93\x11\xe0p\n)\xdaZ\x17y\xff\xf2\xbf\x9b[;"E1\xf0\xde\xbd\xe1;\x9a6Xnc\x8f\xd3\x1dg\xd1\x80\xa9\x8em\x86t\xc8\xa9!\xcd\xb3\xe4mx\xd5\x93%R\xbb9u\xd2\x99p\xe2\xbe\xf3\xfb%\xebd\xc4\x86\xe3\x00\x00\x00\x15\x00\xec^\x98\x84x\xc1\xa8`\xcfB\xc7\x1e\xf0\x8d\xd3\x89\xa3\xa8\xec\xc7\x00\x00\x00\x80Q\xecf\xfd\xcc\x9a+\xcclxu\x1b\x0b\xd7\xfd\xedDP\xd1\x82~H\x0eqn\x1e\xed\xd8\xad\xe9\xe3\xf8!\x1d\\\xb4\xde\xc1e\xd7\xc0(\x1dpQ\xee\xad\xeez\xe1\xb3\xa4\x12d\x92\x1a\x89\x82u\x99\xa3$\x85\x9c\xb840\xe7\xd6&O\x85~\xd6\xac\x1eq\xa1\x06\xa2\xd1ro\xd0}>\xc0O\xaf\x8a\xf8@B\r7\xf6\x89\xda\xd0\xb9\x0e\xb6\xae\xcdh\x1a\x86%\xdcN\x8cE\xd8\xcd(\x19\xa6y\x9a\xc0\xc2\xd6;\xc3\xc9{\x104\x00\x00\x00\x80`\xc1\xe8\x18\xe4\xd0\x16\xf9[l\xce\\L*\x19\x14"20K\xc6\x18*\xe5\x91\x80\xbf+\xec\xfe\xd3D\xf7\xa6\xf9Y0x#sl\x88BU\x7f\x1c\r\xb3\x08EL\x86\xa2\x8c\x81\x15pD\xac\x9c\xa3\x8e\x02\x89\xb9\xb6\x9f\xaa\xd0\xc8\x89o\x81Qm\x18f0\\\x92\x1f\xbf\xa1\x8d8\x8a\xb1\xec\xb8G\xbd9b\x8d\x7f\x9bk\xb9x\xe5\xde\xce/\'f\xc4\x8bmX\xd9 \xb4\xd5\xe8\xd1\xe1\xc8\xeb\xe7\xbc2LG\x90]\\%\x9f\x00\x00\x00\x15\x00\x85mio\xa4\xa2\xcc\xadnC\x94\x84I*;\xf40\xc9\xd7\xa9'), Key2(comment=b'ECDSA-p256', public=b'\x00\x00\x00\x13ecdsa-sha2-nistp256\x00\x00\x00\x08nistp256\x00\x00\x00A\x04D\x01\'\xac\xa9\xeaJ#\x1e\x80\x1e\xd2R"xq\xb2h\x128c\x11\xf5\xe8\x19,;\x1d\xcf\xf4h\x8c\xaeQ\xee\x15\xc2\xdb\xc77\x80\xc4\xc2\x15\x1d0s\xe1\xbfa\xd9}pz\xc6af4d\xbd\xc6\xc6\x1as', sigs=[TestSig2(flags=0, sig=b'\x00\x00\x00\x13ecdsa-sha2-nistp256\x00\x00\x00J\x00\x00\x00!\x00\xe0\x0b\xa1\x01\xc1\xc6A\x0c\x0c\x02\x9f\xc2B\x18G=\xfa+\xde\x8a/)x\xea\xc0R\xc0\x92\xe9\t?\xc7\x00\x00\x00!\x00\xa3\xcd^!\xa5\x0f"\xcd\x9c\x82\xe0\x01\x9c\xf1\xcaa\x83\x83\x848\xe4\xfb\xd9p]\xe1\xcc<\xdb\xde\x99\xe8')], openssh=b'\x00\x00\x00\x08nistp256\x00\x00\x00A\x04D\x01\'\xac\xa9\xeaJ#\x1e\x80\x1e\xd2R"xq\xb2h\x128c\x11\xf5\xe8\x19,;\x1d\xcf\xf4h\x8c\xaeQ\xee\x15\xc2\xdb\xc77\x80\xc4\xc2\x15\x1d0s\xe1\xbfa\xd9}pz\xc6af4d\xbd\xc6\xc6\x1as\x00\x00\x00!\x00\xc3\x8b\xc3A\xde\xfd\xd4\xcb\xf7\x9c\xa0\xc7L\xd1\xb0\xfe\x8e\xf2\xf6o\xe4"\x88K\x15p0\xbc\x0b\x19\xa7\xad'), Key2(comment=b'Ed25519', public=b'\x00\x00\x00\x0bssh-ed25519\x00\x00\x00 \xd6\x9aC\xb8\xa4\x1b\x95\x8a\x97\x9a\x9d\x95\xaa5\xd5\x9b\xc3B\xd2\xd1*\x85\xde.E"\x1c\xe9\xef\xc0\x06\xdb', sigs=[TestSig2(flags=0, sig=b'\x00\x00\x00\x0bssh-ed25519\x00\x00\x00@\xda\xcbw6\xff\xb9\xc1)\x1e\xfa\xef/s!u\xe1\xc0%\xd9-;\x97<\xbc2o\x1a\xef\x17\xd3\x8dvU\xbf\x8d,\xed\xe8S\xe8\xc27\xdb\x1d\xe7\xda\\\xce\xdc\x00\xad\x97BpC\x9a\xec\xd3\r1\x9f\xc0n\x0b')], openssh=b'\x00\x00\x00 \xd6\x9aC\xb8\xa4\x1b\x95\x8a\x97\x9a\x9d\x95\xaa5\xd5\x9b\xc3B\xd2\xd1*\x85\xde.E"\x1c\xe9\xef\xc0\x06\xdb\x00\x00\x00@\rV\xff\xf36D\xe3\xb2\xcc\xda\xa1\x11\x9d\xa2\xa7\xc0\'}C\xcb"\xf0x\rlL\xfc\xcc\x99\x10\x91\xc8\xd6\x9aC\xb8\xa4\x1b\x95\x8a\x97\x9a\x9d\x95\xaa5\xd5\x9b\xc3B\xd2\xd1*\x85\xde.E"\x1c\xe9\xef\xc0\x06\xdb')] +def key1examples(Key1): + return [Key1(comment=b'RSA-1024a', public=b"\x00\x00\x04\x00\x00\x06%\x04\x00\x98\x8d\xac\xa1\xff\xd1\x05\xd4@\x93\x11\xfc\xd8\xb5\x8c\x18\xa8/\x9ePh\x06y,\xc1\xdd\xc2q\x90\xe0g\xebIgl\x12\xacs.\xc1\xd7\xd0,\x8d\xd4\xa4\xd1\x88F\x1dW\xa6\xb9\x808+0u`B\xa8\xd2z\x0c>}\xaeA\xa7\x945\x91\x0c\xd5@5s\xa8R\xc31\xc5\x8e'\xec6\x00\x98\xdd\x0b\x93\xa8\x8e\xe6\xa9\x19\xa2\xbaf\xd6\xa8@\x1b\x82\xf4\xf5j\xc4\x06\xdd\x08\x7f\xcce\xcdc%\xc4W\xd7k\xd2\xe3\xcf\xa2\xbaI=", challenge=105855771610781217219148893240371739231586890974005582821084910669621353003219053895226458126049169949952763904000891276244889049724351186264170655451406153989624471223276671179692313150356649135789040179224142632652879598695018927369451625737003799565133274183885945180682771324880529165922297762364545903323, response=b'U\xb2\xdf(\xb8\xc7\xf5\r\x16\xa0%O9l\xdb\xf0', private=b'\x00\x00\x04\x00\x04\x00\x98\x8d\xac\xa1\xff\xd1\x05\xd4@\x93\x11\xfc\xd8\xb5\x8c\x18\xa8/\x9ePh\x06y,\xc1\xdd\xc2q\x90\xe0g\xebIgl\x12\xacs.\xc1\xd7\xd0,\x8d\xd4\xa4\xd1\x88F\x1dW\xa6\xb9\x808+0u`B\xa8\xd2z\x0c>}\xaeA\xa7\x945\x91\x0c\xd5@5s\xa8R\xc31\xc5\x8e\'\xec6\x00\x98\xdd\x0b\x93\xa8\x8e\xe6\xa9\x19\xa2\xbaf\xd6\xa8@\x1b\x82\xf4\xf5j\xc4\x06\xdd\x08\x7f\xcce\xcdc%\xc4W\xd7k\xd2\xe3\xcf\xa2\xbaI=\x00\x06%\x04\x00\x94n+m0@\xfe\xc0\xad\x88--];\x04\xd9\xb8e\xae\xca\xc6\x14"\xdfp\x84\xbd09\xef\x19\x00\x9arv\xfdi\x84\xd3\x8c+\xed$nR[,\xbb\xf11N]\x07\x83\xacE\xb2\x9b\xb7\x9a\xcd\xc5\xde\x86\xe7\xf9O\xf9\xa0\xce\xca\x085\xe7\xb7En\x94\x1e;T)SH\xff\x85\xe5\x8d\x1f\xa6,\xc7\xffV\x80\xf2E=\x08\x8e\xe6\x9e\xb2}py\xad\xa55Z\xf4\xed\xc8^+tnIN)vE\xbd\x82\xb665\xdd\x01\xfb\x04d\xa4\xb80\x98l\xd0!l\x99[\x12\x1c\x85\xda\xa5\xd0#\xcc\x7f\x80\xcdE~\x8bF \xf1;\x9d\xfb\xc0\xa3\x93\xa2\xb89^\x91D\xb7\xd3\x18\xbdx\x93U\xc3{\xa4\t,F\xb5\xdd\xadk\xc9@\xd0$\xc6\x03\x02\x00\x9a\xa4\\\xb4\xe5\x05\xe0&\xb0\x06\xc3\x9dQ`\xe4w\xe3-6N "\xa2\x9a%\xf16T\x92%\x16\xf9\xfc\xb5\xc6\xc4\xbb\xa8\xf7?\xa7"\xa1\x9do\x10A3\x14\x12\x18Uj\x19k\x19\x93\x99\xc9\xab\xfa\xa7\x15\xcb\x02\x00\xfc\x8a\xdb\xcc2\x9d[\x1a\xd0\x12\x1c\xad7\xbdk\xaa\xc6Ql\xeb7;\x87f\x8fv\xafM\x8b\xa8\xaa\n40\x90)\xb8t\tBaU\xba<\xcb\xa1\x12\xad\xaad\xb3\x0e\xf4\xfc\x07\x13;\x1c\x17]P[|\x17'), Key1(comment=b'RSA-1024b', public=b'\x00\x00\x04\x00\x00\x06%\x04\x00\x97\xe4sJ\xf8i\x83\x9f\xe8k%\xc6\xb7\xcbm!\xd7\xdd\xd5!N\xad<8\x0e\x1f\xa15yV\xbcr\xec\x8c\xca\x94\xed\x0c\xdbDC\x9e\xe1\xf5\xe4_\xb6>\x19\xe0\xdf\xb1te\xc7n\x86\xf7\xd15\x9e\xfc\x81\x90V\x92\xae\x1cb\xcc\xde\x05k\x8eNIa\x87\x1a\x8aG\xdd\xc9\xc9K\xfe\xb3W\xb0%\xe2\x10bs\x18\xe2\x07I\xf8\x88P\x04i!\xa9\xdd5\x12\x12\xdbp\x06\x03\xbb\x0e(\x82\x0e\xe7\xe7r\x17\xdbN\x91\x141Q', challenge=106369452810277819728395930691403679528939443121481728310811020449278450935158409081846069415863931371431145424909167586350456375465223628112328681772147389155179853401808803792809242283837570604791348990555643874877574733757512944970974196016255159184273573080435875970788050018918135917906633435695051286334, response=b'U\xb2\xdf(\xb8\xc7\xf5\r\x16\xa0%O9l\xdb\xf0', private=b'\x00\x00\x04\x00\x04\x00\x97\xe4sJ\xf8i\x83\x9f\xe8k%\xc6\xb7\xcbm!\xd7\xdd\xd5!N\xad<8\x0e\x1f\xa15yV\xbcr\xec\x8c\xca\x94\xed\x0c\xdbDC\x9e\xe1\xf5\xe4_\xb6>\x19\xe0\xdf\xb1te\xc7n\x86\xf7\xd15\x9e\xfc\x81\x90V\x92\xae\x1cb\xcc\xde\x05k\x8eNIa\x87\x1a\x8aG\xdd\xc9\xc9K\xfe\xb3W\xb0%\xe2\x10bs\x18\xe2\x07I\xf8\x88P\x04i!\xa9\xdd5\x12\x12\xdbp\x06\x03\xbb\x0e(\x82\x0e\xe7\xe7r\x17\xdbN\x91\x141Q\x00\x06%\x03\xfe1C,O\xaa\x83\x15\xee\xac>m\x1d\xda\xbe\x84BS\xd9>4P\xde=\x0bB\xd9\xd3kI\xf2\x9d\xfb\xc2W,\xf2\x07\xb1$\x84\xd7\xa9&\xb0\x9d\x18\x1fn\x16;\x18\x1d\xe0\x8f\xb6M\\4\xb2\x8d\xee_\xbbP\xe7 j!\xc7W\xcb\xc9\x19\nM\x90\xfe4\xe5U\xef[\xdc&A\xde\xd9\x84\x02\xdek\xec\xaf\xb4\xd0I\xaaR\xa6\xc1\x8b\xbc\x13\xf1?,\xc6{\n\x02p\xa7\'\xa1\xb9\xf8\x1f\xeb\x99\xe2\xcf\xc4%"+Mu\x9d\x02\x00\xc4\x89W\x05\x8f\xff\xabX6\x8f\x9fQ\x19\xb8\xc2\xc2Y|\xa90g\xa9\xa7\xa9\x17 \xeb\xbbSMd\xf4YW\xa7\x93\xfcn\xc3AI\x04tK\x1a\xd74`\xec]+\xd8\x91`W@\xc7\xa6G\x82\x99\xac\x8c\x9b\x02\x00\xacs\x1e6\xa5\x10\xb2\xd1\xc9|\x87\x15\xb6\xd9*\x05O\x9e\x95\xec\x1f\xac\xbc/2\xc1\xdb\xa7\x97w@\xfe?d\xb2|\xd4\x96\x02\xc8y\xdf; \x89\x0b'), Key1(comment=b'RSA-768d', public=b'\x00\x00\x03\x00\x00\x06%\x03\x00\x9e4w\xb6C\x1c\xb1\xcdV\x96\t\x14\x04T\xb5\xca\x0ct?a8\xfd-\xb1l\x83/\xc3\x95\x97\x8b \xcdZW\x15\x87G\xa8\x1d\xea(\x1d\x03V\xe8\xe8/M.\xe6\xd6\x8d,\xf3>$"R\xcciYwp*\xc7z\x0c\xc3/k\x87\xc1{4\x1bw\xf1\x00\xda~\x84\x0e\xb0)\'\x84\x9e<\xd1\x19\x18\x81\xcb\xffo', challenge=600986133602165113984107876330614577281000470334319933978738264340033662158902949574073710382789301043986608302703563989903269508211841666685953915670687064469873711668788199922957307325206107166514327102609966835942325119838536897, response=b'U\xb2\xdf(\xb8\xc7\xf5\r\x16\xa0%O9l\xdb\xf0', private=b'\x00\x00\x03\x00\x03\x00\x9e4w\xb6C\x1c\xb1\xcdV\x96\t\x14\x04T\xb5\xca\x0ct?a8\xfd-\xb1l\x83/\xc3\x95\x97\x8b \xcdZW\x15\x87G\xa8\x1d\xea(\x1d\x03V\xe8\xe8/M.\xe6\xd6\x8d,\xf3>$"R\xcciYwp*\xc7z\x0c\xc3/k\x87\xc1{4\x1bw\xf1\x00\xda~\x84\x0e\xb0)\'\x84\x9e<\xd1\x19\x18\x81\xcb\xffo\x00\x06%\x02\xfe"4\xdb\x9d\x07\x97\x80c\xbf\xb1\xbc\xc6\x0e\xc65$\xc4l)a!\x14%\x8e%L\xcc\x0e\x9c\xe2~\xf2U\xea\x04\xfd\xbcb\x856\xe6\x856\xb4\x9d+px\x97\x0c\x17\xde\x93\xd8zOAO\xea\xab\xa2p\x14\x87\xf5\x1c\x00\xb2\xa3A\x08\x14\xe9v\xb8\xd2\xbf\x03C\xf2\xa3\xfeV/BZ\x11\x82#\xff)\xf8\x93\x0f\xf0m\x01\x80\x85\xde\xf4\x1b\xd2Pu\xf3\xd8\xf8Z\xabZ\x92q\xef\xab\x06\x85\xcd\xc3\x85\xd6?j\xd4\xaa\x96l\xeeRZ\xab8\x05\x84xT\xdd\x15j\xea9O_\xf4`R\x01\x80\xc4\xeb\x12\x92\xdc\x05\\b\x8c\xec\x11\x10\xc5\xaa\xdf\x97\x1eD\x92\x06_\xb3\xc28C\xceH\xa7\x8a\xf2F\xe6+\x01\xbf\xad\x81\x99&2\xcc\xff\xa2\xaad\x04\x08\x8d\x01\x80\xcd\xab\xe5\xdeE^a-\t$\xa4a\xd4h8\xe4>\xe1d\xcc0n\xe3\xee\xc5\xe7\xd4\xa59\x8f\x9f\xb2\x1d\n\x00h\x14\xad\xcdq\x89UTPu\x9e>\xeb')] diff --git a/test/agenttestgen.py b/test/agenttestgen.py new file mode 100755 index 0000000..1eef296 --- /dev/null +++ b/test/agenttestgen.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 + +import sys + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + +def generate(): + import hashlib + + print("""\ +# DO NOT EDIT DIRECTLY! Autogenerated by agenttestgen.py +# +# To regenerate, run +# python3 agenttestgen.py > agenttestdata.py +# +# agenttestgen.py depends on the testcrypt system, so you must also +# have built testcrypt in the parent directory, or else set +# PUTTY_TESTCRYPT to point at a working implementation of it. + +""") + + from testcrypt import (rsa_generate, dsa_generate, ecdsa_generate, + eddsa_generate, random_clear, random_queue, + ssh_key_public_blob, ssh_key_openssh_blob, + ssh_key_sign, rsa1_generate, rsa_ssh1_encrypt, + rsa_ssh1_public_blob, rsa_ssh1_private_blob_agent, + mp_from_bytes_be) + from agenttest import (Key2, TestSig2, test_message_to_sign, + Key1, test_session_id) + import ssh + + keygen2 = [ + ('RSA-1024', lambda: rsa_generate(1024, False), + (ssh.SSH_AGENT_RSA_SHA2_256, ssh.SSH_AGENT_RSA_SHA2_512)), + ('DSA-1024', lambda: dsa_generate(1024)), + ('ECDSA-p256', lambda: ecdsa_generate(256)), + ('Ed25519', lambda: eddsa_generate(256)), + ] + + keys2 = [] + + for record in keygen2: + if len(record) == 2: + record += ((),) + comment, genfn, flaglist = record + flaglist = (0,) + flaglist + + random_clear() + random_queue(b''.join(hashlib.sha512('{}{:d}'.format(comment, j) + .encode('ASCII')).digest() + for j in range(1000))) + key = genfn() + sigs = [TestSig2(flags, ssh_key_sign(key, test_message_to_sign, flags)) + for flags in flaglist] + + keys2.append(Key2(comment.encode("ASCII"), + ssh_key_public_blob(key), + sigs, + ssh_key_openssh_blob(key))) + + print("def key2examples(Key2, TestSig2):\n return {!r}".format(keys2)) + + keygen1 = [ + ('RSA-1024a', 1024), + ('RSA-1024b', 1024), + ('RSA-768c', 768), + ('RSA-768d', 768), + ] + + keys1 = [] + + for comment, bits in keygen1: + random_clear() + random_queue(b''.join(hashlib.sha512('{}{:d}'.format(comment, j) + .encode('ASCII')).digest() + for j in range(1000))) + key = rsa1_generate(bits) + preimage = b'Test128BitRSA1ChallengeCleartext' + assert len(preimage) == 32 + challenge_bytes = rsa_ssh1_encrypt(preimage, key) + assert len(challenge_bytes) > 0 + challenge = int(mp_from_bytes_be(challenge_bytes)) + response = hashlib.md5(preimage + test_session_id).digest() + + keys1.append(Key1(comment.encode("ASCII"), + rsa_ssh1_public_blob(key, 'exponent_first'), + challenge, response, + rsa_ssh1_private_blob_agent(key))) + + print("def key1examples(Key1):\n return {!r}".format(keys1)) + +if __name__ == "__main__": + generate() diff --git a/test/cryptsuite.py b/test/cryptsuite.py index 7bc4305..757de67 100755 --- a/test/cryptsuite.py +++ b/test/cryptsuite.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import sys import unittest import struct import itertools @@ -8,6 +9,7 @@ import hashlib import binascii import base64 +import json try: from math import gcd except ImportError: @@ -15,41 +17,18 @@ from eccref import * from testcrypt import * +from ssh import * + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" try: base64decode = base64.decodebytes except AttributeError: base64decode = base64.decodestring -def nbits(n): - # Mimic mp_get_nbits for ordinary Python integers. - assert 0 <= n - smax = next(s for s in itertools.count() if (n >> (1 << s)) == 0) - toret = 0 - for shift in reversed([1 << s for s in range(smax)]): - if n >> shift != 0: - n >>= shift - toret += shift - assert n <= 1 - if n == 1: - toret += 1 - return toret - def unhex(s): return binascii.unhexlify(s.replace(" ", "").replace("\n", "")) -def ssh_uint32(n): - return struct.pack(">L", n) -def ssh_string(s): - return ssh_uint32(len(s)) + s -def ssh1_mpint(x): - bits = nbits(x) - bytevals = [0xFF & (x >> (8*n)) for n in range((bits-1)//8, -1, -1)] - return struct.pack(">H" + "B" * len(bytevals), bits, *bytevals) -def ssh2_mpint(x): - bytevals = [0xFF & (x >> (8*n)) for n in range(nbits(x)//8, -1, -1)] - return struct.pack(">L" + "B" * len(bytevals), len(bytevals), *bytevals) - def rsa_bare(e, n): rsa = rsa_new() get_rsa_ssh1_pub(ssh_uint32(nbits(n)) + ssh1_mpint(e) + ssh1_mpint(n), @@ -111,11 +90,15 @@ def last(iterable): pass return toret +def le_integer(x, nbits): + assert nbits % 8 == 0 + return bytes([0xFF & (x >> (8*n)) for n in range(nbits//8)]) + @contextlib.contextmanager def queued_random_data(nbytes, seed): hashsize = 512 // 8 data = b''.join( - hashlib.sha512(unicode_to_bytes("preimage:{:d}:{}".format(i, seed))) + hashlib.sha512("preimage:{:d}:{}".format(i, seed).encode('ascii')) .digest() for i in range((nbytes + hashsize - 1) // hashsize)) data = data[:nbytes] random_queue(data) @@ -128,6 +111,12 @@ def queued_specific_random_data(data): yield None random_clear() +@contextlib.contextmanager +def random_prng(seed): + random_make_prng('sha256', seed) + yield None + random_clear() + def hash_str(alg, message): h = ssh_hash_new(alg) ssh_hash_update(h, message) @@ -149,6 +138,9 @@ def mac_str(alg, key, message, cipher=None): ssh2_mac_update(m, message) return ssh2_mac_genresult(m) +def lcm(a, b): + return a * b // gcd(a, b) + class MyTestBase(unittest.TestCase): "Intermediate class that adds useful helper methods." def assertEqualBin(self, x, y): @@ -215,9 +207,9 @@ def checkHex(hexstr): n = mp_from_hex(hexstr) i = int(hexstr, 16) self.assertEqual(mp_get_hex(n), - unicode_to_bytes("{:x}".format(i))) + "{:x}".format(i).encode('ascii')) self.assertEqual(mp_get_hex_uppercase(n), - unicode_to_bytes("{:X}".format(i))) + "{:X}".format(i).encode('ascii')) checkHex("0") checkHex("f") checkHex("00000000000000000000000000000000000000000000000000") @@ -228,7 +220,7 @@ def checkDec(hexstr): n = mp_from_hex(hexstr) i = int(hexstr, 16) self.assertEqual(mp_get_decimal(n), - unicode_to_bytes("{:d}".format(i))) + "{:d}".format(i).encode('ascii')) checkDec("0") checkDec("f") checkDec("00000000000000000000000000000000000000000000000000") @@ -277,6 +269,19 @@ def testComparison(self): mp_max_into(am_big, am, bm) self.assertEqual(int(am_big), max(ai, bi)) + # Test mp_{eq,hs}_integer in the case where the integer is as + # large as possible and the bignum contains very few words. In + # modes where BIGNUM_INT_BITS < 64, this used to go wrong. + mp10 = mp_new(4) + mp_copy_integer_into(mp10, 10) + highbit = 1 << 63 + self.assertEqual(mp_hs_integer(mp10, highbit | 9), 0) + self.assertEqual(mp_hs_integer(mp10, highbit | 10), 0) + self.assertEqual(mp_hs_integer(mp10, highbit | 11), 0) + self.assertEqual(mp_eq_integer(mp10, highbit | 9), 0) + self.assertEqual(mp_eq_integer(mp10, highbit | 10), 0) + self.assertEqual(mp_eq_integer(mp10, highbit | 11), 0) + def testConditionals(self): testnumbers = [(mp_copy(n),n) for n in fibonacci_scattered()] for am, ai in testnumbers: @@ -360,6 +365,24 @@ def testBasicArithmetic(self): bm = mp_copy(bi) self.assertEqual(int(mp_mul(am, bm)), ai * bi) + def testAddInteger(self): + initial = mp_copy(4444444444444444444444444) + + x = mp_new(mp_max_bits(initial) + 64) + + # mp_{add,sub,copy}_integer_into should be able to cope with + # any uintmax_t. Test a number that requires more than 32 bits. + mp_add_integer_into(x, initial, 123123123123123) + self.assertEqual(int(x), 4444444444567567567567567) + mp_sub_integer_into(x, initial, 123123123123123) + self.assertEqual(int(x), 4444444444321321321321321) + mp_copy_integer_into(x, 123123123123123) + self.assertEqual(int(x), 123123123123123) + + # mp_mul_integer_into only takes a uint16_t integer input + mp_mul_integer_into(x, initial, 10001) + self.assertEqual(int(x), 44448888888888888888888884444) + def testDivision(self): divisors = [1, 2, 3, 2**16+1, 2**32-1, 2**32+1, 2**128-159, 141421356237309504880168872420969807856967187537694807] @@ -379,6 +402,41 @@ def testDivision(self): self.assertEqual(int(mp_div(n, d)), q) self.assertEqual(int(mp_mod(n, d)), r) + # Make sure divmod_into can handle not getting one + # of its output pointers (or even both). + mp_clear(mq) + mp_divmod_into(n, d, mq, None) + self.assertEqual(int(mq), q) + mp_clear(mr) + mp_divmod_into(n, d, None, mr) + self.assertEqual(int(mr), r) + mp_divmod_into(n, d, None, None) + # No tests we can do after that last one - we just + # insist that it isn't allowed to have crashed! + + def testNthRoot(self): + roots = [1, 13, 1234567654321, + 57721566490153286060651209008240243104215933593992] + tests = [] + tests.append((0, 2, 0, 0)) + tests.append((0, 3, 0, 0)) + for r in roots: + for n in 2, 3, 5: + tests.append((r**n, n, r, 0)) + tests.append((r**n+1, n, r, 1)) + tests.append((r**n-1, n, r-1, r**n - (r-1)**n - 1)) + for x, n, eroot, eremainder in tests: + with self.subTest(x=x): + mx = mp_copy(x) + remainder = mp_copy(mx) + root = mp_nthroot(x, n, remainder) + self.assertEqual(int(root), eroot) + self.assertEqual(int(remainder), eremainder) + self.assertEqual(int(mp_nthroot(2*10**100, 2, None)), + 141421356237309504880168872420969807856967187537694) + self.assertEqual(int(mp_nthroot(3*10**150, 3, None)), + 144224957030740838232163831078010958839186925349935) + def testBitwise(self): p = 0x3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e e = 0x2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190 @@ -434,6 +492,48 @@ def testInversion(self): int(monty_invert(mc, monty_import(mc, x))), int(monty_import(mc, inv))) + def testGCD(self): + powerpairs = [(0,0), (1,0), (1,1), (2,1), (2,2), (75,3), (17,23)] + for a2, b2 in powerpairs: + for a3, b3 in powerpairs: + for a5, b5 in powerpairs: + a = 2**a2 * 3**a3 * 5**a5 * 17 * 19 * 23 + b = 2**b2 * 3**b3 * 5**b5 * 65423 + d = 2**min(a2, b2) * 3**min(a3, b3) * 5**min(a5, b5) + + ma = mp_copy(a) + mb = mp_copy(b) + + self.assertEqual(int(mp_gcd(ma, mb)), d) + + md = mp_new(nbits(d)) + mA = mp_new(nbits(b)) + mB = mp_new(nbits(a)) + mp_gcd_into(ma, mb, md, mA, mB) + self.assertEqual(int(md), d) + A = int(mA) + B = int(mB) + self.assertEqual(a*A - b*B, d) + self.assertTrue(0 <= A < b//d) + self.assertTrue(0 <= B < a//d) + + self.assertEqual(mp_coprime(ma, mb), 1 if d==1 else 0) + + # Make sure gcd_into can handle not getting some + # of its output pointers. + mp_clear(md) + mp_gcd_into(ma, mb, md, None, None) + self.assertEqual(int(md), d) + mp_clear(mA) + mp_gcd_into(ma, mb, None, mA, None) + self.assertEqual(int(mA), A) + mp_clear(mB) + mp_gcd_into(ma, mb, None, None, mB) + self.assertEqual(int(mB), B) + mp_gcd_into(ma, mb, None, None, None) + # No tests we can do after that last one - we just + # insist that it isn't allowed to have crashed! + def testMonty(self): moduli = [5, 19, 2**16+1, 2**31-1, 2**128-159, 2**255-19, 293828847201107461142630006802421204703, @@ -571,10 +671,20 @@ def testShifts(self): mp_lshift_fixed_into(mp, mp, i) self.assertEqual(int(mp), (x << i) & mp_mask(mp)) + mp_copy_into(mp, x) + mp_lshift_safe_into(mp, mp, i) + self.assertEqual(int(mp), (x << i) & mp_mask(mp)) + mp_copy_into(mp, x) mp_rshift_fixed_into(mp, mp, i) self.assertEqual(int(mp), x >> i) + + mp_copy_into(mp, x) + mp_rshift_safe_into(mp, mp, i) + self.assertEqual(int(mp), x >> i) + self.assertEqual(int(mp_rshift_fixed(x, i)), x >> i) + self.assertEqual(int(mp_rshift_safe(x, i)), x >> i) def testRandom(self): @@ -705,6 +815,12 @@ def check_point(mp, rp): check_point(ecc_montgomery_double(mP), rP + rP) check_point(ecc_montgomery_double(mQ), rQ + rQ) + zero = ecc_montgomery_point_new(mc, 0) + self.assertEqual(ecc_montgomery_is_identity(zero), False) + identity = ecc_montgomery_double(zero) + ecc_montgomery_get_affine(identity) + self.assertEqual(ecc_montgomery_is_identity(identity), True) + def testEdwardsSimple(self): p, d, a = 3141592661, 2688750488, 367934288 @@ -803,6 +919,224 @@ def testEdwardsMultiply(self): self.assertEqual(int(x), int(rGi.x)) self.assertEqual(int(y), int(rGi.y)) +class keygen(MyTestBase): + def testPrimeCandidateSource(self): + def inspect(pcs): + # Returns (pcs->limit, pcs->factor, pcs->addend) as Python integers + return tuple(map(int, pcs_inspect(pcs))) + + # Test accumulating modular congruence requirements, by + # inspecting the internal values computed during + # require_residue. We ensure that the addend satisfies all our + # congruences and the factor is the lcm of all the moduli + # (hence, the arithmetic progression defined by those + # parameters is precisely the set of integers satisfying the + # requirements); we also ensure that the limiting values + # (addend itself at the low end, and addend + (limit-1) * + # factor at the high end) are the maximal subsequence of that + # progression that are within the originally specified range. + + def check(pcs, lo, hi, mod_res_pairs): + limit, factor, addend = inspect(pcs) + + for mod, res in mod_res_pairs: + self.assertEqual(addend % mod, res % mod) + + self.assertEqual(factor, functools.reduce( + lcm, [mod for mod, res in mod_res_pairs])) + + self.assertFalse(lo <= addend + (-1) * factor < hi) + self.assertTrue (lo <= addend < hi) + self.assertTrue (lo <= addend + (limit-1) * factor < hi) + self.assertFalse(lo <= addend + limit * factor < hi) + + pcs = pcs_new(64) + check(pcs, 2**63, 2**64, [(2, 1)]) + pcs_require_residue(pcs, 3, 2) + check(pcs, 2**63, 2**64, [(2, 1), (3, 2)]) + pcs_require_residue_1(pcs, 7) + check(pcs, 2**63, 2**64, [(2, 1), (3, 2), (7, 1)]) + pcs_require_residue(pcs, 16, 7) + check(pcs, 2**63, 2**64, [(2, 1), (3, 2), (7, 1), (16, 7)]) + pcs_require_residue(pcs, 49, 8) + check(pcs, 2**63, 2**64, [(2, 1), (3, 2), (7, 1), (16, 7), (49, 8)]) + + # Now test-generate some actual values, and ensure they + # satisfy all the congruences, and also avoid one residue mod + # 5 that we told them to. Also, give a nontrivial range. + pcs = pcs_new_with_firstbits(64, 0xAB, 8) + pcs_require_residue(pcs, 0x100, 0xCD) + pcs_require_residue_1(pcs, 65537) + pcs_avoid_residue_small(pcs, 5, 3) + pcs_ready(pcs) + with random_prng("test seed"): + for i in range(100): + n = int(pcs_generate(pcs)) + self.assertTrue((0xAB<<56) < n < (0xAC<<56)) + self.assertEqual(n % 0x100, 0xCD) + self.assertEqual(n % 65537, 1) + self.assertNotEqual(n % 5, 3) + + # I'm not actually testing here that the outputs of + # pcs_generate are non-multiples of _all_ primes up to + # 2^16. But checking this many for 100 turns is enough + # to be pretty sure. (If you take the product of + # (1-1/p) over all p in the list below, you find that + # a given random number has about a 13% chance of + # avoiding being a multiple of any of them. So 100 + # trials without a mistake gives you 0.13^100 < 10^-88 + # as the probability of it happening by chance. More + # likely the code is actually working :-) + + for p in [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61]: + self.assertNotEqual(n % p, 0) + + def testPocklePositive(self): + def add_small(po, *ps): + for p in ps: + self.assertEqual(pockle_add_small_prime(po, p), 'POCKLE_OK') + def add(po, *args): + self.assertEqual(pockle_add_prime(po, *args), 'POCKLE_OK') + + # Transcription of the proof that 2^130-5 is prime from + # Theorem 3.1 from http://cr.yp.to/mac/poly1305-20050329.pdf + po = pockle_new() + p1 = (2**130 - 6) // 1517314646 + p2 = (p1 - 1) // 222890620702 + add_small(po, 37003, 221101) + add(po, p2, [37003, 221101], 2) + add(po, p1, [p2], 2) + add(po, 2**130 - 5, [p1], 2) + + # My own proof that 2^255-19 is prime + po = pockle_new() + p1 = 8574133 + p2 = 1919519569386763 + p3 = 75445702479781427272750846543864801 + p4 = (2**255 - 20) // (65147*12) + p = 2**255 - 19 + add_small(po, p1) + add(po, p2, [p1], 2) + add(po, p3, [p2], 2) + add(po, p4, [p3], 2) + add(po, p, [p4], 2) + + # And the prime used in Ed448, while I'm here + po = pockle_new() + p1 = 379979 + p2 = 1764234391 + p3 = 97859369123353 + p4 = 34741861125639557 + p5 = 36131535570665139281 + p6 = 167773885276849215533569 + p7 = 596242599987116128415063 + p = 2**448 - 2**224 - 1 + add_small(po, p1, p2) + add(po, p3, [p1], 2) + add(po, p4, [p2], 2) + add(po, p5, [p4], 2) + add(po, p6, [p3], 3) + add(po, p7, [p5], 3) + add(po, p, [p6, p7], 2) + + p = 4095744004479977 + factors = [2, 79999] # just enough factors to exceed cbrt(p) + po = pockle_new() + for q in factors: + add_small(po, q) + add(po, p, factors, 3) + + # The order of the generator in Ed25519 + po = pockle_new() + p1a, p1b = 132667, 137849 + p2 = 3044861653679985063343 + p3 = 198211423230930754013084525763697 + p = 2**252 + 0x14def9dea2f79cd65812631a5cf5d3ed + add_small(po, p1a, p1b) + add(po, p2, [p1a, p1b], 2) + add(po, p3, [p2], 2) + add(po, p, [p3], 2) + + # And the one in Ed448 + po = pockle_new() + p1 = 766223 + p2 = 3009341 + p3 = 7156907 + p4 = 671065561 + p5 = 342682509629 + p6 = 6730519843040614479184435237013 + p = 2**446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d + add_small(po, p1, p2, p3, p4) + add(po, p5, [p1], 2) + add(po, p6, [p3,p4], 2) + add(po, p, [p2,p5,p6], 2) + + def testPockleNegative(self): + def add_small(po, p): + self.assertEqual(pockle_add_small_prime(po, p), 'POCKLE_OK') + + po = pockle_new() + self.assertEqual(pockle_add_small_prime(po, 0), + 'POCKLE_PRIME_SMALLER_THAN_2') + self.assertEqual(pockle_add_small_prime(po, 1), + 'POCKLE_PRIME_SMALLER_THAN_2') + self.assertEqual(pockle_add_small_prime(po, 2**61 - 1), + 'POCKLE_SMALL_PRIME_NOT_SMALL') + self.assertEqual(pockle_add_small_prime(po, 4), + 'POCKLE_SMALL_PRIME_NOT_PRIME') + + po = pockle_new() + self.assertEqual(pockle_add_prime(po, 1919519569386763, [8574133], 2), + 'POCKLE_FACTOR_NOT_KNOWN_PRIME') + + po = pockle_new() + add_small(po, 8574133) + self.assertEqual(pockle_add_prime(po, 1919519569386765, [8574133], 2), + 'POCKLE_FACTOR_NOT_A_FACTOR') + + p = 4095744004479977 + factors = [2, 79997] # not quite enough factors to reach cbrt(p) + po = pockle_new() + for q in factors: + add_small(po, q) + self.assertEqual(pockle_add_prime(po, p, factors, 3), + 'POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL') + + p = 1999527 * 3999053 + factors = [999763] + po = pockle_new() + for q in factors: + add_small(po, q) + self.assertEqual(pockle_add_prime(po, p, factors, 3), + 'POCKLE_DISCRIMINANT_IS_SQUARE') + + p = 9999929 * 9999931 + factors = [257, 2593] + po = pockle_new() + for q in factors: + add_small(po, q) + self.assertEqual(pockle_add_prime(po, p, factors, 3), + 'POCKLE_FERMAT_TEST_FAILED') + + p = 1713000920401 # a Carmichael number + po = pockle_new() + add_small(po, 561787) + self.assertEqual(pockle_add_prime(po, p, [561787], 2), + 'POCKLE_WITNESS_POWER_IS_1') + + p = 4294971121 + factors = [3, 5, 11, 17] + po = pockle_new() + for q in factors: + add_small(po, q) + self.assertEqual(pockle_add_prime(po, p, factors, 17), + 'POCKLE_WITNESS_POWER_NOT_COPRIME') + + po = pockle_new() + add_small(po, 2) + self.assertEqual(pockle_add_prime(po, 1, [2], 1), + 'POCKLE_PRIME_SMALLER_THAN_2') + class crypt(MyTestBase): def testSSH1Fingerprint(self): # Example key and reference fingerprint value generated by @@ -812,6 +1146,36 @@ def testSSH1Fingerprint(self): self.assertEqual( fp, b"768 96:12:c8:bc:e6:03:75:86:e8:c7:b9:af:d8:0c:15:75") + def testSSH2Fingerprints(self): + # A sensible key blob that we can make sense of. + sensible_blob = base64.decodebytes( + b'AAAAC3NzaC1lZDI1NTE5AAAAICWiV0VAD4lQ7taUN7vZ5Rkc' + b'SLJBW5ubn6ZINwCOzpn3') + self.assertEqual(ssh2_fingerprint_blob(sensible_blob, "sha256"), + b'ssh-ed25519 255 SHA256:' + b'E4VmaHW0sUF7SUgSEOmMJ8WBtt0e/j3zbsKvyqfFnu4') + self.assertEqual(ssh2_fingerprint_blob(sensible_blob, "md5"), + b'ssh-ed25519 255 ' + b'35:73:80:df:a3:2c:1a:f2:2c:a6:5c:84:ce:48:6a:7e') + + # A key blob with an unknown algorithm name, so that we can't + # extract the bit count. + silly_blob = ssh_string(b'foo') + ssh_string(b'key data') + self.assertEqual(ssh2_fingerprint_blob(silly_blob, "sha256"), + b'foo SHA256:' + b'mvfJTB4PaRI7hxYaYwn0sH8G6zW1HbLkbWnZE2YIKc4') + self.assertEqual(ssh2_fingerprint_blob(silly_blob, "md5"), + b'foo ' + b'5f:5f:97:94:97:be:01:5c:f6:3f:e3:6e:55:46:ea:52') + + # A key blob without even a valid algorithm-name string at the start. + very_silly_blob = b'foo' + self.assertEqual(ssh2_fingerprint_blob(very_silly_blob, "sha256"), + b'SHA256:' + b'LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564') + self.assertEqual(ssh2_fingerprint_blob(very_silly_blob, "md5"), + b'ac:bd:18:db:4c:c2:f8:5c:ed:ef:65:4f:cc:c4:a4:d8') + def testAES(self): # My own test cases, generated by a mostly independent # reference implementation of AES in Python. ('Mostly' @@ -1077,11 +1441,20 @@ def testAuxEncryptFns(self): p = b'three AES blocks, or six DES, of arbitrary input' k = b'thirty-two-byte aes-256 test key' + iv = b'\0' * 16 c = unhex('7b112d00c0fc95bc13fcdacfd43281bf' 'de9389db1bbcfde79d59a303d41fd2eb' '0955c9477ae4ee3a4d6c1fbe474c0ef6') - self.assertEqualBin(aes256_encrypt_pubkey(k, p), c) - self.assertEqualBin(aes256_decrypt_pubkey(k, c), p) + self.assertEqualBin(aes256_encrypt_pubkey(k, iv, p), c) + self.assertEqualBin(aes256_decrypt_pubkey(k, iv, c), p) + + # same k as in the previous case + iv = unhex('0102030405060708090a0b0c0d0e0f10') + c = unhex('9e9c8a91b739677b834397bdd8e70c05' + 'c3e2cf6cce68d376d798a59848621c6d' + '42b9e7101260a438daadd7b742875a36') + self.assertEqualBin(aes256_encrypt_pubkey(k, iv, p), c) + self.assertEqualBin(aes256_decrypt_pubkey(k, iv, c), p) k = b'3des with keys distinct.' iv = b'randomIV' @@ -1174,11 +1547,118 @@ def testSSHCiphers(self): ssh_cipher_decrypt(cipher, iv[:ivlen]) self.assertEqualBin(ssh_cipher_decrypt(cipher, c), p) + def testRSAKex(self): + # Round-trip test of the RSA key exchange functions, plus a + # hardcoded plain/ciphertext pair to guard against the + # behaviour accidentally changing. + def blobs(n, e, d, p, q, iqmp): + # For RSA kex, the public blob is formatted exactly like + # any other SSH-2 RSA public key. But there's no private + # key blob format defined by the protocol, so for the + # purposes of making a test RSA private key, we borrow the + # function we already had that decodes one out of the wire + # format used in the SSH-1 agent protocol. + pubblob = ssh_string(b"ssh-rsa") + ssh2_mpint(e) + ssh2_mpint(n) + privblob = (ssh_uint32(nbits(n)) + ssh1_mpint(n) + ssh1_mpint(e) + + ssh1_mpint(d) + ssh1_mpint(iqmp) + + ssh1_mpint(q) + ssh1_mpint(p)) + return pubblob, privblob + + # Parameters for a test key. + p = 0xf49e4d21c1ec3d1c20dc8656cc29aadb2644a12c98ed6c81a6161839d20d398d + q = 0xa5f0bc464bf23c4c83cf17a2f396b15136fbe205c07cb3bb3bdb7ed357d1cd13 + n = p*q + e = 37 + d = int(mp_invert(e, (p-1)*(q-1))) + iqmp = int(mp_invert(q, p)) + assert iqmp * q % p == 1 + assert d * e % (p-1) == 1 + assert d * e % (q-1) == 1 + + pubblob, privblob = blobs(n, e, d, p, q, iqmp) + + pubkey = ssh_rsakex_newkey(pubblob) + privkey = get_rsa_ssh1_priv_agent(privblob) + + plain = 0x123456789abcdef + hashalg = 'md5' + with queued_random_data(64, "rsakex encrypt test"): + cipher = ssh_rsakex_encrypt(pubkey, hashalg, ssh2_mpint(plain)) + decoded = ssh_rsakex_decrypt(privkey, hashalg, cipher) + self.assertEqual(int(decoded), plain) + self.assertEqualBin(cipher, unhex( + '34277d1060dc0a434d98b4239de9cec59902a4a7d17a763587cdf8c25d57f51a' + '7964541892e7511798e61dd78429358f4d6a887a50d2c5ebccf0e04f48fc665c' + )) + + def testMontgomeryKexLowOrderPoints(self): + # List of all the bad input values for Curve25519 which can + # end up generating a zero output key. You can find the first + # five (the ones in canonical representation, i.e. in + # [0,2^255-19)) by running + # find_montgomery_power2_order_x_values(curve25519.p, curve25519.a) + # and then encoding the results little-endian. + bad_keys_25519 = [ + "0000000000000000000000000000000000000000000000000000000000000000", + "0100000000000000000000000000000000000000000000000000000000000000", + "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157", + "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800", + "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + + # Input values less than 2^255 are reduced mod p, so those + # of the above values which are still in that range when + # you add 2^255-19 to them should also be caught. + "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + + # Input values are reduced mod 2^255 before reducing mod + # p. So setting the high-order bit of any of the above 7 + # values should also lead to rejection, because it will be + # stripped off and then the value will be recognised as + # one of the above. + "0000000000000000000000000000000000000000000000000000000000000080", + "0100000000000000000000000000000000000000000000000000000000000080", + "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7", + "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880", + "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ] + + # Same for Curve448, found by the analogous eccref function call + # find_montgomery_power2_order_x_values(curve448.p, curve448.a) + bad_keys_448 = [ + # The first three are the bad values in canonical + # representationm. In Curve448 these are just 0, 1 and -1. + '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + '0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', + 'fefffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff', + + # As with Curve25519, we must also include values in + # non-canonical representation that reduce to one of the + # above mod p. + 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffff', + '00000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff' + + # But that's all, because Curve448 fits neatly into a + # whole number of bytes, so there's no secondary reduction + # mod a power of 2. + ] + + with random_prng("doesn't matter"): + ecdh25519 = ssh_ecdhkex_newkey('curve25519') + ecdh448 = ssh_ecdhkex_newkey('curve448') + for pub in bad_keys_25519: + key = ssh_ecdhkex_getkey(ecdh25519, unhex(pub)) + self.assertEqual(key, None) + for pub in bad_keys_448: + key = ssh_ecdhkex_getkey(ecdh448, unhex(pub)) + self.assertEqual(key, None) + def testPRNG(self): hashalg = 'sha256' seed = b"hello, world" entropy = b'1234567890' * 100 - rev = lambda s: valbytes(reversed(bytevals(s))) # Replicate the generation of some random numbers. to ensure # they really are the hashes of what they're supposed to be. @@ -1191,23 +1671,25 @@ def testPRNG(self): prng_add_entropy(pr, 0, entropy) # forces a reseed data3 = prng_read(pr, 128) + le128 = lambda x: le_integer(x, 128) + key1 = hash_str(hashalg, b'R' + seed) expected_data1 = b''.join( - rev(hash_str(hashalg, key1 + b'G' + ssh2_mpint(counter))) + hash_str(hashalg, key1 + b'G' + le128(counter)) for counter in range(4)) # After prng_read finishes, we expect the PRNG to have # automatically reseeded itself, so that if its internal state # is revealed then the previous output can't be reconstructed. key2 = hash_str(hashalg, key1 + b'R') expected_data2 = b''.join( - rev(hash_str(hashalg, key2 + b'G' + ssh2_mpint(counter))) + hash_str(hashalg, key2 + b'G' + le128(counter)) for counter in range(4,8)) # There will have been another reseed after the second # prng_read, and then another due to the entropy. key3 = hash_str(hashalg, key2 + b'R') key4 = hash_str(hashalg, key3 + b'R' + hash_str(hashalg, entropy)) expected_data3 = b''.join( - rev(hash_str(hashalg, key4 + b'G' + ssh2_mpint(counter))) + hash_str(hashalg, key4 + b'G' + le128(counter)) for counter in range(8,12)) self.assertEqualBin(data1, expected_data1) @@ -1276,6 +1758,228 @@ def testDSA(self): self.assertFalse(ssh_key_verify(pubkey, badsig0, "hello, again")) self.assertFalse(ssh_key_verify(pubkey, badsigq, "hello, again")) + def testBLAKE2b(self): + # The standard test vectors for BLAKE2b (in the separate class + # below) don't satisfy me because they only test one hash + # size. These additional tests exercise BLAKE2b's configurable + # output length. The expected results are derived from the + # BLAKE2 reference implementation. + + def b2_with_len(data, length): + h = blake2b_new_general(length) + h.update(data) + return h.digest()[:length] + + self.assertEqualBin(b2_with_len(b'hello', 1), unhex("29")) + self.assertEqualBin(b2_with_len(b'hello', 2), unhex("accd")) + self.assertEqualBin(b2_with_len(b'hello', 3), unhex("980032")) + self.assertEqualBin(b2_with_len(b'hello', 5), unhex("9baecc38f2")) + self.assertEqualBin(b2_with_len(b'hello', 8), unhex( + "a7b6eda801e5347d")) + self.assertEqualBin(b2_with_len(b'hello', 13), unhex( + "6eedb122c6707328a66aa34a07")) + self.assertEqualBin(b2_with_len(b'hello', 21), unhex( + "c7f0f74a227116547b3d2788e927ee2a76c87d8797")) + self.assertEqualBin(b2_with_len(b'hello', 34), unhex( + "2f5fcdf2b870fa254051dd448193a1fb6e92be122efca539ba2aeac0bc6c77d0" + "dadc")) + self.assertEqualBin(b2_with_len(b'hello', 55), unhex( + "daafcf2bd6fccf976cbc234b71cd9f4f7d56fe0eb33a40018707089a215c44a8" + "4b272d0329ae6d85a0f8acc7e964dc2facb715ba472bb6")) + + def testArgon2LongHash(self): + # Unit-test the Argon2 long hash function H', which starts off + # the same as BLAKE2b, but comes with its own method of + # extending the output length past 64 bytes. + # + # I generated these test values using a test program linked + # against the reference implementation's libargon2.a and + # calling its blake2b_long function. + preimage = b'hello, world' + + self.assertEqualBin(argon2_long_hash(1, preimage), unhex("8b")) + self.assertEqualBin(argon2_long_hash(2, preimage), unhex("1ff9")) + self.assertEqualBin(argon2_long_hash(63, preimage), unhex( + "e2c997721f1d64aa8c25e588fb8ab19646ce6d5c2a431fa560fcb813e55dd481" + "322d2630d95ca6b1b63317b13d6b111e5816170c80c3ca7d5b4bf894096de4")) + self.assertEqualBin(argon2_long_hash(64, preimage), unhex( + "0c7ba7ee6d510b4bb5c9b69ac91e25e0b11aa30dd6234b8e61b0fe1537c037b8" + "8ed5aa59a277e8cc07095c81aff26d08967e4dfdabd32db8b6af6ceb78cf8c47")) + self.assertEqualBin(argon2_long_hash(65, preimage), unhex( + "680941abbd8fc80f28c38d623e90903f08709bf76575e2775d4ce01c31b192c8" + "73038d9a31af8991c8b1ad4f2b1991f4d15f73ab0f4f3add415c297a12eb9ddb" + "76")) + self.assertEqualBin(argon2_long_hash(95, preimage), unhex( + "4be28c51850fed70d9403e1406b6ba68a83d98cf222a4ee162beef60fd3384df" + "eba3fce9d95f646982eb384ac943ce5263cb03428fd8d261cc41ffdb7ba328fe" + "098526f2b49593f9e7f38188598ce4693b59f4dd32db30c1be9a9d35784fa0")) + self.assertEqualBin(argon2_long_hash(96, preimage), unhex( + "20295ea01e822cca113f668f33e5e481ed5879bfd7de6359ea42d497da97be52" + "2cdd518d34ae32c44cabd45249b4e697626b0b14b6a33a2bd138be0a4bceeaf4" + "9528f93acef01b093ee84d8d871d1ee6cf7c10e83ad0619631aed19345166f03")) + self.assertEqualBin(argon2_long_hash(97, preimage), unhex( + "d24b31f3ac0baad168d524efc4bafee55fef743fd60b14e28b860d7523e319c7" + "520e2d5457cc3d06dc1044530afdf6990fa12e38d5802eb642f8e77fcfee2c0b" + "1f84a28877f2f2f049ed9299e1e0230f98af3a161185970aad21f0ea0f5184cf" + "90")) + self.assertEqualBin(argon2_long_hash(127, preimage), unhex( + "5d1e8380450dbc985418ed1f3700b925ae0719e4486e29131c81bca7083ac6b8" + "f535c3398488e34d3dc1390de44097f1eee498f10ebe85b579e99a7672023b01" + "ca5c20e63c595b640e00d80f113a52e3773719889b266ab4c65269c11fb212e4" + "75f2b769bb26321bb60ecc0d490821e5056d7dfc9def3cd065d3ba90360764")) + self.assertEqualBin(argon2_long_hash(128, preimage), unhex( + "be15b316f3483c4d0d00f71a65b974894a2025f441b79b9fe461bc740cb0b039" + "c4fe914f61c05a612d63ebc50a662b2d59b1996091e5e3474340544ea46a46cb" + "25c41ff700fafcd96c4f12ddc698cd2426558f960696837ea8170fd2fe284b54" + "8f585f97919ef14f2b3cbb351eb98872add7ba6d08c1401232df6cc878fbeb22")) + self.assertEqualBin(argon2_long_hash(129, preimage), unhex( + "83da464c278dcb12c29b6685fee6d32f0b461337c155369ad0d56b58b0aa5f80" + "9aa7b56bd41b664c8d768957f8f0e40999fb0178eb53cf83f31d725bf92881bc" + "900774bce4cdf56b6386ad3de6891d11a0ccd4564a3431fc4c24105a02d0a6a2" + "434712b9a7471f3223c72a6e64912200d0a3d149a19d06fe9dc8ec09d7ed5a48" + "bb")) + self.assertEqualBin(argon2_long_hash(511, preimage), unhex( + "30c0c0d0467e7665368db0b40a2324a61fb569d35172de2df53a9739a8d18e60" + "b4f25d521c8855604be3e24ea56302566074323d94c0bd3a33d08f185d8ba5ac" + "a2bc3fb2e4c4e5ffec5778daea67c6b5913c9cac16f2e5c7b7818e757fa747b3" + "69e586d616010a752762f69c604238ed8738430366fbdb7493454fa02391a76b" + "30f241695b9fa8d3a3116227c6bb6f72d325cf104ab153d15f928b22767d467d" + "4bf7e16176aaa7315954b7872061933c12d548f1f93a8abb9d73791661bee521" + "b2ae51be373a229dfef32787234c1be5846d133563002b9a029178716ad41e70" + "1539d3fad300c77607c5217701e3e485d72c980f3f71d525c8148375a2f8d22c" + "a211ba165330a90b7e0e6baa6073833925c23bdd388ee904f38463c7e6b85475" + "09b810aae5c9ffc5dd902c2ffe049c338e3ae2c6416d3b874d6a9d384089564c" + "0d8e4dce9b6e47e1d5ec9087bf526cc9fa35aab1893a0588d31b77fea37e0799" + "468deacde47629d2960a3519b3bcd4e22364a9cccd3b128cba21cac27f140d53" + "f79c11e4157e4cb48272eecdf62f52084a27e5b0933bbe66ded17e2df6f8d398" + "f6c479c3c716457820ad177b8bd9334cb594e03d09fcc4f82d4385e141eacd7d" + "9ad1e1c4cb42788af70bac0509f0a891e662960955490abf2763373803e8c89c" + "df632579cb9c647634b30df214a3d67b92fd55d283c42c63b470a48a78cd5b")) + self.assertEqualBin(argon2_long_hash(512, preimage), unhex( + "79a6974e29a9a6c069e0156774d35c5014a409f5ffc60013725367a7208d4929" + "7d228637751768a31a59e27aa89372f1bcc095a6fa331198a5bd5ad053ba2ebb" + "cbcc501ea55cf142e8d95209228c9ab60cd104d5077472f2a9ecaa071aed6ee9" + "5de29e188b7399d5b6b7ed897b2bc4dd1ea745eb9974e39ca6fb983380cc537a" + "c04dfe6caefe85faf206b1613092ebadf791eaa8a5b814c9a79a73a5733b0505" + "a47163c10a0f7309df6663896df6079a7c88c6879bb591a40abd398c6deda792" + "1cc3986435b1c840a768b2fa507446f2f77a406b1b2f739f7795db24789c8927" + "24b4c84b7005445123154f8cd2ba63a7ede672af5d197f846700732025c9931d" + "1c67c5493417ca394a8f68ba532645815cf7b5102af134ecb4fd9e326f53779a" + "3039dbef6a0880db9e38b6b61d2f9ead969e4224c2d9c69b5897e5eeb7032e83" + "334e192ff50017056ccb84d4cc8eee3ab248d2614643d0174fe18c72186dd967" + "92d8545645ddf4a9b2c7a91c9a71857a399449d7154077a8e9580f1a2d20227d" + "671b455ccb897cba0491e50892120d7877f7776d653cfdb176fa3f64a9e6f848" + "cd681c487b488775aaf698294eec813b2cca90d68d63b5d886d61c1a8e922aaa" + "330fd658ede56e34bcd288048e845eba7b8e2e7cc22ba6c91b523e48017aa878" + "8ce4f91d0e6d6c6706762fb0cc7f465cee3916684fb21e337cfe1b583e0b1e92")) + self.assertEqualBin(argon2_long_hash(513, preimage), unhex( + "32243cfbd7eca582d60b3b8ea3ba3d93783537689c7cbcd1d1cbde46200b8c86" + "617fc00e8a9ae991a1e2f91c67e07d5f0a777d982c1461d0c5474e4e164b053c" + "2808559e2b8a5ac4a46a5fcbc825b1d5302c7b0611940194eb494d45ce7113a2" + "3424b51c199c6a5100ab159ff323eda5feffee4da4155a028a81da9d44e4286b" + "ac3dab4ffce43a80b6ce97a47ea0ac51ee16e8b4d3b68942afdc20e1c21747c4" + "94859c3d3883e7dc19ea416a393a3507683d9d03e6a3a91f8f1cb8a7d5d9892e" + "80c8fb0222527a73a1f59b9dd41770982f2af177a6e96093064534803edd0713" + "71ede53024cedc291d768325bb4e4def9af1b5569c349b64816496c37a8787b5" + "4fbe248372ebadb5ce20e03eaa935dc55ff4b8cbe5d6d844c7b71d4656fef22c" + "5a49f13d75a7a8368a2dbc1e78d732b879bfc5c9467eda2bf4918f0c59037ae3" + "dee7880a171409dd1a4e143c814e60301ac77237f261fa7519a04e68000530f9" + "708ed9fda5609d655560a9491f80f5875ad5725e3120686b73319c6a727932e3" + "20a2174422523498c38fea47aeb20d135ff9fd93c6fa6db0005e0001685d7577" + "33a82a4dc9dd6556b938f7b8dafd0d670846780b9931b815063708189b17877b" + "825533bcc250fb576a28be4caa107e6a3a6f7b0c60fb51b0def27008b7e272ac" + "95d610bfa912339799a2e537ce543d7862dddbe31bb224fda4ae283571847a28" + "54")) + self.assertEqualBin(argon2_long_hash(1024, preimage), unhex( + "951252f6fa152124f381266a358d9b78b88e469d08d5fc78e4ea32253c7fc26c" + "3ff1c93529ab4ee6fcf00acf29bbaba934a4014ce2625e0806601c55e6ce70d7" + "121fd82f0904f335c5c7ba07dc6e6adf7582c92f7f255072203ea85844b4fe54" + "817476a20bb742710ffc42750361be94332d0fc721b192309acfa70da43db6ae" + "1d0f0bbe8a3250966a4532b36728162073c9eb3e119ea4c1c187c775dbb25a5d" + "d883e3f65706a5fca897cdc4a8aa7b68ba3f57940c72f3a3396c417e758ba071" + "95be4afba325237c0e2738a74d96fd1350fb623cb2ad40ea8b1e070cf398b98c" + "2865ea40225b81f031f2b405409ca01dc5d9903d3d8e1d6381fbe7ccfc8f3dab" + "eadafd7c976c0ba84a936f78ff7df0f112c089ba88f82bed7f9a6e31a91e5fee" + "f675755454b948de22695660b243b9eca3bcc89608f83d2baa1d73dd6b8bd4f9" + "b995ed9cb0f1edc6e98a49ed841b506c1bf59b43f4b3457a376bbff116c1a4f6" + "07cc62381fc5c19953c68f300c1b51198d40784d812d25810ba404862f04b680" + "6039a074f612ad8b84e0941ba23c915c3e7162c225fbecffdb7dc1ab559b2b54" + "32fe8a498c32e918d8e7e33254ff75077f648827705e987f4d90fba971e78e1a" + "6896b4d775c7359dc950f1e964fa04621aacf3c0988969490f4c72c54caf79e8" + "481053cc0a27ffcd3580aabf9ef1268d498d8a18bd70e9b8402e011753bb7dc7" + "e856c00d988fca924ee7cf61979c38cda8a872e4cc4fbdc90c23a0ded71eb944" + "bb816ab22d9a4380e3e9d1cec818165c2fba6c5d51dcbf452c0cb1779a384937" + "64d695370e13a301eca7be68d4112d2177381514efbb36fe08fc5bc2970301b8" + "06f8e5a57a780e894d5276e2025bb775b6d1861e33c54ab6e3eb72947fbe6f91" + "8174ce24eb4682efbb3c4f01233dc7ce9ef44792e9e876bb03e6751b3d559047" + "d045127d976aa042fc55c690c9048e200065e7b7de19d9353aa9ac9b3e7611f0" + "d1c42d069a300455ca1f7420a352bace89215e705106927510c11b3b1c1486d9" + "f3ab006d2de2ee2c94574f760ce8c246bca229f98c66f06042b14f1fff9a16c0" + "1550237e16d108ce5597299b1eb406a9ee505a29a6e0fa526b3e6beafd336aea" + "138b2f31971586f67c5ffffbd6826d1c75666038c43d0bdff4edfc294e064a49" + "2eed43e2dc78d00abc4e85edcd9563b8251b66f57b0f4b6d17f5a3f35c87c488" + "dbeeb84fd720286197c2dec8290eccf3a313747de285b9cd3548e90cf81b3838" + "3ffcc8c2a7f582feb369d05cb96b9b224d05902b3e39e5b96536032e9dddeb9b" + "9d4f40a9c8f544ca37cf8d39d7c8c6a33880e9184ed017bd642db9590759bd10" + "7362048ede5c0257feecc4984584592c566f37fba8469c064015339fb4f03023" + "56ece37fd3655aae2bfc989b9b4c1384efc3503c8866db901802cb36eda9fb00")) + + def testArgon2(self): + # A few tests of my own of Argon2, derived from the reference + # implementation. + pwd = b"password" + salt = b"salt of at least 16 bytes" + secret = b"secret" + assoc = b"associated data" + + # Smallest memory (8Kbyte) and parallelism (1) parameters the + # reference implementation will accept, but lots of passes + self.assertEqualBin( + argon2('i', 8, 16, 1, 24, pwd, salt, secret, assoc), unhex( + "314da280240a3ca1eedd1f1db417a76eb0741e7df64b8cdf")) + self.assertEqualBin( + argon2('d', 8, 16, 1, 24, pwd, salt, secret, assoc), unhex( + "9cc961cf43e0f86c2d4e202b816dc5bc5b2177e68faa0b08")) + self.assertEqualBin( + argon2('id', 8, 16, 1, 24, pwd, salt, secret, assoc), unhex( + "6cd6c490c582fa597721d772d4e3de166987792491b48c51")) + + # Test a memory cost value that isn't a power of 2. This + # checks a wraparound case during the conversion of J1 to a + # block index, and is a regression test for a bug that nearly + # got past me during original development. + self.assertEqualBin( + argon2('i', 104, 16, 2, 24, pwd, salt, secret, assoc), unhex( + "a561963623f1073c9aa8caecdb600c73ffc6de677ba8d97c")) + self.assertEqualBin( + argon2('d', 104, 16, 2, 24, pwd, salt, secret, assoc), unhex( + "a9014db7f1d468fb25b88fa7fc0deac0f2e7f27e25d2cf6e")) + self.assertEqualBin( + argon2('id', 104, 16, 2, 24, pwd, salt, secret, assoc), unhex( + "64f3212b1e7725ffcf9ae2d1753d63e763bcd6970061a435")) + + # Larger parameters that should exercise the pseudorandom + # block indexing reasonably thoroughly. Also generate plenty + # of output data. + self.assertEqualBin( + argon2('i', 1024, 5, 16, 77, pwd, salt, secret, assoc), unhex( + "b008a685ff57730fad0e6f3ef3b9189282c0d9b05303675f43b5f3054724" + "733fcbe8e2639cc2c930535b31b723339041bcd703bf2483455acf86c0e6" + "9ed88c545ad40f1f2068855e4d61e99407")) + self.assertEqualBin( + argon2('d', 1024, 5, 16, 111, pwd, salt, secret, assoc), unhex( + "399ffbcd720c47745b9deb391ed0de7d5e0ffe53aef9f8ef7a7918cfa212" + "53df8cc577affbd5e0c0f8bf6d93c11b2f63973f8fc8f89dccd832fc587e" + "5d61717be6e88ca33eef5d1e168c028bae632a2a723c6c83f8e755f39171" + "5eda1c77c8e2fe06fbdd4e56d35262587e7df73cd7")) + self.assertEqualBin( + argon2('id', 1024, 5, 16, 123, pwd, salt, secret, assoc), unhex( + "6636807289cb9b9c032f48dcc31ffed1de4ca6c1b97e1ce768d690486341" + "2ac84b39d568a81dd01d9ee3ceec6cc23441d95e6abeb4a2024f1f540d56" + "9b799277c4037ddc7195ba783c9158a901adc7d4a5df8357b34a3869e5d6" + "aeae2a21201eef5e347de22c922192e8f46274b0c9d33e965155a91e7686" + "9d530e")) + def testRSAVerify(self): def blobs(n, e, d, p, q, iqmp): pubblob = ssh_string(b"ssh-rsa") + ssh2_mpint(e) + ssh2_mpint(n) @@ -1345,6 +2049,7 @@ def testKeyMethods(self): test_keys = [ ('ed25519', 'AAAAC3NzaC1lZDI1NTE5AAAAIM7jupzef6CD0ps2JYxJp9IlwY49oorOseV5z5JFDFKn', 'AAAAIAf4/WRtypofgdNF2vbZOUFE1h4hvjw4tkGJZyOzI7c3', 255, b'0xf4d6e7f6f4479c23f0764ef43cea1711dbfe02aa2b5a32ff925c7c1fbf0f0db,0x27520c4592cf79e5b1ce8aa23d8ec125d2a7498c25369bd283a07fde9cbae3ce', [(0, 'AAAAC3NzaC1lZDI1NTE5AAAAQN73EqfyA4WneqDhgZ98TlRj9V5Wg8zCrMxTLJN1UtyfAnPUJDtfG/U0vOsP8PrnQxd41DDDnxrAXuqJz8rOagc=')]), + ('ed448', 'AAAACXNzaC1lZDQ0OAAAADnRI0CQDym5IqUidLNDcSdHe54bYEwqjpjBlab8uKGoe6FRqqejha7+5U/VAHy7BmE23+ju26O9XgA=', 'AAAAObP9klqyiJSJsdFJf+xwZQdkbZGUqXE07K6e5plfRTGjYYkyWJFUNFH4jzIn9xH1TX9z9EGycPaXAA==', 448, b'0x4bf4a2b6586c60d8cdb52c2b45b897f6d2224bc37987489c0d70febb449e8c82964ed5785827be808e44d31dd31e6ff7c99f43e49f419928,0x5ebda3dbeee8df366106bb7c00d54fe5feae85a3a7aa51a17ba8a1b8fca695c1988e2a4c601b9e7b47277143b37422a522b9290f904023d1', [(0, 'AAAACXNzaC1lZDQ0OAAAAHLkSVioGMvLesZp3Tn+Z/sSK0Hl7RHsHP4q9flLzTpZG5h6JDH3VmZBEjTJ6iOLaa0v4FoNt0ng4wAB53WrlQC4h3iAusoGXnPMAKJLmqzplKOCi8HKXk8Xl8fsXbaoyhatv1OZpwJcffmh1x+x+LSgNQA=')]), ('p256', 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q=', 'AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q==', 256, b'nistp256,0x7918434b10a2ae4b6c923554c5a1c376d5e374d2622dd6569a8880a70128af75,0x4dc14594031981c78e1d0d3100561c49ccc8b6d2ef16efb191c2d7ad177837b4', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABIAAAAIAryzHDGi/TcCnbdxZkIYR5EGR6SNYXr/HlQRF8le+/IAAAAIERfzn6eHuBbqWIop2qL8S7DWRB3lenN1iyL10xYQPKw')]), ('p384', 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg==', 'AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg==', 384, b'nistp384,0xc60af0f52d7c0949c0a6814c8184b82cc7d2fa8e31ae146dc8eb05b4db9065525277e8fa7b82f34342763f4924cb358e,0xf7c5f06cca2dce73f07de767233be35fc15058d5eeb107b101437a4e0ac96bca90480a89395989dd7d56e90da35ab61e', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMDmHrtXCADzLvkkWG/duBAHlf6B1mVvdt6F0uzXfsf8Yub8WXNUNVnYq6ovrWPzLggAAADEA9izzwoUuFcXYRJeKcRLZEGMmSDDPzUZb7oZR0UgD1jsMQXs8UfpO31Qur/FDSCRK')]), ('p521', 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg==', 'AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw==', 521, b'nistp521,0x16b1ad86528cd79dafbb61a193e47b88ef7f33a7be8537a1359ebe141c287f81cf90f076fc71d78f9fb0e729d6c94ad6897e53236ae2b89108ac912b7111f92e094,0xe72435f6c38cab299fb00c74b3f65c21f69d85f81e51f79d9eb3a817dd125190603eaa12a92aea7c80ac7bf1131b95e5fcc2046d22efb860c52729bd5e75112246', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAACMAAAAQgCLgvftvwM3CUaigrW0yzmCHoYjC6GLtO+6S91itqpgMEtWPNlaTZH6QQqkgscijWdXx98dDkQao/gcAKVmOZKPXgAAAEIB1PIrsDF1y6poJ/czqujB7NSUWt31v+c2t6UA8m2gTA1ARuVJ9XBGLMdceOTB00Hi9psC2RYFLpaWREOGCeDa6ow=')]), @@ -1423,14 +2128,199 @@ def testKeyMethods(self): # both parts. Other than that, we don't do much to # make this a rigorous cryptographic test. for n, d in [(1,3),(2,3)]: - sigbytes = list(bytevals(sigblob)) + sigbytes = list(sigblob) bit = 8 * len(sigbytes) * n // d sigbytes[bit // 8] ^= 1 << (bit % 8) - badsig = valbytes(sigbytes) + badsig = bytes(sigbytes) for key in [pubkey, privkey, privkey2]: self.assertFalse(ssh_key_verify( key, badsig, test_message)) + def testPPKLoadSave(self): + # Stability test of PPK load/save functions. + input_clear_key = b"""\ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: none +Comment: ed25519-key-20200105 +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F +JH1W +Private-Lines: 1 +AAAAIGvvIpl8jyqn8Xufkw6v3FnEGtXF3KWw55AP3/AGEBpY +Private-MAC: 816c84093fc4877e8411b8e5139c5ce35d8387a2630ff087214911d67417a54d +""" + input_encrypted_key = b"""\ +PuTTY-User-Key-File-3: ssh-ed25519 +Encryption: aes256-cbc +Comment: ed25519-key-20200105 +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F +JH1W +Key-Derivation: Argon2id +Argon2-Memory: 8192 +Argon2-Passes: 13 +Argon2-Parallelism: 1 +Argon2-Salt: 37c3911bfefc8c1d11ec579627d2b3d9 +Private-Lines: 1 +amviz4sVUBN64jLO3gt4HGXJosUArghc4Soi7aVVLb2Tir5Baj0OQClorycuaPRd +Private-MAC: 6f5e588e475e55434106ec2c3569695b03f423228b44993a9e97d52ffe7be5a8 +""" + algorithm = b'ssh-ed25519' + comment = b'ed25519-key-20200105' + pp = b'test-passphrase' + public_blob = unhex( + '0000000b7373682d65643235353139000000207242b33387688f57ff218bb639' + 'f6d9fd213ba54f3100d5b5cb64ca6e85247d56') + + self.assertEqual(ppk_encrypted_s(input_clear_key), (False, comment)) + self.assertEqual(ppk_encrypted_s(input_encrypted_key), (True, comment)) + self.assertEqual(ppk_encrypted_s("not a key file"), (False, None)) + + self.assertEqual(ppk_loadpub_s(input_clear_key), + (True, algorithm, public_blob, comment, None)) + self.assertEqual(ppk_loadpub_s(input_encrypted_key), + (True, algorithm, public_blob, comment, None)) + self.assertEqual(ppk_loadpub_s("not a key file"), + (False, None, b'', None, + b'not a PuTTY SSH-2 private key')) + + k1, c, e = ppk_load_s(input_clear_key, None) + self.assertEqual((c, e), (comment, None)) + k2, c, e = ppk_load_s(input_encrypted_key, pp) + self.assertEqual((c, e), (comment, None)) + privblob = ssh_key_private_blob(k1) + self.assertEqual(ssh_key_private_blob(k2), privblob) + + salt = unhex('37c3911bfefc8c1d11ec579627d2b3d9') + with queued_specific_random_data(salt): + self.assertEqual(ppk_save_sb(k1, comment, None, + 3, 'id', 8192, 13, 1), + input_clear_key) + with queued_specific_random_data(salt): + self.assertEqual(ppk_save_sb(k2, comment, None, + 3, 'id', 8192, 13, 1), + input_clear_key) + + with queued_specific_random_data(salt): + self.assertEqual(ppk_save_sb(k1, comment, pp, + 3, 'id', 8192, 13, 1), + input_encrypted_key) + with queued_specific_random_data(salt): + self.assertEqual(ppk_save_sb(k2, comment, pp, + 3, 'id', 8192, 13, 1), + input_encrypted_key) + + # And check we can still handle v2 key files. + v2_clear_key = b"""\ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: none +Comment: ed25519-key-20200105 +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F +JH1W +Private-Lines: 1 +AAAAIGvvIpl8jyqn8Xufkw6v3FnEGtXF3KWw55AP3/AGEBpY +Private-MAC: 2a629acfcfbe28488a1ba9b6948c36406bc28422 +""" + v2_encrypted_key = b"""\ +PuTTY-User-Key-File-2: ssh-ed25519 +Encryption: aes256-cbc +Comment: ed25519-key-20200105 +Public-Lines: 2 +AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F +JH1W +Private-Lines: 1 +4/jKlTgC652oa9HLVGrMjHZw7tj0sKRuZaJPOuLhGTvb25Jzpcqpbi+Uf+y+uo+Z +Private-MAC: 5b1f6f4cc43eb0060d2c3e181bc0129343adba2b +""" + + self.assertEqual(ppk_encrypted_s(v2_clear_key), (False, comment)) + self.assertEqual(ppk_encrypted_s(v2_encrypted_key), (True, comment)) + self.assertEqual(ppk_encrypted_s("not a key file"), (False, None)) + + self.assertEqual(ppk_loadpub_s(v2_clear_key), + (True, algorithm, public_blob, comment, None)) + self.assertEqual(ppk_loadpub_s(v2_encrypted_key), + (True, algorithm, public_blob, comment, None)) + self.assertEqual(ppk_loadpub_s("not a key file"), + (False, None, b'', None, + b'not a PuTTY SSH-2 private key')) + + k1, c, e = ppk_load_s(v2_clear_key, None) + self.assertEqual((c, e), (comment, None)) + k2, c, e = ppk_load_s(v2_encrypted_key, pp) + self.assertEqual((c, e), (comment, None)) + self.assertEqual(ssh_key_private_blob(k1), privblob) + self.assertEqual(ssh_key_private_blob(k2), privblob) + + self.assertEqual(ppk_save_sb(k2, comment, None, + 2, 'id', 8192, 13, 1), + v2_clear_key) + self.assertEqual(ppk_save_sb(k1, comment, pp, + 2, 'id', 8192, 13, 1), + v2_encrypted_key) + + def testRSA1LoadSave(self): + # Stability test of SSH-1 RSA key-file load/save functions. + input_clear_key = unhex( + "5353482050524956415445204B45592046494C4520464F524D415420312E310A" + "000000000000000002000200BB115A85B741E84E3D940E690DF96A0CBFDC07CA" + "70E51DA8234D211DE77341CEF40C214CAA5DCF68BE2127447FD6C84CCB17D057" + "A74F2365B9D84A78906AEB51000625000000107273612D6B65792D3230323030" + "313036208E208E0200929EE615C6FC4E4B29585E52570F984F2E97B3144AA5BD" + "4C6EB2130999BB339305A21FFFA79442462A8397AF8CAC395A3A3827DE10457A" + "1F1B277ABFB8C069C100FF55B1CAD69B3BD9E42456CF28B1A4B98130AFCE08B2" + "8BCFFF5FFFED76C5D51E9F0100C5DE76889C62B1090A770AE68F087A19AB5126" + "E60DF87710093A2AD57B3380FB0100F2068AC47ECB33BF8F13DF402BABF35EE7" + "26BD32F7564E51502DF5C8F4888B2300000000") + input_encrypted_key = unhex( + "5353482050524956415445204b45592046494c4520464f524d415420312e310a" + "000300000000000002000200bb115a85b741e84e3d940e690df96a0cbfdc07ca" + "70e51da8234d211de77341cef40c214caa5dcf68be2127447fd6c84ccb17d057" + "a74f2365b9d84a78906aeb51000625000000107273612d6b65792d3230323030" + "3130363377f926e811a5f044c52714801ecdcf9dd572ee0a193c4f67e87ab2ce" + "4569d0c5776fd6028909ed8b6d663bef15d207d3ef6307e7e21dbec56e8d8b4e" + "894ded34df891bb29bae6b2b74805ac80f7304926abf01ae314dd69c64240761" + "34f15d50c99f7573252993530ec9c4d5016dd1f5191730cda31a5d95d362628b" + "2a26f4bb21840d01c8360e4a6ce216c4686d25b8699d45cf361663bb185e2c5e" + "652012a1e0f9d6d19afbb28506f7775bfd8129") + + comment = b'rsa-key-20200106' + pp = b'test-passphrase' + public_blob = unhex( + "000002000006250200bb115a85b741e84e3d940e690df96a0cbfdc07ca70e51d" + "a8234d211de77341cef40c214caa5dcf68be2127447fd6c84ccb17d057a74f23" + "65b9d84a78906aeb51") + + self.assertEqual(rsa1_encrypted_s(input_clear_key), (False, comment)) + self.assertEqual(rsa1_encrypted_s(input_encrypted_key), + (True, comment)) + self.assertEqual(rsa1_encrypted_s("not a key file"), (False, None)) + + self.assertEqual(rsa1_loadpub_s(input_clear_key), + (1, public_blob, comment, None)) + self.assertEqual(rsa1_loadpub_s(input_encrypted_key), + (1, public_blob, comment, None)) + + k1 = rsa_new() + status, c, e = rsa1_load_s(input_clear_key, k1, None) + self.assertEqual((status, c, e), (1, comment, None)) + k2 = rsa_new() + status, c, e = rsa1_load_s(input_clear_key, k2, None) + self.assertEqual((status, c, e), (1, comment, None)) + + with queued_specific_random_data(unhex("208e")): + self.assertEqual(rsa1_save_sb(k1, comment, None), input_clear_key) + with queued_specific_random_data(unhex("208e")): + self.assertEqual(rsa1_save_sb(k2, comment, None), input_clear_key) + + with queued_specific_random_data(unhex("99f3")): + self.assertEqual(rsa1_save_sb(k1, comment, pp), + input_encrypted_key) + with queued_specific_random_data(unhex("99f3")): + self.assertEqual(rsa1_save_sb(k2, comment, pp), + input_encrypted_key) + class standard_test_vectors(MyTestBase): def testAES(self): def vector(cipher, key, plaintext, ciphertext): @@ -1731,80 +2621,166 @@ def testSHA256(self): "8ad3361763f7e9b2d95f4f0da6e1ccbc")) def testSHA384(self): - # Test cases from RFC 6234 section 8.5, omitting the ones - # whose input is not a multiple of 8 bits - self.assertEqualBin(hash_str('sha384', "abc"), unhex( - 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163' - '1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')) - self.assertEqualBin(hash_str('sha384', - "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), unhex( - '09330c33f71147e83d192fc782cd1b4753111b173b3b05d2' - '2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039')) - self.assertEqualBin(hash_str_iter('sha384', - ("a" * 1000 for _ in range(1000))), unhex( - '9d0e1809716474cb086e834e310a4a1ced149e9c00f24852' - '7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985')) - self.assertEqualBin(hash_str('sha384', - "01234567012345670123456701234567" * 20), unhex( - '2fc64a4f500ddb6828f6a3430b8dd72a368eb7f3a8322a70' - 'bc84275b9c0b3ab00d27a5cc3c2d224aa6b61a0d79fb4596')) - self.assertEqualBin(hash_str('sha384', b"\xB9"), unhex( - 'bc8089a19007c0b14195f4ecc74094fec64f01f90929282c' - '2fb392881578208ad466828b1c6c283d2722cf0ad1ab6938')) - self.assertEqualBin(hash_str('sha384', - unhex("a41c497779c0375ff10a7f4e08591739")), unhex( - 'c9a68443a005812256b8ec76b00516f0dbb74fab26d66591' - '3f194b6ffb0e91ea9967566b58109cbc675cc208e4c823f7')) - self.assertEqualBin(hash_str('sha384', unhex( - "399669e28f6b9c6dbcbb6912ec10ffcf74790349b7dc8fbe4a8e7b3b5621db0f" - "3e7dc87f823264bbe40d1811c9ea2061e1c84ad10a23fac1727e7202fc3f5042" - "e6bf58cba8a2746e1f64f9b9ea352c711507053cf4e5339d52865f25cc22b5e8" - "7784a12fc961d66cb6e89573199a2ce6565cbdf13dca403832cfcb0e8b7211e8" - "3af32a11ac17929ff1c073a51cc027aaedeff85aad7c2b7c5a803e2404d96d2a" - "77357bda1a6daeed17151cb9bc5125a422e941de0ca0fc5011c23ecffefdd096" - "76711cf3db0a3440720e1615c1f22fbc3c721de521e1b99ba1bd557740864214" - "7ed096")), unhex( - '4f440db1e6edd2899fa335f09515aa025ee177a79f4b4aaf' - '38e42b5c4de660f5de8fb2a5b2fbd2a3cbffd20cff1288c0')) + for hashname in ['sha384_sw', 'sha384_hw']: + if ssh_hash_new(hashname) is None: + continue # skip testing of unavailable HW implementation + + # Test cases from RFC 6234 section 8.5, omitting the ones + # whose input is not a multiple of 8 bits + self.assertEqualBin(hash_str('sha384', "abc"), unhex( + 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163' + '1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')) + self.assertEqualBin(hash_str('sha384', + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), + unhex('09330c33f71147e83d192fc782cd1b4753111b173b3b05d2' + '2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039')) + self.assertEqualBin(hash_str_iter('sha384', + ("a" * 1000 for _ in range(1000))), unhex( + '9d0e1809716474cb086e834e310a4a1ced149e9c00f24852' + '7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985')) + self.assertEqualBin(hash_str('sha384', + "01234567012345670123456701234567" * 20), unhex( + '2fc64a4f500ddb6828f6a3430b8dd72a368eb7f3a8322a70' + 'bc84275b9c0b3ab00d27a5cc3c2d224aa6b61a0d79fb4596')) + self.assertEqualBin(hash_str('sha384', b"\xB9"), unhex( + 'bc8089a19007c0b14195f4ecc74094fec64f01f90929282c' + '2fb392881578208ad466828b1c6c283d2722cf0ad1ab6938')) + self.assertEqualBin(hash_str('sha384', + unhex("a41c497779c0375ff10a7f4e08591739")), unhex( + 'c9a68443a005812256b8ec76b00516f0dbb74fab26d66591' + '3f194b6ffb0e91ea9967566b58109cbc675cc208e4c823f7')) + self.assertEqualBin(hash_str('sha384', unhex( + "399669e28f6b9c6dbcbb6912ec10ffcf74790349b7dc8fbe4a8e7b3b5621" + "db0f3e7dc87f823264bbe40d1811c9ea2061e1c84ad10a23fac1727e7202" + "fc3f5042e6bf58cba8a2746e1f64f9b9ea352c711507053cf4e5339d5286" + "5f25cc22b5e87784a12fc961d66cb6e89573199a2ce6565cbdf13dca4038" + "32cfcb0e8b7211e83af32a11ac17929ff1c073a51cc027aaedeff85aad7c" + "2b7c5a803e2404d96d2a77357bda1a6daeed17151cb9bc5125a422e941de" + "0ca0fc5011c23ecffefdd09676711cf3db0a3440720e1615c1f22fbc3c72" + "1de521e1b99ba1bd5577408642147ed096")), unhex( + '4f440db1e6edd2899fa335f09515aa025ee177a79f4b4aaf' + '38e42b5c4de660f5de8fb2a5b2fbd2a3cbffd20cff1288c0')) def testSHA512(self): - # Test cases from RFC 6234 section 8.5, omitting the ones - # whose input is not a multiple of 8 bits - self.assertEqualBin(hash_str('sha512', "abc"), unhex( - 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a' - '2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')) - self.assertEqualBin(hash_str('sha512', - "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" - "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), unhex( - '8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018' - '501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909')) - self.assertEqualBin(hash_str_iter('sha512', - ("a" * 1000 for _ in range(1000))), unhex( - 'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb' - 'de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b')) - self.assertEqualBin(hash_str('sha512', - "01234567012345670123456701234567" * 20), unhex( - '89d05ba632c699c31231ded4ffc127d5a894dad412c0e024db872d1abd2ba814' - '1a0f85072a9be1e2aa04cf33c765cb510813a39cd5a84c4acaa64d3f3fb7bae9')) - self.assertEqualBin(hash_str('sha512', b"\xD0"), unhex( - '9992202938e882e73e20f6b69e68a0a7149090423d93c81bab3f21678d4aceee' - 'e50e4e8cafada4c85a54ea8306826c4ad6e74cece9631bfa8a549b4ab3fbba15')) - self.assertEqualBin(hash_str('sha512', - unhex("8d4e3c0e3889191491816e9d98bff0a0")), unhex( - 'cb0b67a4b8712cd73c9aabc0b199e9269b20844afb75acbdd1c153c9828924c3' - 'ddedaafe669c5fdd0bc66f630f6773988213eb1b16f517ad0de4b2f0c95c90f8')) - self.assertEqualBin(hash_str('sha512', unhex( - "a55f20c411aad132807a502d65824e31a2305432aa3d06d3e282a8d84e0de1de" - "6974bf495469fc7f338f8054d58c26c49360c3e87af56523acf6d89d03e56ff2" - "f868002bc3e431edc44df2f0223d4bb3b243586e1a7d924936694fcbbaf88d95" - "19e4eb50a644f8e4f95eb0ea95bc4465c8821aacd2fe15ab4981164bbb6dc32f" - "969087a145b0d9cc9c67c22b763299419cc4128be9a077b3ace634064e6d9928" - "3513dc06e7515d0d73132e9a0dc6d3b1f8b246f1a98a3fc72941b1e3bb2098e8" - "bf16f268d64f0b0f4707fe1ea1a1791ba2f3c0c758e5f551863a96c949ad47d7" - "fb40d2")), unhex( - 'c665befb36da189d78822d10528cbf3b12b3eef726039909c1a16a270d487193' - '77966b957a878e720584779a62825c18da26415e49a7176a894e7510fd1451f5')) + for hashname in ['sha512_sw', 'sha512_hw']: + if ssh_hash_new(hashname) is None: + continue # skip testing of unavailable HW implementation + + # Test cases from RFC 6234 section 8.5, omitting the ones + # whose input is not a multiple of 8 bits + self.assertEqualBin(hash_str('sha512', "abc"), unhex( + 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55' + 'd39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94f' + 'a54ca49f')) + self.assertEqualBin(hash_str('sha512', + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), + unhex('8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299' + 'aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26' + '545e96e55b874be909')) + self.assertEqualBin(hash_str_iter('sha512', + ("a" * 1000 for _ in range(1000))), unhex( + 'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa9' + '73ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217' + 'ad8cc09b')) + self.assertEqualBin(hash_str('sha512', + "01234567012345670123456701234567" * 20), unhex( + '89d05ba632c699c31231ded4ffc127d5a894dad412c0e024db872d1abd2b' + 'a8141a0f85072a9be1e2aa04cf33c765cb510813a39cd5a84c4acaa64d3f' + '3fb7bae9')) + self.assertEqualBin(hash_str('sha512', b"\xD0"), unhex( + '9992202938e882e73e20f6b69e68a0a7149090423d93c81bab3f21678d4a' + 'ceeee50e4e8cafada4c85a54ea8306826c4ad6e74cece9631bfa8a549b4a' + 'b3fbba15')) + self.assertEqualBin(hash_str('sha512', + unhex("8d4e3c0e3889191491816e9d98bff0a0")), unhex( + 'cb0b67a4b8712cd73c9aabc0b199e9269b20844afb75acbdd1c153c98289' + '24c3ddedaafe669c5fdd0bc66f630f6773988213eb1b16f517ad0de4b2f0' + 'c95c90f8')) + self.assertEqualBin(hash_str('sha512', unhex( + "a55f20c411aad132807a502d65824e31a2305432aa3d06d3e282a8d84e0d" + "e1de6974bf495469fc7f338f8054d58c26c49360c3e87af56523acf6d89d" + "03e56ff2f868002bc3e431edc44df2f0223d4bb3b243586e1a7d92493669" + "4fcbbaf88d9519e4eb50a644f8e4f95eb0ea95bc4465c8821aacd2fe15ab" + "4981164bbb6dc32f969087a145b0d9cc9c67c22b763299419cc4128be9a0" + "77b3ace634064e6d99283513dc06e7515d0d73132e9a0dc6d3b1f8b246f1" + "a98a3fc72941b1e3bb2098e8bf16f268d64f0b0f4707fe1ea1a1791ba2f3" + "c0c758e5f551863a96c949ad47d7fb40d2")), unhex( + 'c665befb36da189d78822d10528cbf3b12b3eef726039909c1a16a270d48' + '719377966b957a878e720584779a62825c18da26415e49a7176a894e7510' + 'fd1451f5')) + + def testSHA3(self): + # Source: all the SHA-3 test strings from + # https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values#aHashing + # which are a multiple of 8 bits long. + + self.assertEqualBin(hash_str('sha3_224', ''), unhex("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7")) + self.assertEqualBin(hash_str('sha3_224', unhex('a3')*200), unhex("9376816aba503f72f96ce7eb65ac095deee3be4bf9bbc2a1cb7e11e0")) + self.assertEqualBin(hash_str('sha3_256', ''), unhex("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")) + self.assertEqualBin(hash_str('sha3_256', unhex('a3')*200), unhex("79f38adec5c20307a98ef76e8324afbfd46cfd81b22e3973c65fa1bd9de31787")) + self.assertEqualBin(hash_str('sha3_384', ''), unhex("0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004")) + self.assertEqualBin(hash_str('sha3_384', unhex('a3')*200), unhex("1881de2ca7e41ef95dc4732b8f5f002b189cc1e42b74168ed1732649ce1dbcdd76197a31fd55ee989f2d7050dd473e8f")) + self.assertEqualBin(hash_str('sha3_512', ''), unhex("a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26")) + self.assertEqualBin(hash_str('sha3_512', unhex('a3')*200), unhex("e76dfad22084a8b1467fcf2ffa58361bec7628edf5f3fdc0e4805dc48caeeca81b7c13c30adf52a3659584739a2df46be589c51ca1a4a8416df6545a1ce8ba00")) + self.assertEqualBin(hash_str('shake256_114bytes', ''), unhex("46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853349ec75546f58fb7c2775c38462c5010d846")) + self.assertEqualBin(hash_str('shake256_114bytes', unhex('a3')*200), unhex("cd8a920ed141aa0407a22d59288652e9d9f1a7ee0c1e7c1ca699424da84a904d2d700caae7396ece96604440577da4f3aa22aeb8857f961c4cd8e06f0ae6610b1048a7f64e1074cd629e85ad7566048efc4fb500b486a3309a8f26724c0ed628001a1099422468de726f1061d99eb9e93604")) + + def testBLAKE2b(self): + # Test case from RFC 7693 appendix A. + self.assertEqualBin(hash_str('blake2b', b'abc'), unhex( + "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1" + "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")) + + # A small number of test cases from the larger test vector + # set, testing multiple blocks and the empty input. + self.assertEqualBin(hash_str('blake2b', b''), unhex( + "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419" + "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce")) + self.assertEqualBin(hash_str('blake2b', unhex('00')), unhex( + "2fa3f686df876995167e7c2e5d74c4c7b6e48f8068fe0e44208344d480f7904c" + "36963e44115fe3eb2a3ac8694c28bcb4f5a0f3276f2e79487d8219057a506e4b")) + self.assertEqualBin(hash_str('blake2b', bytes(range(255))), unhex( + "5b21c5fd8868367612474fa2e70e9cfa2201ffeee8fafab5797ad58fefa17c9b" + "5b107da4a3db6320baaf2c8617d5a51df914ae88da3867c2d41f0cc14fa67928")) + + # You can get this test program to run the full version of the + # test vectors by modifying the source temporarily to set this + # variable to a pathname where you downloaded the JSON file + # blake2-kat.json. + blake2_test_vectors_path = None + if blake2_test_vectors_path is not None: + with open(blake2_test_vectors_path) as fh: + vectors = json.load(fh) + for vector in vectors: + if vector['hash'] != 'blake2b': + continue + if len(vector['key']) != 0: + continue + + h = blake2b_new_general(len(vector['out']) // 2) + ssh_hash_update(h, unhex(vector['in'])) + digest = ssh_hash_digest(h) + self.assertEqualBin(digest, unhex(vector['out'])) + + def testArgon2(self): + # draft-irtf-cfrg-argon2-12 section 5 + self.assertEqualBin( + argon2('d', 32, 3, 4, 32, b'\x01' * 32, b'\x02' * 16, + b'\x03' * 8, b'\x04' * 12), + unhex("512b391b6f1162975371d30919734294" + "f868e3be3984f3c1a13a4db9fabe4acb")) + self.assertEqualBin( + argon2('i', 32, 3, 4, 32, b'\x01' * 32, b'\x02' * 16, + b'\x03' * 8, b'\x04' * 12), + unhex("c814d9d1dc7f37aa13f0d77f2494bda1" + "c8de6b016dd388d29952a4c4672b6ce8")) + self.assertEqualBin( + argon2('id', 32, 3, 4, 32, b'\x01' * 32, b'\x02' * 16, + b'\x03' * 8, b'\x04' * 12), + unhex("0d640df58d78766c08c037a34a8b53c9" + "d01ef0452d75b65eb52520e96b01e659")) def testHmacSHA(self): # Test cases from RFC 6234 section 8.5. @@ -1907,41 +2883,117 @@ def vector(privkey, pubkey, message, signature): signature = unhex(words[3])[:64] vector(privkey, pubkey, message, signature) + def testEd448(self): + def vector(privkey, pubkey, message, signature): + x, y = ecc_edwards_get_affine(eddsa_public( + mp_from_bytes_le(privkey), 'ed448')) + self.assertEqual(int(y) | ((int(x) & 1) << 455), + int(mp_from_bytes_le(pubkey))) + pubblob = ssh_string(b"ssh-ed448") + ssh_string(pubkey) + privblob = ssh_string(privkey) + sigblob = ssh_string(b"ssh-ed448") + ssh_string(signature) + pubkey = ssh_key_new_pub('ed448', pubblob) + self.assertTrue(ssh_key_verify(pubkey, sigblob, message)) + privkey = ssh_key_new_priv('ed448', pubblob, privblob) + # Deterministic signature check as in Ed25519 + self.assertEqualBin(ssh_key_sign(privkey, message, 0), sigblob) + + # Source: RFC 8032 section 7.4 + + privkey = unhex('6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b') + pubkey = unhex('5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180') + message = b'' + signature = unhex('533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600') + vector(privkey, pubkey, message, signature) + + privkey = unhex('c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e') + pubkey = unhex('43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480') + message = unhex('03') + signature = unhex('26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00') + vector(privkey, pubkey, message, signature) + + privkey = unhex('cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328') + pubkey = unhex('dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400') + message = unhex('0c3e544074ec63b0265e0c') + signature = unhex('1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d389dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b051068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5028961c9bf8ffd973fe5d5c206492b140e00') + vector(privkey, pubkey, message, signature) + + privkey = unhex('258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b') + pubkey = unhex('3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580') + message = unhex('64a65f3cdedcdd66811e2915') + signature = unhex('7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00') + vector(privkey, pubkey, message, signature) + + privkey = unhex('d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01') + pubkey = unhex('df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00') + message = unhex('bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944') + signature = unhex('554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900') + vector(privkey, pubkey, message, signature) + + privkey = unhex('2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5') + pubkey = unhex('79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00') + message = unhex('15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11') + signature = unhex('c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00') + vector(privkey, pubkey, message, signature) + + privkey = unhex('872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8') + pubkey = unhex('a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400') + message = unhex('6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e972660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db977025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a6059d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87') + signature = unhex('e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00') + vector(privkey, pubkey, message, signature) + def testMontgomeryKex(self): # Unidirectional tests, consisting of an input random number # string and peer public value, giving the expected output # shared key. Source: RFC 7748 section 5.2. rfc7748s5_2 = [ - ('a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4', + ('curve25519', + 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4', 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c', 0xc3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552), - ('4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d', + ('curve25519', + '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d', 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493', 0x95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957), + ('curve448', + '3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3', + '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086', + 0xce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f), + ('curve448', + '203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f', + '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db', + 0x884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d), ] - for priv, pub, expected in rfc7748s5_2: + for method, priv, pub, expected in rfc7748s5_2: with queued_specific_random_data(unhex(priv)): - ecdh = ssh_ecdhkex_newkey('curve25519') + ecdh = ssh_ecdhkex_newkey(method) key = ssh_ecdhkex_getkey(ecdh, unhex(pub)) self.assertEqual(int(key), expected) # Bidirectional tests, consisting of the input random number # strings for both parties, and the expected public values and - # shared key. Source: RFC 7748 section 6.1. - rfc7748s6_1 = [ - ('77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a', + # shared key. Source: RFC 7748 section 6. + rfc7748s6 = [ + ('curve25519', # section 6.1 + '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a', '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a', '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb', 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f', 0x4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742), + ('curve448', # section 6.2 + '9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b', + '9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0', + '1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d', + '3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609', + 0x07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d), ] - for apriv, apub, bpriv, bpub, expected in rfc7748s6_1: + for method, apriv, apub, bpriv, bpub, expected in rfc7748s6: with queued_specific_random_data(unhex(apriv)): - alice = ssh_ecdhkex_newkey('curve25519') + alice = ssh_ecdhkex_newkey(method) with queued_specific_random_data(unhex(bpriv)): - bob = ssh_ecdhkex_newkey('curve25519') + bob = ssh_ecdhkex_newkey(method) self.assertEqualBin(ssh_ecdhkex_getpublic(alice), unhex(apub)) self.assertEqualBin(ssh_ecdhkex_getpublic(bob), unhex(bpub)) akey = ssh_ecdhkex_getkey(alice, unhex(bpub)) diff --git a/test/desref.py b/test/desref.py index e09a07a..bc3e51a 100755 --- a/test/desref.py +++ b/test/desref.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Reference implementation of DES. # @@ -15,6 +15,8 @@ import functools import argparse +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + def bitor(x, y): return x | y def split_words(val, width=32): diff --git a/test/display.txt b/test/display.txt index f7b5c90..a61f618 100644 --- a/test/display.txt +++ b/test/display.txt @@ -4,11 +4,11 @@ Test of all features involved in do_text() Reverse video + red on yellow:  bing!  Yellow on red should look the same:  bong!  -Basic attrs, combining chars, both widths: Bold blink underline [Λ̊][チ][text] -Wide char should be off by 1 narrow char: Bold blink underline [Λ̊][チ][text] +Basic attrs, combining chars, both widths: Bold blink under strike [Λ̊][チ][text] +Wide char should be off by 1 narrow char: Bold blink under strike [Λ̊][チ][text] Double width, double height. Should be red top, magenta bottom, blue DW only: -#3Bold blink underline [Λ̊][チ][text] -#4Bold blink underline [Λ̊][チ][text] -#6Bold blink underline [Λ̊][チ][text] +#3Bold blink under strike [Λ̊][チ][text] +#4Bold blink under strike [Λ̊][チ][text] +#6Bold blink under strike [Λ̊][チ][text] diff --git a/test/eccref.py b/test/eccref.py index 4474304..f39fa5e 100644 --- a/test/eccref.py +++ b/test/eccref.py @@ -1,184 +1,10 @@ +import sys import numbers import itertools -def jacobi(n,m): - """Compute the Jacobi symbol. - - The special case of this when m is prime is the Legendre symbol, - which is 0 if n is congruent to 0 mod m; 1 if n is congruent to a - non-zero square number mod m; -1 if n is not congruent to any - square mod m. - - """ - assert m & 1 - acc = 1 - while True: - n %= m - if n == 0: - return 0 - while not (n & 1): - n >>= 1 - if (m & 7) not in {1,7}: - acc *= -1 - if n == 1: - return acc - if (n & 3) == 3 and (m & 3) == 3: - acc *= -1 - n, m = m, n - -class SqrtModP(object): - """Class for finding square roots of numbers mod p. - - p must be an odd prime (but its primality is not checked).""" - - def __init__(self, p): - p = abs(p) - assert p & 1 - self.p = p - - # Decompose p as 2^e k + 1 for odd k. - self.k = p-1 - self.e = 0 - while not (self.k & 1): - self.k >>= 1 - self.e += 1 - - # Find a non-square mod p. - for self.z in itertools.count(1): - if jacobi(self.z, self.p) == -1: - break - self.zinv = ModP(self.p, self.z).invert() - - def sqrt_recurse(self, a): - ak = pow(a, self.k, self.p) - for i in range(self.e, -1, -1): - if ak == 1: - break - ak = ak*ak % self.p - assert i > 0 - if i == self.e: - return pow(a, (self.k+1) // 2, self.p) - r_prime = self.sqrt_recurse(a * pow(self.z, 2**i, self.p)) - return r_prime * pow(self.zinv, 2**(i-1), self.p) % self.p - - def sqrt(self, a): - j = jacobi(a, self.p) - if j == 0: - return 0 - if j < 0: - raise ValueError("{} has no square root mod {}".format(a, self.p)) - a %= self.p - r = self.sqrt_recurse(a) - assert r*r % self.p == a - # Normalise to the smaller (or 'positive') one of the two roots. - return min(r, self.p - r) +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" - def __str__(self): - return "{}({})".format(type(self).__name__, self.p) - def __repr__(self): - return self.__str__() - -class ModP(object): - """Class that represents integers mod p as a field. - - All the usual arithmetic operations are supported directly, - including division, so you can write formulas in a natural way - without having to keep saying '% p' everywhere or call a - cumbersome modular_inverse() function. - - """ - def __init__(self, p, n=0): - self.p = p - if isinstance(n, type(self)): - self.check(n) - n = n.n - self.n = n % p - def check(self, other): - assert isinstance(other, type(self)) - assert isinstance(self, type(other)) - assert self.p == other.p - def coerce_to(self, other): - if not isinstance(other, type(self)): - other = type(self)(self.p, other) - else: - self.check(other) - return other - def invert(self): - "Internal routine which returns the bare inverse." - if self.n % self.p == 0: - raise ZeroDivisionError("division by {!r}".format(self)) - a = self.n, 1, 0 - b = self.p, 0, 1 - while b[0]: - q = a[0] // b[0] - a = a[0] - q*b[0], a[1] - q*b[1], a[2] - q*b[2] - b, a = a, b - assert abs(a[0]) == 1 - return a[1]*a[0] - def __int__(self): - return self.n - def __add__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (self.n + rhs.n) % self.p) - def __neg__(self): - return type(self)(self.p, -self.n % self.p) - def __radd__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (self.n + rhs.n) % self.p) - def __sub__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (self.n - rhs.n) % self.p) - def __rsub__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (rhs.n - self.n) % self.p) - def __mul__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (self.n * rhs.n) % self.p) - def __rmul__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (self.n * rhs.n) % self.p) - def __div__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (self.n * rhs.invert()) % self.p) - def __rdiv__(self, rhs): - rhs = self.coerce_to(rhs) - return type(self)(self.p, (rhs.n * self.invert()) % self.p) - def __truediv__(self, rhs): return self.__div__(rhs) - def __rtruediv__(self, rhs): return self.__rdiv__(rhs) - def __pow__(self, exponent): - assert exponent >= 0 - n, b_to_n = 1, self - total = type(self)(self.p, 1) - while True: - if exponent & n: - exponent -= n - total *= b_to_n - n *= 2 - if n > exponent: - break - b_to_n *= b_to_n - return total - def __cmp__(self, rhs): - rhs = self.coerce_to(rhs) - return cmp(self.n, rhs.n) - def __eq__(self, rhs): - rhs = self.coerce_to(rhs) - return self.n == rhs.n - def __ne__(self, rhs): - rhs = self.coerce_to(rhs) - return self.n != rhs.n - def __lt__(self, rhs): - raise ValueError("Elements of a modular ring have no ordering") - def __le__(self, rhs): - raise ValueError("Elements of a modular ring have no ordering") - def __gt__(self, rhs): - raise ValueError("Elements of a modular ring have no ordering") - def __ge__(self, rhs): - raise ValueError("Elements of a modular ring have no ordering") - def __str__(self): - return "0x{:x}".format(self.n) - def __repr__(self): - return "{}(0x{:x},0x{:x})".format(type(self).__name__, self.p, self.n) +from numbertheory import * class AffinePoint(object): """Base class for points on an elliptic curve.""" @@ -288,9 +114,9 @@ def __init__(self, p, a, b): def cpoint(self, x, yparity=0): if not hasattr(self, 'sqrtmodp'): - self.sqrtmodp = SqrtModP(self.p) + self.sqrtmodp = RootModP(2, self.p) rhs = x**3 + self.a.n * x + self.b.n - y = self.sqrtmodp.sqrt(rhs) + y = self.sqrtmodp.root(rhs) if (y - yparity) % 2: y = -y return self.point(x, y) @@ -316,9 +142,13 @@ def __add__(self, rhs): xdiff = x2-x1 if xdiff != 0: slope = (y2-y1) / xdiff - else: + elif y1 != 0: assert y1 == y2 slope = (3*x1*x1 + 2*self.curve.a*x1 + 1) / (2*self.curve.b*y1) + else: + # If y1 was 0 as well, then we must have found an + # order-2 point that doubles to the identity. + return self.curve.point() xp = self.curve.b*slope*slope - self.curve.a - x1 - x2 yp = -(y1 + slope * (xp-x1)) return self.curve.point(xp, yp) @@ -330,9 +160,9 @@ def __init__(self, p, a, b): def cpoint(self, x, yparity=0): if not hasattr(self, 'sqrtmodp'): - self.sqrtmodp = SqrtModP(self.p) + self.sqrtmodp = RootModP(2, self.p) rhs = (x**3 + self.a.n * x**2 + x) / self.b - y = self.sqrtmodp.sqrt(int(rhs)) + y = self.sqrtmodp.root(int(rhs)) if (y - yparity) % 2: y = -y return self.point(x, y) @@ -371,11 +201,11 @@ def point(self, *args): def cpoint(self, y, xparity=0): if not hasattr(self, 'sqrtmodp'): - self.sqrtmodp = SqrtModP(self.p) + self.sqrtmodp = RootModP(self.p) y = ModP(self.p, y) y2 = y**2 radicand = (y2 - 1) / (self.d * y2 - self.a) - x = self.sqrtmodp.sqrt(radicand.n) + x = self.sqrtmodp.root(radicand.n) if (x - xparity) % 2: x = -x return self.point(x, y) @@ -384,6 +214,96 @@ def __repr__(self): return "{}(0x{:x}, {}, {})".format( type(self).__name__, self.p, self.d, self.a) +def find_montgomery_power2_order_x_values(p, a): + # Find points on a Montgomery elliptic curve that have order a + # power of 2. + # + # Motivation: both Curve25519 and Curve448 are abelian groups + # whose overall order is a large prime times a small factor of 2. + # The approved base point of each curve generates a cyclic + # subgroup whose order is the large prime. Outside that cyclic + # subgroup there are many other points that have large prime + # order, plus just a handful that have tiny order. If one of the + # latter is presented to you as a Diffie-Hellman public value, + # nothing useful is going to happen, and RFC 7748 says we should + # outlaw those values. And any actual attempt to outlaw them is + # going to need to know what they are, either to check for each + # one directly, or to use them as test cases for some other + # approach. + # + # In a group of order p 2^k, an obvious way to search for points + # with order dividing 2^k is to generate random group elements and + # raise them to the power p. That guarantees that you end up with + # _something_ with order dividing 2^k (even if it's boringly the + # identity). And you also know from theory how many such points + # you expect to exist, so you can count the distinct ones you've + # found, and stop once you've got the right number. + # + # But that isn't actually good enough to find all the public + # values that are problematic! The reason why not is that in + # Montgomery key exchange we don't actually use a full elliptic + # curve point: we only use its x-coordinate. And the formulae for + # doubling and differential addition on x-coordinates can accept + # some values that don't correspond to group elements _at all_ + # without detecting any error - and some of those nonsense x + # coordinates can also behave like low-order points. + # + # (For example, the x-coordinate -1 in Curve25519 is such a value. + # The reference ECC code in this module will raise an exception if + # you call curve25519.cpoint(-1): it corresponds to no valid point + # at all. But if you feed it into the doubling formula _anyway_, + # it doubles to the valid curve point with x-coord 0, which in + # turn doubles to the curve identity. Bang.) + # + # So we use an alternative approach which discards the group + # theory of the actual elliptic curve, and focuses purely on the + # doubling formula as an algebraic transformation on Z_p. Our + # question is: what values of x have the property that if you + # iterate the doubling map you eventually end up dividing by zero? + # To answer that, we must solve cubics and quartics mod p, via the + # code in numbertheory.py for doing so. + + E = EquationSolverModP(p) + + def viableSolutions(it): + for x in it: + try: + yield int(x) + except ValueError: + pass # some field-extension element that isn't a real value + + def valuesDoublingTo(y): + # The doubling formula for a Montgomery curve point given only + # by x coordinate is (x+1)^2(x-1)^2 / (4(x^3+ax^2+x)). + # + # If we want to find a point that doubles to some particular + # value, we can set that formula equal to y and expand to get the + # quartic equation x^4 + (-4y)x^3 + (-4ay-2)x^2 + (-4y)x + 1 = 0. + return viableSolutions(E.solve_monic_quartic(-4*y, -4*a*y-2, -4*y, 1)) + + queue = [] + qset = set() + pos = 0 + def insert(x): + if x not in qset: + queue.append(x) + qset.add(x) + + # Our ultimate aim is to find points that end up going to the + # curve identity / point at infinity after some number of + # doublings. So our starting point is: what values of x make the + # denominator of the doubling formula zero? + for x in viableSolutions(E.solve_monic_cubic(a, 1, 0)): + insert(x) + + while pos < len(queue): + y = queue[pos] + pos += 1 + for x in valuesDoublingTo(y): + insert(x) + + return queue + p256 = WeierstrassCurve(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, -3, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b) p256.G = p256.point(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5) p256.G_order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 @@ -399,7 +319,13 @@ def __repr__(self): curve25519 = MontgomeryCurve(2**255-19, 0x76d06, 1) curve25519.G = curve25519.cpoint(9) +curve448 = MontgomeryCurve(2**448-2**224-1, 0x262a6, 1) +curve448.G = curve448.cpoint(5) + ed25519 = TwistedEdwardsCurve(2**255-19, 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3, -1) ed25519.G = ed25519.point(0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a,0x6666666666666666666666666666666666666666666666666666666666666658) ed25519.G_order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed +ed448 = TwistedEdwardsCurve(2**448-2**224-1, -39081, +1) +ed448.G = ed448.point(0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e,0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14) +ed448.G_order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 diff --git a/test/mpu-check.pl b/test/mpu-check.pl new file mode 100755 index 0000000..f5f0e81 --- /dev/null +++ b/test/mpu-check.pl @@ -0,0 +1,19 @@ +#!/usr/bin/perl + +# Trivial command-line client for the function +# Math::Prime::Util::verify_prime, which checks a certificate of +# primality in MPU format. + +use strict; +use warnings; +use Math::Prime::Util; + +Math::Prime::Util::prime_set_config(verbose => 1); + +my $cert = ""; +$cert .= $_ while <<>>; + +my $success = Math::Prime::Util::verify_prime($cert); + +die "verification failed\n" unless $success; +warn "verification succeeded\n"; diff --git a/test/numbertheory.py b/test/numbertheory.py new file mode 100644 index 0000000..58f601e --- /dev/null +++ b/test/numbertheory.py @@ -0,0 +1,638 @@ +import sys +import numbers +import itertools +import unittest + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + +def invert(a, b): + "Multiplicative inverse of a mod b. a,b must be coprime." + A = (a, 1, 0) + B = (b, 0, 1) + while B[0]: + q = A[0] // B[0] + A, B = B, tuple(Ai - q*Bi for Ai, Bi in zip(A, B)) + assert abs(A[0]) == 1 + return A[1]*A[0] % b + +def jacobi(n,m): + """Compute the Jacobi symbol. + + The special case of this when m is prime is the Legendre symbol, + which is 0 if n is congruent to 0 mod m; 1 if n is congruent to a + non-zero square number mod m; -1 if n is not congruent to any + square mod m. + + """ + assert m & 1 + acc = 1 + while True: + n %= m + if n == 0: + return 0 + while not (n & 1): + n >>= 1 + if (m & 7) not in {1,7}: + acc *= -1 + if n == 1: + return acc + if (n & 3) == 3 and (m & 3) == 3: + acc *= -1 + n, m = m, n + +class CyclicGroupRootFinder(object): + """Class for finding rth roots in a cyclic group. r must be prime.""" + + # Basic strategy: + # + # We write |G| = r^k u, with u coprime to r. This gives us a + # nested sequence of subgroups G = G_0 > G_1 > ... > G_k, each + # with index r in its predecessor. G_0 is the whole group, and the + # innermost G_k has order u. + # + # Within G_k, you can take an rth root by raising an element to + # the power of (r^{-1} mod u). If k=0 (so G = G_0 = G_k) then + # that's all that's needed: every element has a unique rth root. + # But if k>0, then things go differently. + # + # Define the 'rank' of an element g as the highest i such that + # g \in G_i. Elements of rank 0 are the non-rth-powers: they don't + # even _have_ an rth root. Elements of rank k are the easy ones to + # take rth roots of, as above. + # + # In between, you can follow an inductive process, as long as you + # know one element z of rank 0. Suppose we're trying to take the + # rth root of some g with rank i. Repeatedly multiply g by z^{r^i} + # until its rank increases; then take the root of that + # (recursively), and divide off z^{r^{i-1}} once you're done. + + def __init__(self, r, order): + self.order = order # order of G + self.r = r + self.k = next(k for k in itertools.count() + if self.order % (r**(k+1)) != 0) + self.u = self.order // (r**self.k) + self.z = next(z for z in self.iter_elements() + if self.index(z) == 0) + self.zinv = self.inverse(self.z) + self.root_power = invert(self.r, self.u) if self.u > 1 else 0 + + self.roots_of_unity = {self.identity()} + if self.k > 0: + exponent = self.order // self.r + for z in self.iter_elements(): + root_of_unity = self.pow(z, exponent) + if root_of_unity not in self.roots_of_unity: + self.roots_of_unity.add(root_of_unity) + if len(self.roots_of_unity) == r: + break + + def index(self, g): + h = self.pow(g, self.u) + for i in range(self.k+1): + if h == self.identity(): + return self.k - i + h = self.pow(h, self.r) + assert False, ("Not a cyclic group! Raising {} to u r^k should give e." + .format(g)) + + def all_roots(self, g): + try: + r = self.root(g) + except ValueError: + return [] + return {r * rou for rou in self.roots_of_unity} + + def root(self, g): + i = self.index(g) + if i == 0 and self.k > 0: + raise ValueError("{} has no {}th root".format(g, self.r)) + out = self.root_recurse(g, i) + assert self.pow(out, self.r) == g + return out + + def root_recurse(self, g, i): + if i == self.k: + return self.pow(g, self.root_power) + z_in = self.pow(self.z, self.r**i) + z_out = self.pow(self.zinv, self.r**(i-1)) + adjust = self.identity() + while True: + g = self.mul(g, z_in) + adjust = self.mul(adjust, z_out) + i2 = self.index(g) + if i2 > i: + return self.mul(self.root_recurse(g, i2), adjust) + +class AdditiveGroupRootFinder(CyclicGroupRootFinder): + """Trivial test subclass for CyclicGroupRootFinder. + + Represents a cyclic group of any order additively, as the integers + mod n under addition. This makes root-finding trivial without + having to use the complicated algorithm above, and therefore it's + a good way to test the complicated algorithm under conditions + where the right answers are obvious.""" + + def __init__(self, r, order): + super().__init__(r, order) + + def mul(self, x, y): + return (x + y) % self.order + def pow(self, x, n): + return (x * n) % self.order + def inverse(self, x): + return (-x) % self.order + def identity(self): + return 0 + def iter_elements(self): + return range(self.order) + +class TestCyclicGroupRootFinder(unittest.TestCase): + def testRootFinding(self): + for order in 10, 11, 12, 18: + grf = AdditiveGroupRootFinder(3, order) + for i in range(order): + try: + r = grf.root(i) + except ValueError: + r = None + + if order % 3 == 0 and i % 3 != 0: + self.assertEqual(r, None) + else: + self.assertEqual(r*3 % order, i) + +class RootModP(CyclicGroupRootFinder): + """The live class that can take rth roots mod a prime.""" + + def __init__(self, r, p): + self.modulus = p + super().__init__(r, p-1) + + def mul(self, x, y): + return (x * y) % self.modulus + def pow(self, x, n): + return pow(x, n, self.modulus) + def inverse(self, x): + return invert(x, self.modulus) + def identity(self): + return 1 + def iter_elements(self): + return range(1, self.modulus) + + def root(self, g): + return 0 if g == 0 else super().root(g) + +class ModP(object): + """Class that represents integers mod p as a field. + + All the usual arithmetic operations are supported directly, + including division, so you can write formulas in a natural way + without having to keep saying '% p' everywhere or call a + cumbersome modular_inverse() function. + + """ + def __init__(self, p, n=0): + self.p = p + if isinstance(n, type(self)): + self.check(n) + n = n.n + self.n = n % p + def check(self, other): + assert isinstance(other, type(self)) + assert isinstance(self, type(other)) + assert self.p == other.p + def coerce_to(self, other): + if not isinstance(other, type(self)): + other = type(self)(self.p, other) + else: + self.check(other) + return other + def __int__(self): + return self.n + def __add__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (self.n + rhs.n) % self.p) + def __neg__(self): + return type(self)(self.p, -self.n % self.p) + def __radd__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (self.n + rhs.n) % self.p) + def __sub__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (self.n - rhs.n) % self.p) + def __rsub__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (rhs.n - self.n) % self.p) + def __mul__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (self.n * rhs.n) % self.p) + def __rmul__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (self.n * rhs.n) % self.p) + def __div__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (self.n * invert(rhs.n, self.p)) % self.p) + def __rdiv__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, (rhs.n * invert(self.n, self.p)) % self.p) + def __truediv__(self, rhs): return self.__div__(rhs) + def __rtruediv__(self, rhs): return self.__rdiv__(rhs) + def __pow__(self, exponent): + assert exponent >= 0 + n, b_to_n = 1, self + total = type(self)(self.p, 1) + while True: + if exponent & n: + exponent -= n + total *= b_to_n + n *= 2 + if n > exponent: + break + b_to_n *= b_to_n + return total + def __cmp__(self, rhs): + rhs = self.coerce_to(rhs) + return cmp(self.n, rhs.n) + def __eq__(self, rhs): + rhs = self.coerce_to(rhs) + return self.n == rhs.n + def __ne__(self, rhs): + rhs = self.coerce_to(rhs) + return self.n != rhs.n + def __lt__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __le__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __gt__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __ge__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __str__(self): + return "0x{:x}".format(self.n) + def __repr__(self): + return "{}(0x{:x},0x{:x})".format(type(self).__name__, self.p, self.n) + def __hash__(self): + return hash((type(self).__name__, self.p, self.n)) + +class QuadraticFieldExtensionModP(object): + """Class representing Z_p[sqrt(d)] for a given non-square d. + """ + def __init__(self, p, d, n=0, m=0): + self.p = p + self.d = d + if isinstance(n, ModP): + assert self.p == n.p + n = n.n + if isinstance(m, ModP): + assert self.p == m.p + m = m.n + if isinstance(n, type(self)): + self.check(n) + m += n.m + n = n.n + self.n = n % p + self.m = m % p + + @classmethod + def constructor(cls, p, d): + return lambda *args: cls(p, d, *args) + + def check(self, other): + assert isinstance(other, type(self)) + assert isinstance(self, type(other)) + assert self.p == other.p + assert self.d == other.d + def coerce_to(self, other): + if not isinstance(other, type(self)): + other = type(self)(self.p, self.d, other) + else: + self.check(other) + return other + def __int__(self): + if self.m != 0: + raise ValueError("Can't coerce a non-element of Z_{} to integer" + .format(self.p)) + return int(self.n) + def __add__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, self.d, + (self.n + rhs.n) % self.p, + (self.m + rhs.m) % self.p) + def __neg__(self): + return type(self)(self.p, self.d, + -self.n % self.p, + -self.m % self.p) + def __radd__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, self.d, + (self.n + rhs.n) % self.p, + (self.m + rhs.m) % self.p) + def __sub__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, self.d, + (self.n - rhs.n) % self.p, + (self.m - rhs.m) % self.p) + def __rsub__(self, rhs): + rhs = self.coerce_to(rhs) + return type(self)(self.p, self.d, + (rhs.n - self.n) % self.p, + (rhs.m - self.m) % self.p) + def __mul__(self, rhs): + rhs = self.coerce_to(rhs) + n, m, N, M = self.n, self.m, rhs.n, rhs.m + return type(self)(self.p, self.d, + (n*N + self.d*m*M) % self.p, + (n*M + m*N) % self.p) + def __rmul__(self, rhs): + return self.__mul__(rhs) + def __div__(self, rhs): + rhs = self.coerce_to(rhs) + n, m, N, M = self.n, self.m, rhs.n, rhs.m + # (n+m sqrt d)/(N+M sqrt d) = (n+m sqrt d)(N-M sqrt d)/(N^2-dM^2) + denom = (N*N - self.d*M*M) % self.p + if denom == 0: + raise ValueError("division by zero") + recipdenom = invert(denom, self.p) + return type(self)(self.p, self.d, + (n*N - self.d*m*M) * recipdenom % self.p, + (m*N - n*M) * recipdenom % self.p) + def __rdiv__(self, rhs): + rhs = self.coerce_to(rhs) + return rhs.__div__(self) + def __truediv__(self, rhs): return self.__div__(rhs) + def __rtruediv__(self, rhs): return self.__rdiv__(rhs) + def __pow__(self, exponent): + assert exponent >= 0 + n, b_to_n = 1, self + total = type(self)(self.p, self.d, 1) + while True: + if exponent & n: + exponent -= n + total *= b_to_n + n *= 2 + if n > exponent: + break + b_to_n *= b_to_n + return total + def __cmp__(self, rhs): + rhs = self.coerce_to(rhs) + return cmp((self.n, self.m), (rhs.n, rhs.m)) + def __eq__(self, rhs): + rhs = self.coerce_to(rhs) + return self.n == rhs.n and self.m == rhs.m + def __ne__(self, rhs): + rhs = self.coerce_to(rhs) + return self.n != rhs.n or self.m != rhs.m + def __lt__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __le__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __gt__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __ge__(self, rhs): + raise ValueError("Elements of a modular ring have no ordering") + def __str__(self): + if self.m == 0: + return "0x{:x}".format(self.n) + else: + return "0x{:x}+0x{:x}*sqrt({:d})".format(self.n, self.m, self.d) + def __repr__(self): + return "{}(0x{:x},0x{:x},0x{:x},0x{:x})".format( + type(self).__name__, self.p, self.d, self.n, self.m) + def __hash__(self): + return hash((type(self).__name__, self.p, self.d, self.n, self.m)) + +class RootInQuadraticExtension(CyclicGroupRootFinder): + """Take rth roots in the quadratic extension of Z_p.""" + + def __init__(self, r, p, d): + self.modulus = p + self.constructor = QuadraticFieldExtensionModP.constructor(p, d) + super().__init__(r, p*p-1) + + def mul(self, x, y): + return x * y + def pow(self, x, n): + return x ** n + def inverse(self, x): + return 1/x + def identity(self): + return self.constructor(1, 0) + def iter_elements(self): + p = self.modulus + for n_plus_m in range(1, 2*p-1): + n_min = max(0, n_plus_m-(p-1)) + n_max = min(p-1, n_plus_m) + for n in range(n_min, n_max + 1): + m = n_plus_m - n + assert(0 <= n < p) + assert(0 <= m < p) + assert(n != 0 or m != 0) + yield self.constructor(n, m) + + def root(self, g): + return 0 if g == 0 else super().root(g) + +class EquationSolverModP(object): + """Class that can solve quadratics, cubics and quartics over Z_p. + + p must be a nontrivial prime (bigger than 3). + """ + + # This is a port to Z_p of reasonably standard algorithms for + # solving quadratics, cubics and quartics over the reals. + # + # When you solve a cubic in R, you sometimes have to deal with + # intermediate results that are complex numbers. In particular, + # you have to solve a quadratic whose coefficients are in R but + # its roots may be complex, and then having solved that quadratic, + # you need to iterate over all three cube roots of the solution in + # order to recover all the roots of your cubic. (Even if the cubic + # ends up having three real roots, you can't calculate them + # without going through those complex intermediate values.) + # + # So over Z_p, the same thing applies: we're going to need to be + # able to solve any quadratic with coefficients in Z_p, even if + # its discriminant turns out not to be a quadratic residue mod p, + # and then we'll need to find _three_ cube roots of the result, + # even if p == 2 (mod 3) so that numbers only have one cube root + # each. + # + # Both of these problems can be solved at once if we work in the + # finite field GF(p^2), i.e. make a quadratic field extension of + # Z_p by adjoining a square root of some non-square d. The + # multiplicative group of GF(p^2) is cyclic and has order p^2-1 = + # (p-1)(p+1), with the mult group of Z_p forming the unique + # subgroup of order (p-1) within it. So we've multiplied the group + # order by p+1, which is even (since by assumption p > 3), and + # therefore a square root is now guaranteed to exist for every + # number in the Z_p subgroup. Moreover, no matter whether p itself + # was congruent to 1 or 2 mod 3, p^2 is always congruent to 1, + # which means that the mult group of GF(p^2) has order divisible + # by 3. So there are guaranteed to be three distinct cube roots of + # unity, and hence, three cube roots of any number that's a cube + # at all. + # + # Quartics don't introduce any additional problems. To solve a + # quartic, you factorise it into two quadratic factors, by solving + # a cubic to find one of the coefficients. So if you can already + # solve cubics, then you're more or less done. The only wrinkle is + # that the two quadratic factors will have coefficients in GF(p^2) + # but not necessarily in Z_p. But that doesn't stop us at least + # _trying_ to solve them by taking square roots in GF(p^2) - and + # if the discriminant of one of those quadratics has is not a + # square even in GF(p^2), then its solutions will only exist if + # you escalate further to GF(p^4), in which case the answer is + # simply that there aren't any solutions in Z_p to that quadratic. + + def __init__(self, p): + self.p = p + self.nonsquare_mod_p = d = RootModP(2, p).z + self.constructor = QuadraticFieldExtensionModP.constructor(p, d) + self.sqrt = RootInQuadraticExtension(2, p, d) + self.cbrt = RootInQuadraticExtension(3, p, d) + + def solve_quadratic(self, a, b, c): + "Solve ax^2 + bx + c = 0." + a, b, c = map(self.constructor, (a, b, c)) + assert a != 0 + return self.solve_monic_quadratic(b/a, c/a) + + def solve_monic_quadratic(self, b, c): + "Solve x^2 + bx + c = 0." + b, c = map(self.constructor, (b, c)) + s = b/2 + return [y - s for y in self.solve_depressed_quadratic(c - s*s)] + + def solve_depressed_quadratic(self, c): + "Solve x^2 + c = 0." + return self.sqrt.all_roots(-c) + + def solve_cubic(self, a, b, c, d): + "Solve ax^3 + bx^2 + cx + d = 0." + a, b, c, d = map(self.constructor, (a, b, c, d)) + assert a != 0 + return self.solve_monic_cubic(b/a, c/a, d/a) + + def solve_monic_cubic(self, b, c, d): + "Solve x^3 + bx^2 + cx + d = 0." + b, c, d = map(self.constructor, (b, c, d)) + s = b/3 + return [y - s for y in self.solve_depressed_cubic( + c - 3*s*s, 2*s*s*s - c*s + d)] + + def solve_depressed_cubic(self, c, d): + "Solve x^3 + cx + d = 0." + c, d = map(self.constructor, (c, d)) + solutions = set() + # To solve x^3 + cx + d = 0, set p = -c/3, then + # substitute x = z + p/z to get z^6 + d z^3 + p^3 = 0. + # Solve that quadratic for z^3, then take cube roots. + p = -c/3 + for z3 in self.solve_monic_quadratic(d, p**3): + # As I understand the theory, we _should_ only need to + # take cube roots of one root of that quadratic: the other + # one should give the same set of answers after you map + # each one through z |-> z+p/z. But speed isn't at a + # premium here, so I'll do this the way that must work. + for z in self.cbrt.all_roots(z3): + solutions.add(z + p/z) + return solutions + + def solve_quartic(self, a, b, c, d, e): + "Solve ax^4 + bx^3 + cx^2 + dx + e = 0." + a, b, c, d, e = map(self.constructor, (a, b, c, d, e)) + assert a != 0 + return self.solve_monic_quartic(b/a, c/a, d/a, e/a) + + def solve_monic_quartic(self, b, c, d, e): + "Solve x^4 + bx^3 + cx^2 + dx + e = 0." + b, c, d, e = map(self.constructor, (b, c, d, e)) + s = b/4 + return [y - s for y in self.solve_depressed_quartic( + c - 6*s*s, d - 2*c*s + 8*s*s*s, e - d*s + c*s*s - 3*s*s*s*s)] + + def solve_depressed_quartic(self, c, d, e): + "Solve x^4 + cx^2 + dx + e = 0." + c, d, e = map(self.constructor, (c, d, e)) + solutions = set() + # To solve an equation of this form, we search for a value y + # such that subtracting the original polynomial from (x^2+y)^2 + # yields a quadratic of the special form (ux+v)^2. + # + # Then our equation is rewritten as (x^2+y)^2 - (ux+v)^2 = 0 + # i.e. ((x^2+y) + (ux+v)) ((x^2+y) - (ux+v)) = 0 + # i.e. the product of two quadratics, each of which we then solve. + # + # To find y, we write down the discriminant of the quadratic + # (x^2+y)^2 - (x^4 + cx^2 + dx + e) and set it to 0, which + # gives a cubic in y. Maxima gives the coefficients as + # (-8)y^3 + (4c)y^2 + (8e)y + (d^2-4ce). + # + # As above, we _should_ only need one value of y. But I go + # through them all just in case, because I don't care about + # speed, and because checking the assertions inside this loop + # for every value is extra reassurance that I've done all of + # this right. + for y in self.solve_cubic(-8, 4*c, 8*e, d*d-4*c*e): + # Subtract the original equation from (x^2+y)^2 to get the + # coefficients of our quadratic residual. + A, B, C = 2*y-c, -d, y*y-e + # Expect that to have zero discriminant, i.e. a repeated root. + assert B*B - 4*A*C == 0 + # If (Ax^2+Bx+C) == (ux+v)^2 then we have u^2=A, 2uv=B, v^2=C. + # So we can either recover u as sqrt(A) or v as sqrt(C), and + # whichever we did, find the other from B by division. But + # either of the end coefficients might be zero, so we have + # to be prepared to try either option. + try: + if A != 0: + u = self.sqrt.root(A) + v = B/(2*u) + elif C != 0: + v = self.sqrt.root(C) + u = B/(2*v) + else: + # One last possibility is that all three coefficients + # of our residual quadratic are 0, in which case, + # obviously, u=v=0 as well. + u = v = 0 + except ValueError: + # If Ax^2+Bx+C looked like a perfect square going by + # its discriminant, but actually taking the square + # root of A or C threw an exception, that means that + # it's the square of a polynomial whose coefficients + # live in a yet-higher field extension of Z_p. In that + # case we're not going to end up with roots of the + # original quartic in Z_p if we start from here! + continue + # So now our quartic is factorised into the form + # (x^2 - ux - v + y) (x^2 + ux + v + y). + for x in self.solve_monic_quadratic(-u, y-v): + solutions.add(x) + for x in self.solve_monic_quadratic(u, y+v): + solutions.add(x) + return solutions + +class EquationSolverTest(unittest.TestCase): + def testQuadratic(self): + E = EquationSolverModP(11) + solns = E.solve_quadratic(3, 2, 6) + self.assertEqual(sorted(map(str, solns)), ["0x1", "0x2"]) + + def testCubic(self): + E = EquationSolverModP(11) + solns = E.solve_cubic(7, 2, 0, 2) + self.assertEqual(sorted(map(str, solns)), ["0x1", "0x2", "0x3"]) + + def testQuartic(self): + E = EquationSolverModP(11) + solns = E.solve_quartic(9, 9, 7, 1, 7) + self.assertEqual(sorted(map(str, solns)), ["0x1", "0x2", "0x3", "0x4"]) + +if __name__ == "__main__": + import sys + if sys.argv[1:] == ["--test"]: + sys.argv[1:2] = [] + unittest.main() diff --git a/test/primegen.py b/test/primegen.py new file mode 100755 index 0000000..6964099 --- /dev/null +++ b/test/primegen.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 + +from testcrypt import * +import base64 +import argparse +import itertools + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + +def main(): + opener = lambda mode: lambda fname: lambda: argparse.FileType(mode)(fname) + parser = argparse.ArgumentParser(description='') + IntArg = lambda x: int(x, 0) + parser.add_argument("bits", type=IntArg, nargs="?", default=1024) + parser.add_argument("-s", "--seed") + parser.add_argument("-f", "--firstbits", type=IntArg, default=1) + parser.add_argument("--fast", action='store_const', + dest='policy', const='provable_fast') + parser.add_argument("--complex", action='store_const', + dest='policy', const='provable_maurer_complex') + parser.add_argument("-q", "--quiet", action='store_true') + parser.add_argument("-b", "--binary", action='store_const', + dest='fmt', const='{:b}') + parser.add_argument("-x", "--hex", action='store_const', + dest='fmt', const='{:x}') + parser.add_argument("-o", "--output", type=opener("w"), + default=opener("w")("-"), + help="file to write the prime to") + parser.add_argument("--mpu", type=opener("w"), + help="MPU certificate output file") + parser.add_argument("--safe", action='store_true') + parser.set_defaults(fmt='{:d}', policy='provable_maurer_simple') + args = parser.parse_args() + + seed = args.seed + if seed is None: + with open("/dev/urandom", "rb") as f: + seed = base64.b64encode(f.read(32)).decode("ASCII") + + if not args.quiet: + print("seed =", seed) + random_make_prng('sha256', seed) + assert args.firstbits > 0 + nfirst = next(i for i in itertools.count() if (args.firstbits >> i) == 0) + pgc = primegen_new_context(args.policy) + if args.safe: + while True: + pcs_q = pcs_new_with_firstbits(args.bits - 1, + args.firstbits, nfirst) + pcs_try_sophie_germain(pcs_q) + q = primegen_generate(pgc, pcs_q) + pcs = pcs_new(args.bits) + pcs_require_residue_1_mod_prime(pcs, q) + pcs_set_oneshot(pcs) + p = primegen_generate(pgc, pcs) + if p is not None: + break + else: + pcs = pcs_new_with_firstbits(args.bits, args.firstbits, nfirst) + p = primegen_generate(pgc, pcs) + + with args.output() as f: + print(args.fmt.format(int(p)), file=f) + + if args.mpu is not None: + s = primegen_mpu_certificate(pgc, p) + with args.mpu() as f: + f.write(s.decode("ASCII")) + +if __name__ == '__main__': + main() diff --git a/test/sclog/sclog.c b/test/sclog/sclog.c index 3512fad..2d2adbf 100644 --- a/test/sclog/sclog.c +++ b/test/sclog/sclog.c @@ -212,6 +212,7 @@ static void wrap_malloc_pre(void *wrapctx, void **user_data) { logging_paused++; *user_data = drwrap_get_arg(wrapctx, 0); + dr_fprintf(outfile, "malloc %"PRIuMAX"\n", (uintmax_t)*user_data); } static void wrap_free_pre(void *wrapctx, void **user_data) { @@ -225,6 +226,7 @@ static void wrap_realloc_pre(void *wrapctx, void **user_data) void *ptr = drwrap_get_arg(wrapctx, 0); freed(ptr); *user_data = drwrap_get_arg(wrapctx, 1); + dr_fprintf(outfile, "realloc %"PRIuMAX"\n", (uintmax_t)*user_data); } static void wrap_alloc_post(void *wrapctx, void *user_data) { @@ -388,10 +390,10 @@ static dr_emit_flags_t instrument_instr( if (instr_reads_memory(instr) || instr_writes_memory(instr)) { for (int i = 0, limit = instr_num_srcs(instr); i < limit; i++) try_mem_opnd(drcontext, bb, instr, &loc, - instr_get_src(instr, i), false); + instr_get_src(instr, i), instr_writes_memory(instr)); for (int i = 0, limit = instr_num_dsts(instr); i < limit; i++) try_mem_opnd(drcontext, bb, instr, &loc, - instr_get_dst(instr, i), false); + instr_get_dst(instr, i), instr_writes_memory(instr)); } /* @@ -400,6 +402,7 @@ static dr_emit_flags_t instrument_instr( int opcode = instr_get_opcode(instr); switch (opcode) { +#if defined(X86) case OP_div: case OP_idiv: /* @@ -413,6 +416,21 @@ static dr_emit_flags_t instrument_instr( 3, instr_get_src(instr, 2), instr_get_src(instr, 0), OPND_CREATE_INTPTR(loc)); break; +#endif +#if defined(AARCH64) + case OP_sdiv: + case OP_udiv: + /* + * AArch64 hardware divisions. 0 = numerator, 1 = denominator. + */ + instr_format_location(instr, &loc); + dr_insert_clean_call( + drcontext, bb, instr, (void *)log_div, false, + 3, instr_get_src(instr, 0), instr_get_src(instr, 1), + OPND_CREATE_INTPTR(loc)); + break; +#endif +#if defined(X86) case OP_shl: case OP_shr: case OP_sar: @@ -422,30 +440,51 @@ static dr_emit_flags_t instrument_instr( case OP_rol: case OP_ror: case OP_rcl: - case OP_rcr: + case OP_rcr: { /* * Shift instructions. If they're register-controlled, log the * shift count. */ - { - opnd_t shiftcount = instr_get_src(instr, 0); - if (!opnd_is_immed(shiftcount)) { - reg_id_t r0; - drreg_status_t st; - st = drreg_reserve_register(drcontext, bb, instr, NULL, &r0); - DR_ASSERT(st == DRREG_SUCCESS); - opnd_t op_r0 = opnd_create_reg(r0); - instrlist_preinsert(bb, instr, INSTR_CREATE_movzx( - drcontext, op_r0, shiftcount)); - instr_format_location(instr, &loc); - dr_insert_clean_call( - drcontext, bb, instr, (void *)log_var_shift, false, - 2, op_r0, OPND_CREATE_INTPTR(loc)); - st = drreg_unreserve_register(drcontext, bb, instr, r0); - DR_ASSERT(st == DRREG_SUCCESS); - } + opnd_t shiftcount = instr_get_src(instr, 0); + if (!opnd_is_immed(shiftcount)) { + reg_id_t r0; + drreg_status_t st; + st = drreg_reserve_register(drcontext, bb, instr, NULL, &r0); + DR_ASSERT(st == DRREG_SUCCESS); + opnd_t op_r0 = opnd_create_reg(r0); + instr_t *movzx = INSTR_CREATE_movzx(drcontext, op_r0, shiftcount); + instr_set_translation(movzx, instr_get_app_pc(instr)); + instrlist_preinsert(bb, instr, movzx); + instr_format_location(instr, &loc); + dr_insert_clean_call( + drcontext, bb, instr, (void *)log_var_shift, false, + 2, op_r0, OPND_CREATE_INTPTR(loc)); + st = drreg_unreserve_register(drcontext, bb, instr, r0); + DR_ASSERT(st == DRREG_SUCCESS); } break; + } +#endif +#if defined(AARCH64) + case OP_lslv: + case OP_asrv: + case OP_lsrv: + case OP_rorv: { + /* + * AArch64 variable shift instructions. + */ + opnd_t shiftcount = instr_get_src(instr, 1); + DR_ASSERT(opnd_is_reg(shiftcount)); + reg_id_t shiftreg = opnd_get_reg(shiftcount); + if (shiftreg >= DR_REG_W0 && shiftreg <= DR_REG_WSP) + shiftreg = reg_32_to_64(shiftreg); + instr_format_location(instr, &loc); + dr_insert_clean_call( + drcontext, bb, instr, (void *)log_var_shift, false, + 2, opnd_create_reg(shiftreg), OPND_CREATE_INTPTR(loc)); + break; + } +#endif } return DR_EMIT_DEFAULT; @@ -510,6 +549,7 @@ static void try_wrap_fn(const module_data_t *module, const char *name, static void load_module( void *drcontext, const module_data_t *module, bool loaded) { + bool libc = !strncmp(dr_module_preferred_name(module), "libc", 4); #define TRY_WRAP(fn, pre, post) do \ { \ @@ -520,28 +560,33 @@ static void load_module( if (loaded) { TRY_WRAP("log_to_file_real", wrap_logsetfile, NULL); TRY_WRAP("dry_run_real", NULL, wrap_dryrun); - TRY_WRAP("malloc", wrap_malloc_pre, wrap_alloc_post); - TRY_WRAP("realloc", wrap_realloc_pre, wrap_alloc_post); - TRY_WRAP("free", wrap_free_pre, unpause_post); - TRY_WRAP("memset", wrap_memset_pre, unpause_post); - - /* - * More strangely named versions of standard C library - * functions, which I've observed in practice to be where the - * calls end up. I think these are probably selected by - * STT_IFUNC in libc.so, so that the normally named version of - * the function is never reached at all. - * - * This list is not expected to be complete. If you re-run - * this test on a different platform and find control flow - * diverging inside some libc function that looks as if it's - * another name for malloc or memset or whatever, then you may - * need to add more aliases here to stop the test failing. - */ - TRY_WRAP("__GI___libc_malloc", wrap_malloc_pre, wrap_alloc_post); - TRY_WRAP("__GI___libc_realloc", wrap_realloc_pre, wrap_alloc_post); - TRY_WRAP("__GI___libc_free", wrap_free_pre, unpause_post); - TRY_WRAP("__memset_sse2_unaligned", wrap_memset_pre, unpause_post); + if (libc) { + TRY_WRAP("malloc", wrap_malloc_pre, wrap_alloc_post); + TRY_WRAP("realloc", wrap_realloc_pre, wrap_alloc_post); + TRY_WRAP("free", wrap_free_pre, unpause_post); + TRY_WRAP("memset", wrap_memset_pre, unpause_post); + + /* + * More strangely named versions of standard C library + * functions, which I've observed in practice to be where the + * calls end up. I think these are probably selected by + * STT_IFUNC in libc.so, so that the normally named version of + * the function is never reached at all. + * + * This list is not expected to be complete. If you re-run + * this test on a different platform and find control flow + * diverging inside some libc function that looks as if it's + * another name for malloc or memset or whatever, then you may + * need to add more aliases here to stop the test failing. + */ + TRY_WRAP("__GI___libc_malloc", wrap_malloc_pre, wrap_alloc_post); + TRY_WRAP("__libc_malloc", wrap_malloc_pre, wrap_alloc_post); + TRY_WRAP("__GI___libc_realloc", wrap_realloc_pre, wrap_alloc_post); + TRY_WRAP("__GI___libc_free", wrap_free_pre, unpause_post); + TRY_WRAP("__memset_sse2_unaligned", wrap_memset_pre, unpause_post); + TRY_WRAP("__memset_sse2", wrap_memset_pre, unpause_post); + TRY_WRAP("cfree", wrap_free_pre, unpause_post); + } } } diff --git a/test/ssh.py b/test/ssh.py new file mode 100644 index 0000000..90eccb7 --- /dev/null +++ b/test/ssh.py @@ -0,0 +1,101 @@ +import sys +import struct +import itertools + +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + +def nbits(n): + # Mimic mp_get_nbits for ordinary Python integers. + assert 0 <= n + smax = next(s for s in itertools.count() if (n >> (1 << s)) == 0) + toret = 0 + for shift in reversed([1 << s for s in range(smax)]): + if n >> shift != 0: + n >>= shift + toret += shift + assert n <= 1 + if n == 1: + toret += 1 + return toret + +def ssh_byte(n): + return struct.pack("B", n) + +def ssh_uint32(n): + return struct.pack(">L", n) + +def ssh_string(s): + return ssh_uint32(len(s)) + s + +def ssh1_mpint(x): + bits = nbits(x) + bytevals = [0xFF & (x >> (8*n)) for n in range((bits-1)//8, -1, -1)] + return struct.pack(">H" + "B" * len(bytevals), bits, *bytevals) + +def ssh2_mpint(x): + bytevals = [0xFF & (x >> (8*n)) for n in range(nbits(x)//8, -1, -1)] + return struct.pack(">L" + "B" * len(bytevals), len(bytevals), *bytevals) + +def decoder(fn): + def decode(s, return_rest = False): + item, length_consumed = fn(s) + if return_rest: + return item, s[length_consumed:] + else: + return item + return decode + +@decoder +def ssh_decode_byte(s): + return struct.unpack_from("B", s, 0)[0], 1 + +@decoder +def ssh_decode_uint32(s): + return struct.unpack_from(">L", s, 0)[0], 4 + +@decoder +def ssh_decode_string(s): + length = ssh_decode_uint32(s) + assert length + 4 <= len(s) + return s[4:length+4], length+4 + +@decoder +def ssh1_get_mpint(s): # returns it unconsumed, still in wire encoding + nbits = struct.unpack_from(">H", s, 0)[0] + nbytes = (nbits + 7) // 8 + assert nbytes + 2 <= len(s) + return s[:nbytes+2], nbytes+2 + +@decoder +def ssh1_decode_mpint(s): + nbits = struct.unpack_from(">H", s, 0)[0] + nbytes = (nbits + 7) // 8 + assert nbytes + 2 <= len(s) + data = s[2:nbytes+2] + v = 0 + for b in struct.unpack("B" * len(data), data): + v = (v << 8) | b + return v, nbytes+2 + +AGENT_MAX_MSGLEN = 262144 + +SSH1_AGENTC_REQUEST_RSA_IDENTITIES = 1 +SSH1_AGENT_RSA_IDENTITIES_ANSWER = 2 +SSH1_AGENTC_RSA_CHALLENGE = 3 +SSH1_AGENT_RSA_RESPONSE = 4 +SSH1_AGENTC_ADD_RSA_IDENTITY = 7 +SSH1_AGENTC_REMOVE_RSA_IDENTITY = 8 +SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9 +SSH_AGENT_FAILURE = 5 +SSH_AGENT_SUCCESS = 6 +SSH2_AGENTC_REQUEST_IDENTITIES = 11 +SSH2_AGENT_IDENTITIES_ANSWER = 12 +SSH2_AGENTC_SIGN_REQUEST = 13 +SSH2_AGENT_SIGN_RESPONSE = 14 +SSH2_AGENTC_ADD_IDENTITY = 17 +SSH2_AGENTC_REMOVE_IDENTITY = 18 +SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19 +SSH2_AGENTC_EXTENSION = 27 + +SSH_AGENT_RSA_SHA2_256 = 2 +SSH_AGENT_RSA_SHA2_512 = 4 diff --git a/test/testcrypt.py b/test/testcrypt.py index 42da9d8..686302c 100644 --- a/test/testcrypt.py +++ b/test/testcrypt.py @@ -6,21 +6,14 @@ import struct from binascii import hexlify +assert sys.version_info[:2] >= (3,0), "This is Python 3 code" + # Expect to be run from the 'test' subdirectory, one level down from # the main source putty_srcdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -def unicode_to_bytes(arg): - # Slightly fiddly way to do this which should work in Python 2 and 3 - if isinstance(arg, type(u'a')) and not isinstance(arg, type(b'a')): - arg = arg.encode("UTF-8") - return arg - -def bytevals(b): - return struct.unpack("{:d}B".format(len(b)), b) -def valbytes(b): - b = list(b) - return struct.pack("{:d}B".format(len(b)), *b) +def coerce_to_bytes(arg): + return arg.encode("UTF-8") if isinstance(arg, str) else arg class ChildProcessFailure(Exception): pass @@ -69,11 +62,14 @@ def read_line(self): if self.debug is not None: self.debug.write("recv: {}\n".format(line)) return line + def already_terminated(self): + return self.sp is None and self.exitstatus is not None def funcall(self, cmd, args): if self.sp is None: + assert self.exitstatus is None self.start() - self.write_line(unicode_to_bytes(cmd) + b" " + b" ".join( - unicode_to_bytes(arg) for arg in args)) + self.write_line(coerce_to_bytes(cmd) + b" " + b" ".join( + coerce_to_bytes(arg) for arg in args)) argcount = int(self.read_line()) return [self.read_line() for arg in range(argcount)] def wait_for_exit(self): @@ -89,18 +85,38 @@ def check_return_status(self): childprocess = ChildProcess() +method_prefixes = { + 'val_wpoint': 'ecc_weierstrass_', + 'val_mpoint': 'ecc_montgomery_', + 'val_epoint': 'ecc_edwards_', + 'val_hash': 'ssh_hash_', + 'val_mac': 'ssh2_mac_', + 'val_key': 'ssh_key_', + 'val_cipher': 'ssh_cipher_', + 'val_dh': 'dh_', + 'val_ecdh': 'ssh_ecdhkex_', + 'val_rsakex': 'ssh_rsakex_', + 'val_prng': 'prng_', + 'val_pcs': 'pcs_', + 'val_pockle': 'pockle_', +} +method_lists = {t: [] for t in method_prefixes} + class Value(object): def __init__(self, typename, ident): - self.typename = typename - self.ident = ident - def consumed(self): - self.ident = None + self._typename = typename + self._ident = ident + for methodname, function in method_lists.get(self._typename, []): + setattr(self, methodname, + (lambda f: lambda *args: f(self, *args))(function)) + def _consumed(self): + self._ident = None def __repr__(self): - return "Value({!r}, {!r})".format(self.typename, self.ident) + return "Value({!r}, {!r})".format(self._typename, self._ident) def __del__(self): - if self.ident is not None: + if self._ident is not None and not childprocess.already_terminated(): try: - childprocess.funcall("free", [self.ident]) + childprocess.funcall("free", [self._ident]) except ChildProcessFailure: # If we see this exception now, we can't do anything # about it, because exceptions don't propagate out of @@ -116,14 +132,22 @@ def __del__(self): # crashed for some other reason.) pass def __long__(self): - if self.typename != "val_mpint": + if self._typename != "val_mpint": raise TypeError("testcrypt values of types other than mpint" " cannot be converted to integer") - hexval = childprocess.funcall("mp_dump", [self.ident])[0] + hexval = childprocess.funcall("mp_dump", [self._ident])[0] return 0 if len(hexval) == 0 else int(hexval, 16) def __int__(self): return int(self.__long__()) +def marshal_string(val): + val = coerce_to_bytes(val) + assert isinstance(val, bytes), "Bad type for val_string input" + return "".join( + chr(b) if (0x20 <= b < 0x7F and b != 0x25) + else "%{:02x}".format(b) + for b in val) + def make_argword(arg, argtype, fnname, argindex, to_preserve): typename, consumed = argtype if typename.startswith("opt_"): @@ -131,58 +155,90 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve): return "NULL" typename = typename[4:] if typename == "val_string": - arg = unicode_to_bytes(arg) - if isinstance(arg, bytes): - retwords = childprocess.funcall( - "newstring", ["".join("%{:02x}".format(b) - for b in bytevals(arg))]) - arg = make_retvals([typename], retwords, unpack_strings=False)[0] - to_preserve.append(arg) + retwords = childprocess.funcall("newstring", [marshal_string(arg)]) + arg = make_retvals([typename], retwords, unpack_strings=False)[0] + to_preserve.append(arg) if typename == "val_mpint" and isinstance(arg, numbers.Integral): retwords = childprocess.funcall("mp_literal", ["0x{:x}".format(arg)]) arg = make_retvals([typename], retwords)[0] to_preserve.append(arg) if isinstance(arg, Value): - if arg.typename != typename: + if arg._typename != typename: raise TypeError( "{}() argument {:d} should be {} ({} given)".format( - fnname, argindex, typename, arg.typename)) - ident = arg.ident + fnname, argindex, typename, arg._typename)) + ident = arg._ident if consumed: - arg.consumed() + arg._consumed() return ident if typename == "uint" and isinstance(arg, numbers.Integral): return "0x{:x}".format(arg) + if typename == "boolean": + return "true" if arg else "false" if typename in { "hashalg", "macalg", "keyalg", "cipheralg", - "dh_group", "ecdh_alg", "rsaorder"}: - arg = unicode_to_bytes(arg) + "dh_group", "ecdh_alg", "rsaorder", "primegenpolicy", + "argon2flavour", "fptype"}: + arg = coerce_to_bytes(arg) if isinstance(arg, bytes) and b" " not in arg: return arg + if typename == "mpint_list": + sublist = [make_argword(len(arg), ("uint", False), + fnname, argindex, to_preserve)] + for val in arg: + sublist.append(make_argword(val, ("val_mpint", False), + fnname, argindex, to_preserve)) + return b" ".join(coerce_to_bytes(sub) for sub in sublist) raise TypeError( "Can't convert {}() argument {:d} to {} (value was {!r})".format( fnname, argindex, typename, arg)) +def unpack_string(identifier): + retwords = childprocess.funcall("getstring", [identifier]) + childprocess.funcall("free", [identifier]) + return re.sub(b"%[0-9A-F][0-9A-F]", + lambda m: bytes([int(m.group(0)[1:], 16)]), + retwords[0]) + +def unpack_mp(identifier): + retwords = childprocess.funcall("mp_dump", [identifier]) + childprocess.funcall("free", [identifier]) + return int(retwords[0], 16) + def make_retval(rettype, word, unpack_strings): if rettype.startswith("opt_"): if word == b"NULL": return None rettype = rettype[4:] if rettype == "val_string" and unpack_strings: - retwords = childprocess.funcall("getstring", [word]) + return unpack_string(word) + if rettype == "val_keycomponents": + kc = {} + retwords = childprocess.funcall("key_components_count", [word]) + for i in range(int(retwords[0], 0)): + args = [word, "{:d}".format(i)] + retwords = childprocess.funcall("key_components_nth_name", args) + kc_key = unpack_string(retwords[0]) + retwords = childprocess.funcall("key_components_nth_str", args) + if retwords[0] != b"NULL": + kc_value = unpack_string(retwords[0]).decode("ASCII") + else: + retwords = childprocess.funcall("key_components_nth_mp", args) + kc_value = unpack_mp(retwords[0]) + kc[kc_key.decode("ASCII")] = kc_value childprocess.funcall("free", [word]) - return re.sub(b"%[0-9A-F][0-9A-F]", - lambda m: valbytes([int(m.group(0)[1:], 16)]), - retwords[0]) + return kc if rettype.startswith("val_"): return Value(rettype, word) - elif rettype == "uint": + elif rettype == "int" or rettype == "uint": return int(word, 0) elif rettype == "boolean": assert word == b"true" or word == b"false" return word == b"true" + elif rettype == "pocklestatus": + return word.decode("ASCII") raise TypeError("Can't deal with return value {!r} of type {!r}" - .format(rettype, word)) + .format(word, rettype)) def make_retvals(rettypes, retwords, unpack_strings=True): assert len(rettypes) == len(retwords) # FIXME: better exception @@ -254,7 +310,14 @@ def trim_argtype(arg): consumed = True arg = trim_argtype(arg) argtypes.append((arg, consumed)) - scope[function] = Function(function, rettypes, argtypes) + func = Function(function, rettypes, argtypes) + scope[function] = func + if len(argtypes) > 0: + t = argtypes[0][0] + if (t in method_prefixes and + function.startswith(method_prefixes[t])): + methodname = function[len(method_prefixes[t]):] + method_lists[t].append((methodname, func)) _setup(globals()) del _setup diff --git a/test/windowchange.py b/test/windowchange.py new file mode 100755 index 0000000..6e5ffb7 --- /dev/null +++ b/test/windowchange.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python3 + +# Interactive test program for assorted ancillary changes to the +# terminal window - title, position, size, z-order, colour palette +# etc. + +import argparse +import sys +import termios +import os +from time import sleep + +def send(s): + sys.stdout.write(s) + sys.stdout.flush() + +def query(s, lastchar=None): + send(s) + + old_attrs = termios.tcgetattr(0) + new_attrs = old_attrs.copy() + new_attrs[3] &= ~(termios.ECHO | termios.ICANON) + new_attrs[6][termios.VMIN] = 0 + new_attrs[6][termios.VTIME] = 1 + try: + termios.tcsetattr(0, termios.TCSANOW, new_attrs) + + s = b"" + while True: + c = os.read(0, 1) + if len(c) == 0: + break + s += c + if lastchar is not None and c[0] == lastchar: + break + return s + + finally: + termios.tcsetattr(0, termios.TCSANOW, old_attrs) + +def pause(prompt): + input(prompt + (" " if len(prompt) > 0 else "") + "--More--") + +def main(): + testnames = [ + "title", + "icon", + "minimise", + "maximise", + "minquery", + "setpalette", + "querypalette", + "winpos", + "setwinsize", + "querywinsize", + "zorder", + "mousereport", + ] + + parser = argparse.ArgumentParser( + description='Test PuTTY\'s ancillary terminal updates') + parser.add_argument("test", choices=testnames, nargs="*", + help='Sub-test to perform (default: all of them)') + args = parser.parse_args() + + if len(args.test) == 0: + dotest = lambda s: True + else: + testset = set(args.test) + dotest = lambda s: s in testset + + if dotest("title"): + send("\033]2;Title test 1\a") + pause("Title test 1.") + send("\033]2;Title test 2\a") + pause("Title test 2.") + + if dotest("icon"): + send("\033]1;Icon-title test 1\a") + pause("Icon-title test 1.") + send("\033]1;Icon-title test 2\a") + pause("Icon-title test 2.") + + if dotest("minimise"): + pause("About to minimise and restore.") + send("\033[2t") + sleep(2) + send("\033[1t") + + if dotest("maximise"): + pause("About to maximise.") + send("\033[9;1t") + pause("About to unmaximise.") + send("\033[9;0t") + + if dotest("minquery"): + pause("Query minimised status.") + s = query("\033[11t") + if s == b"\033[1t": + print("Reply: not minimised") + elif s == b"\033[2t": + print("Reply: minimised") + else: + print("Reply unknown:", repr(s)) + + if dotest("setpalette"): + print("Palette testing:") + teststr = "" + for i in range(8): + teststr += "\033[0;{:d}m{:X}".format(i+30, i) + for i in range(8): + teststr += "\033[1;{:d}m{:X}".format(i+30, i+8) + teststr += "\033[0;39mG\033[1mH\033[0;7mI\033[1mJ" + teststr += "\033[m" + teststr += " " * 9 + "K" + "\b" * 10 + for c in "0123456789ABCDEFGHIJKL": + pause("Base: " + teststr) + send("\033]P" + c + "ff0000") + pause(c + " red: " + teststr) + send("\033]P" + c + "00ff00") + pause(c + " green: " + teststr) + send("\033]P" + c + "0000ff") + pause(c + " blue: " + teststr) + send("\033]R") + + if dotest("querypalette"): + send("\033]R") + nfail = 262 + for i in range(nfail): + s = query("\033]4;{:d};?\a".format(i), 7).decode("ASCII") + prefix, suffix = "\033]4;{:d};rgb:".format(i), "\a" + if s.startswith(prefix) and s.endswith(suffix): + rgb = [int(word, 16) for word in + s[len(prefix):-len(suffix)].split("/")] + if 0 <= i < 8: + j = i + expected_rgb = [0xbbbb * ((j>>n) & 1) for n in range(3)] + elif 0 <= i-8 < 8: + j = i-8 + expected_rgb = [0x5555 + 0xaaaa * ((j>>n) & 1) + for n in range(3)] + elif 0 <= i-16 < 216: + j = i-16 + expected_rgb = [(0 if v==0 else 0x3737+0x2828*v) for v in + (j // 36, j // 6 % 6, j % 6)] + elif 0 <= i-232 < 24: + j = i-232 + expected_rgb = [j * 0x0a0a + 0x0808] * 3 + else: + expected_rgb = [ + [0xbbbb, 0xbbbb, 0xbbbb], [0xffff, 0xffff, 0xffff], + [0x0000, 0x0000, 0x0000], [0x5555, 0x5555, 0x5555], + [0x0000, 0x0000, 0x0000], [0x0000, 0xffff, 0x0000], + ][i-256] + if expected_rgb == rgb: + nfail -= 1 + else: + print(i, "unexpected: {:04x} {:04x} {:04x}".format(*rgb)) + else: + print(i, "bad format:", repr(s)) + print("Query palette: {:d} colours wrong".format(nfail)) + + if dotest("winpos"): + print("Query window position: ", end="") + s = query("\033[13t").decode("ASCII") + prefix, suffix = "\033[3;", "t" + if s.startswith(prefix) and s.endswith(suffix): + x, y = [int(word) for word in + s[len(prefix):-len(suffix)].split(";")] + print("x={:d} y={:d}".format(x, y)) + else: + print("bad format:", repr(s)) + x, y = 50, 50 + + pause("About to move window.") + send("\033[3;{:d};{:d}t".format(x+50, y+50)) + pause("About to move it back again.") + send("\033[3;{:d};{:d}t".format(x, y)) + + if dotest("setwinsize"): + pause("About to DECCOLM to 132 cols.") + send("\033[?3h") + pause("About to DECCOLM to 80 cols.") + send("\033[?3l") + pause("About to DECCOLM to 132 cols again.") + send("\033[?3h") + pause("About to reset the terminal.") + send("\033c") + pause("About to DECSLPP to 30 rows.") + send("\033[30t") + pause("About to DECSLPP to 24 rows.") + send("\033[24t") + pause("About to DECSNLS to 30 rows.") + send("\033[*30|") + pause("About to DECSNLS to 24 rows.") + send("\033[*24|") + pause("About to DECSCPP to 90 cols.") + send("\033[$90|") + pause("About to DECSCPP to 80 cols.") + send("\033[$80|") + pause("About to xterm to 90x30.") + send("\033[8;30;90t") + pause("About to xterm back to 80x24.") + send("\033[8;24;80t") + + if dotest("querywinsize"): + print("Query window size: ", end="") + s = query("\033[14t").decode("ASCII") + prefix, suffix = "\033[4;", "t" + if s.startswith(prefix) and s.endswith(suffix): + h, w = [int(word) for word in + s[len(prefix):-len(suffix)].split(";")] + print("w={:d} h={:d}".format(w, h)) + else: + print("bad format:", repr(s)) + + if dotest("zorder"): + pause("About to lower to bottom and then raise.") + send("\033[6t") + sleep(2) + send("\033[5t") + + if dotest("mousereport"): + send("\033[?1000h") + pause("Mouse reporting on: expect clicks to generate input") + send("\033[?1000l") + pause("Mouse reporting off: expect clicks to select") + +if __name__ == '__main__': + main() diff --git a/testback.c b/testback.c index f26f395..173786e 100644 --- a/testback.c +++ b/testback.c @@ -32,10 +32,10 @@ #include "putty.h" -static const char *null_init(Seat *, Backend **, LogContext *, Conf *, - const char *, int, char **, int, int); -static const char *loop_init(Seat *, Backend **, LogContext *, Conf *, - const char *, int, char **, int, int); +static char *null_init(const BackendVtable *, Seat *, Backend **, LogContext *, + Conf *, const char *, int, char **, bool, bool); +static char *loop_init(const BackendVtable *, Seat *, Backend **, LogContext *, + Conf *, const char *, int, char **, bool, bool); static void null_free(Backend *); static void loop_free(Backend *); static void null_reconfig(Backend *, Conf *); @@ -53,18 +53,48 @@ static void null_provide_ldisc(Backend *, Ldisc *); static void null_unthrottle(Backend *, size_t); static int null_cfg_info(Backend *); -const struct BackendVtable null_backend = { - null_init, null_free, null_reconfig, null_send, null_sendbuffer, null_size, - null_special, null_get_specials, null_connected, null_exitcode, null_sendok, - null_ldisc, null_provide_ldisc, null_unthrottle, - null_cfg_info, NULL /* test_for_upstream */, "null", -1, 0 +const BackendVtable null_backend = { + .init = null_init, + .free = null_free, + .reconfig = null_reconfig, + .send = null_send, + .sendbuffer = null_sendbuffer, + .size = null_size, + .special = null_special, + .get_specials = null_get_specials, + .connected = null_connected, + .exitcode = null_exitcode, + .sendok = null_sendok, + .ldisc_option_state = null_ldisc, + .provide_ldisc = null_provide_ldisc, + .unthrottle = null_unthrottle, + .cfg_info = null_cfg_info, + .id = "null", + .displayname = "null", + .protocol = -1, + .default_port = 0, }; -const struct BackendVtable loop_backend = { - loop_init, loop_free, null_reconfig, loop_send, null_sendbuffer, null_size, - null_special, null_get_specials, null_connected, null_exitcode, null_sendok, - null_ldisc, null_provide_ldisc, null_unthrottle, - null_cfg_info, NULL /* test_for_upstream */, "loop", -1, 0 +const BackendVtable loop_backend = { + .init = loop_init, + .free = loop_free, + .reconfig = null_reconfig, + .send = loop_send, + .sendbuffer = null_sendbuffer, + .size = null_size, + .special = null_special, + .get_specials = null_get_specials, + .connected = null_connected, + .exitcode = null_exitcode, + .sendok = null_sendok, + .ldisc_option_state = null_ldisc, + .provide_ldisc = null_provide_ldisc, + .unthrottle = null_unthrottle, + .cfg_info = null_cfg_info, + .id = "loop", + .displayname = "loop", + .protocol = -1, + .default_port = 0, }; struct loop_state { @@ -72,10 +102,10 @@ struct loop_state { Backend backend; }; -static const char *null_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, char **realhost, - int nodelay, int keepalive) { +static char *null_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); @@ -83,10 +113,10 @@ static const char *null_init(Seat *seat, Backend **backend_handle, return NULL; } -static const char *loop_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, char **realhost, - int nodelay, int keepalive) { +static char *loop_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { struct loop_state *st = snew(struct loop_state); /* No local authentication phase in this protocol */ diff --git a/testcrypt.c b/testcrypt.c index 277fd3d..6762775 100644 --- a/testcrypt.c +++ b/testcrypt.c @@ -31,11 +31,12 @@ #include "defs.h" #include "ssh.h" +#include "sshkeygen.h" #include "misc.h" #include "mpint.h" #include "ecc.h" -static NORETURN void fatal_error(const char *p, ...) +static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...) { va_list ap; fprintf(stderr, "testcrypt: "); @@ -48,11 +49,26 @@ static NORETURN void fatal_error(const char *p, ...) void out_of_memory(void) { fatal_error("out of memory"); } +/* For platforms where getticks is defined within this code base */ +unsigned long (getticks)(void) +{ unreachable("this is a stub needed to link, and should never be called"); } + +FILE *f_open(const struct Filename *fn, char const *mode, bool private) +{ unreachable("f_open should never be called by this test program"); } + +static bool old_keyfile_warning_given; +void old_keyfile_warning(void) { old_keyfile_warning_given = true; } + static bufchain random_data_queue; +static prng *test_prng; void random_read(void *buf, size_t size) { - if (!bufchain_try_fetch_consume(&random_data_queue, buf, size)) - fatal_error("No random data in queue"); + if (test_prng) { + prng_read(test_prng, buf, size); + } else { + if (!bufchain_try_fetch_consume(&random_data_queue, buf, size)) + fatal_error("No random data in queue"); + } } uint64_t prng_reseed_time_ms(void) @@ -81,6 +97,10 @@ uint64_t prng_reseed_time_ms(void) X(rsakex, RSAKey *, ssh_rsakex_freekey(v)) \ X(rsa, RSAKey *, rsa_free(v)) \ X(prng, prng *, prng_free(v)) \ + X(keycomponents, key_components *, key_components_free(v)) \ + X(pcs, PrimeCandidateSource *, pcs_free(v)) \ + X(pgc, PrimeGenerationContext *, primegen_free_context(v)) \ + X(pockle, Pockle *, pockle_free(v)) \ /* end of list */ typedef struct Value Value; @@ -93,12 +113,18 @@ enum ValueType { typedef enum ValueType ValueType; -const char *const type_names[] = { +static const char *const type_names[] = { #define VALTYPE_NAME(n,t,f) #n, VALUE_TYPES(VALTYPE_NAME) #undef VALTYPE_NAME }; +#define VALTYPE_TYPEDEF(n,t,f) \ + typedef t TD_val_##n; \ + typedef t *TD_out_val_##n; +VALUE_TYPES(VALTYPE_TYPEDEF) +#undef VALTYPE_TYPEDEF + struct Value { /* * Protocol identifier assigned to this value when it was created. @@ -118,6 +144,8 @@ struct Value { #define VALTYPE_UNION(n,t,f) t vu_##n; VALUE_TYPES(VALTYPE_UNION) #undef VALTYPE_UNION + + char *bare_string; }; }; @@ -191,7 +219,17 @@ static const ssh_hashalg *get_hashalg(BinarySource *in) {"sha256_sw", &ssh_sha256_sw}, {"sha256_hw", &ssh_sha256_hw}, {"sha384", &ssh_sha384}, + {"sha384_sw", &ssh_sha384_sw}, + {"sha384_hw", &ssh_sha384_hw}, {"sha512", &ssh_sha512}, + {"sha512_sw", &ssh_sha512_sw}, + {"sha512_hw", &ssh_sha512_hw}, + {"sha3_224", &ssh_sha3_224}, + {"sha3_256", &ssh_sha3_256}, + {"sha3_384", &ssh_sha3_384}, + {"sha3_512", &ssh_sha3_512}, + {"shake256_114bytes", &ssh_shake256_114bytes}, + {"blake2b", &ssh_blake2b}, }; ptrlen name = get_word(in); @@ -234,6 +272,7 @@ static const ssh_keyalg *get_keyalg(BinarySource *in) {"dsa", &ssh_dss}, {"rsa", &ssh_rsa}, {"ed25519", &ssh_ecdsa_ed25519}, + {"ed448", &ssh_ecdsa_ed448}, {"p256", &ssh_ecdsa_nistp256}, {"p384", &ssh_ecdsa_nistp384}, {"p521", &ssh_ecdsa_nistp521}, @@ -316,6 +355,7 @@ static const ssh_kex *get_ecdh_alg(BinarySource *in) const ssh_kex *value; } algs[] = { {"curve25519", &ssh_ec_kex_curve25519}, + {"curve448", &ssh_ec_kex_curve448}, {"nistp256", &ssh_ec_kex_nistp256}, {"nistp384", &ssh_ec_kex_nistp384}, {"nistp521", &ssh_ec_kex_nistp521}, @@ -347,6 +387,70 @@ static RsaSsh1Order get_rsaorder(BinarySource *in) fatal_error("rsaorder '%.*s': not found", PTRLEN_PRINTF(name)); } +static const PrimeGenerationPolicy *get_primegenpolicy(BinarySource *in) +{ + static const struct { + const char *key; + const PrimeGenerationPolicy *value; + } algs[] = { + {"probabilistic", &primegen_probabilistic}, + {"provable_fast", &primegen_provable_fast}, + {"provable_maurer_simple", &primegen_provable_maurer_simple}, + {"provable_maurer_complex", &primegen_provable_maurer_complex}, + }; + + ptrlen name = get_word(in); + for (size_t i = 0; i < lenof(algs); i++) + if (ptrlen_eq_string(name, algs[i].key)) + return algs[i].value; + + fatal_error("primegenpolicy '%.*s': not found", PTRLEN_PRINTF(name)); +} + +static Argon2Flavour get_argon2flavour(BinarySource *in) +{ + static const struct { + const char *key; + Argon2Flavour value; + } algs[] = { + {"d", Argon2d}, + {"i", Argon2i}, + {"id", Argon2id}, + /* I expect to forget which spelling I chose, so let's support many */ + {"argon2d", Argon2d}, + {"argon2i", Argon2i}, + {"argon2id", Argon2id}, + {"Argon2d", Argon2d}, + {"Argon2i", Argon2i}, + {"Argon2id", Argon2id}, + }; + + ptrlen name = get_word(in); + for (size_t i = 0; i < lenof(algs); i++) + if (ptrlen_eq_string(name, algs[i].key)) + return algs[i].value; + + fatal_error("Argon2 flavour '%.*s': not found", PTRLEN_PRINTF(name)); +} + +static FingerprintType get_fptype(BinarySource *in) +{ + static const struct { + const char *key; + FingerprintType value; + } ids[] = { + {"md5", SSH_FPTYPE_MD5}, + {"sha256", SSH_FPTYPE_SHA256}, + }; + + ptrlen name = get_word(in); + for (size_t i = 0; i < lenof(ids); i++) + if (ptrlen_eq_string(name, ids[i].key)) + return ids[i].value; + + fatal_error("fingerprint type '%.*s': not found", PTRLEN_PRINTF(name)); +} + static uintmax_t get_uint(BinarySource *in) { ptrlen word = get_word(in); @@ -356,6 +460,11 @@ static uintmax_t get_uint(BinarySource *in) return toret; } +static bool get_boolean(BinarySource *in) +{ + return ptrlen_eq_string(get_word(in), "true"); +} + static Value *lookup_value(ptrlen word) { Value *val = find234(values, &word, valuefind); @@ -376,7 +485,7 @@ struct finaliser { }; static struct finaliser *finalisers; -size_t nfinalisers, finalisersize; +static size_t nfinalisers, finalisersize; static void add_finaliser(finaliser_fn_t fn, void *ctx) { @@ -400,6 +509,11 @@ static void finaliser_return_value(strbuf *out, void *ctx) put_byte(out, '\n'); } +static void finaliser_sfree(strbuf *out, void *ctx) +{ + sfree(ctx); +} + #define VALTYPE_GETFN(n,t,f) \ static Value *unwrap_value_##n(Value *val) { \ ValueType expected = VT_##n; \ @@ -428,6 +542,14 @@ static char *get_val_string_asciz(BinarySource *in) return get_val_string(in)->s; } +static strbuf *get_opt_val_string(BinarySource *in); + +static char *get_opt_val_string_asciz(BinarySource *in) +{ + strbuf *sb = get_opt_val_string(in); + return sb ? sb->s : NULL; +} + static mp_int **get_out_val_mpint(BinarySource *in) { Value *val = value_new(VT_mpint); @@ -435,6 +557,26 @@ static mp_int **get_out_val_mpint(BinarySource *in) return &val->vu_mpint; } +struct mpint_list { + size_t n; + mp_int **integers; +}; + +static struct mpint_list get_mpint_list(BinarySource *in) +{ + size_t n = get_uint(in); + + struct mpint_list mpl; + mpl.n = n; + + mpl.integers = snewn(n, mp_int *); + for (size_t i = 0; i < n; i++) + mpl.integers[i] = get_val_mpint(in); + + add_finaliser(finaliser_sfree, mpl.integers); + return mpl; +} + static void finaliser_return_uint(strbuf *out, void *ctx) { unsigned *uval = (unsigned *)ctx; @@ -457,9 +599,45 @@ static BinarySink *get_out_val_string_binarysink(BinarySource *in) return BinarySink_UPCAST(val->vu_string); } -static void finaliser_sfree(strbuf *out, void *ctx) +static void return_val_string_asciz_const(strbuf *out, const char *s); +static void return_val_string_asciz(strbuf *out, char *s); + +static void finaliser_return_opt_string_asciz(strbuf *out, void *ctx) { - sfree(ctx); + char **valp = (char **)ctx; + char *val = *valp; + sfree(valp); + if (!val) + strbuf_catf(out, "NULL\n"); + else + return_val_string_asciz(out, val); +} + +static char **get_out_opt_val_string_asciz(BinarySource *in) +{ + char **valp = snew(char *); + *valp = NULL; + add_finaliser(finaliser_return_opt_string_asciz, valp); + return valp; +} + +static void finaliser_return_opt_string_asciz_const(strbuf *out, void *ctx) +{ + const char **valp = (const char **)ctx; + const char *val = *valp; + sfree(valp); + if (!val) + strbuf_catf(out, "NULL\n"); + else + return_val_string_asciz_const(out, val); +} + +static const char **get_out_opt_val_string_asciz_const(BinarySource *in) +{ + const char **valp = snew(const char *); + *valp = NULL; + add_finaliser(finaliser_return_opt_string_asciz_const, valp); + return valp; } static BinarySource *get_val_string_binarysource(BinarySource *in) @@ -471,14 +649,18 @@ static BinarySource *get_val_string_binarysource(BinarySource *in) return src; } -static ssh_hash *get_consumed_val_hash(BinarySource *in) -{ - Value *val = get_value_hash(in); - ssh_hash *toret = val->vu_hash; - del234(values, val); - sfree(val); - return toret; -} +#define GET_CONSUMED_FN(type) \ + typedef TD_val_##type TD_consumed_val_##type; \ + static TD_val_##type get_consumed_val_##type(BinarySource *in) \ + { \ + Value *val = get_value_##type(in); \ + TD_val_##type toret = val->vu_##type; \ + del234(values, val); \ + sfree(val); \ + return toret; \ + } +GET_CONSUMED_FN(hash) +GET_CONSUMED_FN(pcs) static void return_int(strbuf *out, intmax_t u) { @@ -495,14 +677,38 @@ static void return_boolean(strbuf *out, bool b) strbuf_catf(out, "%s\n", b ? "true" : "false"); } -static void return_val_string_asciz(strbuf *out, char *s) +static void return_pocklestatus(strbuf *out, PockleStatus status) +{ + switch (status) { + default: + strbuf_catf(out, "POCKLE_BAD_STATUS_VALUE\n"); + break; + +#define STATUS_CASE(id) \ + case id: \ + strbuf_catf(out, "%s\n", #id); \ + break; + + POCKLE_STATUSES(STATUS_CASE); + +#undef STATUS_CASE + + } +} + +static void return_val_string_asciz_const(strbuf *out, const char *s) { strbuf *sb = strbuf_new(); put_data(sb, s, strlen(s)); - sfree(s); return_val_string(out, sb); } +static void return_val_string_asciz(strbuf *out, char *s) +{ + return_val_string_asciz_const(out, s); + sfree(s); +} + #define NULLABLE_RETURN_WRAPPER(type_name, c_type) \ static void return_opt_##type_name(strbuf *out, c_type ptr) \ { \ @@ -512,10 +718,13 @@ static void return_val_string_asciz(strbuf *out, char *s) return_##type_name(out, ptr); \ } +NULLABLE_RETURN_WRAPPER(val_string, strbuf *) NULLABLE_RETURN_WRAPPER(val_string_asciz, char *) +NULLABLE_RETURN_WRAPPER(val_string_asciz_const, const char *) NULLABLE_RETURN_WRAPPER(val_cipher, ssh_cipher *) NULLABLE_RETURN_WRAPPER(val_hash, ssh_hash *) NULLABLE_RETURN_WRAPPER(val_key, ssh_key *) +NULLABLE_RETURN_WRAPPER(val_mpint, mp_int *) static void handle_hello(BinarySource *in, strbuf *out) { @@ -607,9 +816,24 @@ static size_t random_queue_len(void) static void random_clear(void) { + if (test_prng) { + prng_free(test_prng); + test_prng = NULL; + } + bufchain_clear(&random_data_queue); } +static void random_make_prng(const ssh_hashalg *hashalg, ptrlen seed) +{ + random_clear(); + + test_prng = prng_new(hashalg); + prng_seed_begin(test_prng); + put_datapl(test_prng, seed); + prng_seed_finish(test_prng); +} + mp_int *monty_identity_wrapper(MontyContext *mc) { return mp_copy(monty_identity(mc)); @@ -622,6 +846,16 @@ mp_int *monty_modulus_wrapper(MontyContext *mc) } #define monty_modulus monty_modulus_wrapper +strbuf *ssh_hash_digest_wrapper(ssh_hash *h) +{ + strbuf *sb = strbuf_new(); + void *p = strbuf_append(sb, ssh_hash_alg(h)->hlen); + ssh_hash_digest(h, p); + return sb; +} +#undef ssh_hash_digest +#define ssh_hash_digest ssh_hash_digest_wrapper + strbuf *ssh_hash_final_wrapper(ssh_hash *h) { strbuf *sb = strbuf_new(); @@ -740,11 +974,14 @@ static RSAKey *rsa_new(void) strbuf *rsa_ssh1_encrypt_wrapper(ptrlen input, RSAKey *key) { /* Fold the boolean return value in C into the string return value - * for this purpose, by returning the empty string on failure */ + * for this purpose, by returning NULL on failure */ strbuf *sb = strbuf_new(); put_datapl(sb, input); - if (!rsa_ssh1_encrypt(sb->u, sb->len, key)) - sb->len = 0; + put_padding(sb, key->bytes - input.len, 0); + if (!rsa_ssh1_encrypt(sb->u, input.len, key)) { + strbuf_free(sb); + return NULL; + } return sb; } #define rsa_ssh1_encrypt rsa_ssh1_encrypt_wrapper @@ -754,7 +991,7 @@ strbuf *rsa_ssh1_decrypt_pkcs1_wrapper(mp_int *input, RSAKey *key) /* Again, return "" on failure */ strbuf *sb = strbuf_new(); if (!rsa_ssh1_decrypt_pkcs1(input, key, sb)) - sb->len = 0; + strbuf_clear(sb); return sb; } #define rsa_ssh1_decrypt_pkcs1 rsa_ssh1_decrypt_pkcs1_wrapper @@ -841,28 +1078,32 @@ strbuf *des3_decrypt_pubkey_ossh_wrapper(ptrlen key, ptrlen iv, ptrlen data) } #define des3_decrypt_pubkey_ossh des3_decrypt_pubkey_ossh_wrapper -strbuf *aes256_encrypt_pubkey_wrapper(ptrlen key, ptrlen data) +strbuf *aes256_encrypt_pubkey_wrapper(ptrlen key, ptrlen iv, ptrlen data) { if (key.len != 32) fatal_error("aes256_encrypt_pubkey: key must be 32 bytes long"); + if (iv.len != 16) + fatal_error("aes256_encrypt_pubkey: iv must be 16 bytes long"); if (data.len % 16 != 0) fatal_error("aes256_encrypt_pubkey: data must be a multiple of 16 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); - aes256_encrypt_pubkey(key.ptr, sb->u, sb->len); + aes256_encrypt_pubkey(key.ptr, iv.ptr, sb->u, sb->len); return sb; } #define aes256_encrypt_pubkey aes256_encrypt_pubkey_wrapper -strbuf *aes256_decrypt_pubkey_wrapper(ptrlen key, ptrlen data) +strbuf *aes256_decrypt_pubkey_wrapper(ptrlen key, ptrlen iv, ptrlen data) { if (key.len != 32) fatal_error("aes256_decrypt_pubkey: key must be 32 bytes long"); + if (iv.len != 16) + fatal_error("aes256_encrypt_pubkey: iv must be 16 bytes long"); if (data.len % 16 != 0) fatal_error("aes256_decrypt_pubkey: data must be a multiple of 16 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); - aes256_decrypt_pubkey(key.ptr, sb->u, sb->len); + aes256_decrypt_pubkey(key.ptr, iv.ptr, sb->u, sb->len); return sb; } #define aes256_decrypt_pubkey aes256_decrypt_pubkey_wrapper @@ -893,23 +1134,163 @@ bool crcda_detect(ptrlen packet, ptrlen iv) return toret; } +ssh_key *ppk_load_s_wrapper(BinarySource *src, char **comment, + const char *passphrase, const char **errorstr) +{ + ssh2_userkey *uk = ppk_load_s(src, passphrase, errorstr); + if (uk == SSH2_WRONG_PASSPHRASE) { + /* Fudge this special return value */ + *errorstr = "SSH2_WRONG_PASSPHRASE"; + return NULL; + } + if (uk == NULL) + return NULL; + ssh_key *toret = uk->key; + *comment = uk->comment; + sfree(uk); + return toret; +} +#define ppk_load_s ppk_load_s_wrapper + +int rsa1_load_s_wrapper(BinarySource *src, RSAKey *rsa, char **comment, + const char *passphrase, const char **errorstr) +{ + int toret = rsa1_load_s(src, rsa, passphrase, errorstr); + *comment = rsa->comment; + rsa->comment = NULL; + return toret; +} +#define rsa1_load_s rsa1_load_s_wrapper + +strbuf *ppk_save_sb_wrapper( + ssh_key *key, const char *comment, const char *passphrase, + unsigned fmt_version, Argon2Flavour flavour, + uint32_t mem, uint32_t passes, uint32_t parallel) +{ + /* + * For repeatable testing purposes, we never want a timing-dependent + * choice of password hashing parameters, so this is easy. + */ + ppk_save_parameters save_params; + memset(&save_params, 0, sizeof(save_params)); + save_params.fmt_version = fmt_version; + save_params.argon2_flavour = flavour; + save_params.argon2_mem = mem; + save_params.argon2_passes_auto = false; + save_params.argon2_passes = passes; + save_params.argon2_parallelism = parallel; + + ssh2_userkey uk; + uk.key = key; + uk.comment = dupstr(comment); + strbuf *toret = ppk_save_sb(&uk, passphrase, &save_params); + sfree(uk.comment); + return toret; +} +#define ppk_save_sb ppk_save_sb_wrapper + +strbuf *rsa1_save_sb_wrapper(RSAKey *key, const char *comment, + const char *passphrase) +{ + key->comment = dupstr(comment); + strbuf *toret = rsa1_save_sb(key, passphrase); + sfree(key->comment); + key->comment = NULL; + return toret; +} +#define rsa1_save_sb rsa1_save_sb_wrapper + #define return_void(out, expression) (expression) -static void no_progress(void *param, int action, int phase, int iprogress) {} +static ProgressReceiver null_progress = { .vt = &null_progress_vt }; -mp_int *primegen_wrapper( - int bits, int modulus, int residue, mp_int *factor, unsigned firstbits) +mp_int *primegen_generate_wrapper( + PrimeGenerationContext *ctx, PrimeCandidateSource *pcs) { - return primegen(bits, modulus, residue, factor, - 0, no_progress, NULL, firstbits); + return primegen_generate(ctx, pcs, &null_progress); } -#define primegen primegen_wrapper +#define primegen_generate primegen_generate_wrapper -#define VALTYPE_TYPEDEF(n,t,f) \ - typedef t TD_val_##n; \ - typedef t *TD_out_val_##n; -VALUE_TYPES(VALTYPE_TYPEDEF) -#undef VALTYPE_TYPEDEF +RSAKey *rsa1_generate(int bits, bool strong, PrimeGenerationContext *pgc) +{ + RSAKey *rsakey = snew(RSAKey); + rsa_generate(rsakey, bits, strong, pgc, &null_progress); + rsakey->comment = NULL; + return rsakey; +} + +ssh_key *rsa_generate_wrapper(int bits, bool strong, + PrimeGenerationContext *pgc) +{ + return &rsa1_generate(bits, strong, pgc)->sshk; +} +#define rsa_generate rsa_generate_wrapper + +ssh_key *dsa_generate_wrapper(int bits, PrimeGenerationContext *pgc) +{ + struct dss_key *dsskey = snew(struct dss_key); + dsa_generate(dsskey, bits, pgc, &null_progress); + return &dsskey->sshk; +} +#define dsa_generate dsa_generate_wrapper + +ssh_key *ecdsa_generate_wrapper(int bits) +{ + struct ecdsa_key *ek = snew(struct ecdsa_key); + if (!ecdsa_generate(ek, bits)) { + sfree(ek); + return NULL; + } + return &ek->sshk; +} +#define ecdsa_generate ecdsa_generate_wrapper + +ssh_key *eddsa_generate_wrapper(int bits) +{ + struct eddsa_key *ek = snew(struct eddsa_key); + if (!eddsa_generate(ek, bits)) { + sfree(ek); + return NULL; + } + return &ek->sshk; +} +#define eddsa_generate eddsa_generate_wrapper + +size_t key_components_count(key_components *kc) { return kc->ncomponents; } +const char *key_components_nth_name(key_components *kc, size_t n) +{ + return (n >= kc->ncomponents ? NULL : + kc->components[n].name); +} +const char *key_components_nth_str(key_components *kc, size_t n) +{ + return (n >= kc->ncomponents ? NULL : + kc->components[n].is_mp_int ? NULL : + kc->components[n].text); +} +mp_int *key_components_nth_mp(key_components *kc, size_t n) +{ + return (n >= kc->ncomponents ? NULL : + !kc->components[n].is_mp_int ? NULL : + mp_copy(kc->components[n].mp)); +} + +PockleStatus pockle_add_prime_wrapper(Pockle *pockle, mp_int *p, + struct mpint_list mpl, mp_int *witness) +{ + return pockle_add_prime(pockle, p, mpl.integers, mpl.n, witness); +} +#define pockle_add_prime pockle_add_prime_wrapper + +strbuf *argon2_wrapper(Argon2Flavour flavour, uint32_t mem, uint32_t passes, + uint32_t parallel, uint32_t taglen, + ptrlen P, ptrlen S, ptrlen K, ptrlen X) +{ + strbuf *out = strbuf_new(); + argon2(flavour, mem, passes, parallel, taglen, P, S, K, X, out); + return out; +} +#define argon2 argon2_wrapper #define OPTIONAL_PTR_FUNC(type) \ typedef TD_val_##type TD_opt_val_##type; \ @@ -921,13 +1302,19 @@ VALUE_TYPES(VALTYPE_TYPEDEF) } OPTIONAL_PTR_FUNC(cipher) OPTIONAL_PTR_FUNC(mpint) +OPTIONAL_PTR_FUNC(string) typedef uintmax_t TD_uint; +typedef bool TD_boolean; typedef ptrlen TD_val_string_ptrlen; typedef char *TD_val_string_asciz; typedef BinarySource *TD_val_string_binarysource; typedef unsigned *TD_out_uint; typedef BinarySink *TD_out_val_string_binarysink; +typedef const char *TD_opt_val_string_asciz; +typedef char **TD_out_val_string_asciz; +typedef char **TD_out_opt_val_string_asciz; +typedef const char **TD_out_opt_val_string_asciz_const; typedef ssh_hash *TD_consumed_val_hash; typedef const ssh_hashalg *TD_hashalg; typedef const ssh2_macalg *TD_macalg; @@ -936,6 +1323,12 @@ typedef const ssh_cipheralg *TD_cipheralg; typedef const ssh_kex *TD_dh_group; typedef const ssh_kex *TD_ecdh_alg; typedef RsaSsh1Order TD_rsaorder; +typedef key_components *TD_keycomponents; +typedef const PrimeGenerationPolicy *TD_primegenpolicy; +typedef struct mpint_list TD_mpint_list; +typedef PockleStatus TD_pocklestatus; +typedef Argon2Flavour TD_argon2flavour; +typedef FingerprintType TD_fptype; #define FUNC0(rettype, function) \ static void handle_##function(BinarySource *in, strbuf *out) { \ @@ -982,8 +1375,70 @@ typedef RsaSsh1Order TD_rsaorder; return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5)); \ } +#define FUNC6(rettype, function, type1, type2, type3, type4, type5, \ + type6) \ + static void handle_##function(BinarySource *in, strbuf *out) { \ + TD_##type1 arg1 = get_##type1(in); \ + TD_##type2 arg2 = get_##type2(in); \ + TD_##type3 arg3 = get_##type3(in); \ + TD_##type4 arg4 = get_##type4(in); \ + TD_##type5 arg5 = get_##type5(in); \ + TD_##type6 arg6 = get_##type6(in); \ + return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ + arg6)); \ + } + +#define FUNC7(rettype, function, type1, type2, type3, type4, type5, \ + type6, type7) \ + static void handle_##function(BinarySource *in, strbuf *out) { \ + TD_##type1 arg1 = get_##type1(in); \ + TD_##type2 arg2 = get_##type2(in); \ + TD_##type3 arg3 = get_##type3(in); \ + TD_##type4 arg4 = get_##type4(in); \ + TD_##type5 arg5 = get_##type5(in); \ + TD_##type6 arg6 = get_##type6(in); \ + TD_##type7 arg7 = get_##type7(in); \ + return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7)); \ + } + +#define FUNC8(rettype, function, type1, type2, type3, type4, type5, \ + type6, type7, type8) \ + static void handle_##function(BinarySource *in, strbuf *out) { \ + TD_##type1 arg1 = get_##type1(in); \ + TD_##type2 arg2 = get_##type2(in); \ + TD_##type3 arg3 = get_##type3(in); \ + TD_##type4 arg4 = get_##type4(in); \ + TD_##type5 arg5 = get_##type5(in); \ + TD_##type6 arg6 = get_##type6(in); \ + TD_##type7 arg7 = get_##type7(in); \ + TD_##type8 arg8 = get_##type8(in); \ + return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7, arg8)); \ + } + +#define FUNC9(rettype, function, type1, type2, type3, type4, type5, \ + type6, type7, type8, type9) \ + static void handle_##function(BinarySource *in, strbuf *out) { \ + TD_##type1 arg1 = get_##type1(in); \ + TD_##type2 arg2 = get_##type2(in); \ + TD_##type3 arg3 = get_##type3(in); \ + TD_##type4 arg4 = get_##type4(in); \ + TD_##type5 arg5 = get_##type5(in); \ + TD_##type6 arg6 = get_##type6(in); \ + TD_##type7 arg7 = get_##type7(in); \ + TD_##type8 arg8 = get_##type8(in); \ + TD_##type9 arg9 = get_##type9(in); \ + return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ + arg6, arg7, arg8, arg9)); \ + } + #include "testcrypt.h" +#undef FUNC9 +#undef FUNC8 +#undef FUNC7 +#undef FUNC6 #undef FUNC5 #undef FUNC4 #undef FUNC3 @@ -995,33 +1450,39 @@ static void process_line(BinarySource *in, strbuf *out) { ptrlen id = get_word(in); -#define DISPATCH_COMMAND(cmd) \ - if (ptrlen_eq_string(id, #cmd)) { \ - handle_##cmd(in, out); \ - return; \ - } +#define DISPATCH_INTERNAL(cmdname, handler) do { \ + if (ptrlen_eq_string(id, cmdname)) { \ + handler(in, out); \ + return; \ + } \ + } while (0) + +#define DISPATCH_COMMAND(cmd) DISPATCH_INTERNAL(#cmd, handle_##cmd) DISPATCH_COMMAND(hello); DISPATCH_COMMAND(free); DISPATCH_COMMAND(newstring); DISPATCH_COMMAND(getstring); DISPATCH_COMMAND(mp_literal); DISPATCH_COMMAND(mp_dump); - -#define FUNC(rettype, function, ...) \ - if (ptrlen_eq_string(id, #function)) { \ - handle_##function(in, out); \ - return; \ - } - -#define FUNC0 FUNC -#define FUNC1 FUNC -#define FUNC2 FUNC -#define FUNC3 FUNC -#define FUNC4 FUNC -#define FUNC5 FUNC +#undef DISPATCH_COMMAND + +#define FUNC0(ret,fn) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC1(ret,fn,x) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC2(ret,fn,x,y) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC3(ret,fn,x,y,z) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC4(ret,fn,x,y,z,v) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC5(ret,fn,x,y,z,v,w) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC6(ret,fn,x,y,z,v,w,u) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC7(ret,fn,x,y,z,v,w,u,t) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC8(ret,fn,x,y,z,v,w,u,t,s) DISPATCH_INTERNAL(#fn,handle_##fn); +#define FUNC9(ret,fn,x,y,z,v,w,u,t,s,r) DISPATCH_INTERNAL(#fn,handle_##fn); #include "testcrypt.h" +#undef FUNC9 +#undef FUNC8 +#undef FUNC7 +#undef FUNC6 #undef FUNC5 #undef FUNC4 #undef FUNC3 @@ -1029,6 +1490,8 @@ static void process_line(BinarySource *in, strbuf *out) #undef FUNC1 #undef FUNC0 +#undef DISPATCH_INTERNAL + fatal_error("command '%.*s': unrecognised", PTRLEN_PRINTF(id)); } @@ -1109,7 +1572,7 @@ int main(int argc, char **argv) for (size_t i = 0; i < sb->len; i++) if (sb->s[i] == '\n') lines++; - fprintf(outfp, "%zu\n%s", lines, sb->s); + fprintf(outfp, "%"SIZEu"\n%s", lines, sb->s); fflush(outfp); strbuf_free(sb); sfree(line); diff --git a/testcrypt.h b/testcrypt.h index 5f1fd61..298abc0 100644 --- a/testcrypt.h +++ b/testcrypt.h @@ -41,6 +41,7 @@ FUNC3(void, mp_and_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_or_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_xor_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_bic_into, val_mpint, val_mpint, val_mpint) +FUNC2(void, mp_copy_integer_into, val_mpint, uint) FUNC3(void, mp_add_integer_into, val_mpint, val_mpint, uint) FUNC3(void, mp_sub_integer_into, val_mpint, val_mpint, uint) FUNC3(void, mp_mul_integer_into, val_mpint, val_mpint, uint) @@ -48,12 +49,16 @@ FUNC4(void, mp_cond_add_into, val_mpint, val_mpint, val_mpint, uint) FUNC4(void, mp_cond_sub_into, val_mpint, val_mpint, val_mpint, uint) FUNC3(void, mp_cond_swap, val_mpint, val_mpint, uint) FUNC2(void, mp_cond_clear, val_mpint, uint) -FUNC4(void, mp_divmod_into, val_mpint, val_mpint, val_mpint, val_mpint) +FUNC4(void, mp_divmod_into, val_mpint, val_mpint, opt_val_mpint, opt_val_mpint) FUNC2(val_mpint, mp_div, val_mpint, val_mpint) FUNC2(val_mpint, mp_mod, val_mpint, val_mpint) +FUNC3(val_mpint, mp_nthroot, val_mpint, uint, opt_val_mpint) FUNC2(void, mp_reduce_mod_2to, val_mpint, uint) FUNC2(val_mpint, mp_invert_mod_2to, val_mpint, uint) FUNC2(val_mpint, mp_invert, val_mpint, val_mpint) +FUNC5(void, mp_gcd_into, val_mpint, val_mpint, opt_val_mpint, opt_val_mpint, opt_val_mpint) +FUNC2(val_mpint, mp_gcd, val_mpint, val_mpint) +FUNC2(uint, mp_coprime, val_mpint, val_mpint) FUNC2(val_modsqrt, modsqrt_new, val_mpint, val_mpint) /* The modsqrt functions' 'success' pointer becomes a second return value */ FUNC3(val_mpint, mp_modsqrt, val_modsqrt, val_mpint, out_uint) @@ -75,6 +80,8 @@ FUNC3(val_mpint, mp_modpow, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, mp_modmul, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, mp_modadd, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, mp_modsub, val_mpint, val_mpint, val_mpint) +FUNC3(void, mp_lshift_safe_into, val_mpint, val_mpint, uint) +FUNC3(void, mp_rshift_safe_into, val_mpint, val_mpint, uint) FUNC2(val_mpint, mp_rshift_safe, val_mpint, uint) FUNC3(void, mp_lshift_fixed_into, val_mpint, val_mpint, uint) FUNC3(void, mp_rshift_fixed_into, val_mpint, val_mpint, uint) @@ -105,6 +112,7 @@ FUNC3(val_mpoint, ecc_montgomery_diff_add, val_mpoint, val_mpoint, val_mpoint) FUNC1(val_mpoint, ecc_montgomery_double, val_mpoint) FUNC2(val_mpoint, ecc_montgomery_multiply, val_mpoint, val_mpint) FUNC2(void, ecc_montgomery_get_affine, val_mpoint, out_val_mpint) +FUNC1(boolean, ecc_montgomery_is_identity, val_mpoint) FUNC4(val_ecurve, ecc_edwards_curve, val_mpint, val_mpint, val_mpint, opt_val_mpint) FUNC3(val_epoint, ecc_edwards_point_new, val_ecurve, val_mpint, val_mpint) FUNC3(val_epoint, ecc_edwards_point_new_from_y, val_ecurve, val_mpint, uint) @@ -122,10 +130,14 @@ FUNC3(void, ecc_edwards_get_affine, val_epoint, out_val_mpint, out_val_mpint) * API by the hash object also functioning as a BinarySink. */ FUNC1(opt_val_hash, ssh_hash_new, hashalg) +FUNC1(void, ssh_hash_reset, val_hash) FUNC1(val_hash, ssh_hash_copy, val_hash) +FUNC1(val_string, ssh_hash_digest, val_hash) FUNC1(val_string, ssh_hash_final, consumed_val_hash) FUNC2(void, ssh_hash_update, val_hash, val_string_ptrlen) +FUNC1(opt_val_hash, blake2b_new_general, uint) + /* * The ssh2_mac abstraction. Note the optional ssh_cipher parameter * to ssh2_mac_new. Also, again, I've invented an ssh2_mac_update so @@ -136,6 +148,7 @@ FUNC2(void, ssh2_mac_setkey, val_mac, val_string_ptrlen) FUNC1(void, ssh2_mac_start, val_mac) FUNC2(void, ssh2_mac_update, val_mac, val_string_ptrlen) FUNC1(val_string, ssh2_mac_genresult, val_mac) +FUNC1(val_string_asciz_const, ssh2_mac_text_name, val_mac) /* * The ssh_key abstraction. All the uses of BinarySink and @@ -154,8 +167,17 @@ FUNC2(void, ssh_key_public_blob, val_key, out_val_string_binarysink) FUNC2(void, ssh_key_private_blob, val_key, out_val_string_binarysink) FUNC2(void, ssh_key_openssh_blob, val_key, out_val_string_binarysink) FUNC1(val_string_asciz, ssh_key_cache_str, val_key) +FUNC1(val_keycomponents, ssh_key_components, val_key) FUNC2(uint, ssh_key_public_bits, keyalg, val_string_ptrlen) +/* + * Accessors to retrieve the innards of a 'key_components'. + */ +FUNC1(uint, key_components_count, val_keycomponents) +FUNC2(opt_val_string_asciz_const, key_components_nth_name, val_keycomponents, uint) +FUNC2(opt_val_string_asciz_const, key_components_nth_str, val_keycomponents, uint) +FUNC2(opt_val_mpint, key_components_nth_mp, val_keycomponents, uint) + /* * The ssh_cipher abstraction. The in-place encrypt and decrypt * functions are wrapped to replace them with versions that take one @@ -184,15 +206,18 @@ FUNC2(val_mpint, dh_find_K, val_dh, val_mpint) */ FUNC1(val_ecdh, ssh_ecdhkex_newkey, ecdh_alg) FUNC2(void, ssh_ecdhkex_getpublic, val_ecdh, out_val_string_binarysink) -FUNC2(val_mpint, ssh_ecdhkex_getkey, val_ecdh, val_string_ptrlen) +FUNC2(opt_val_mpint, ssh_ecdhkex_getkey, val_ecdh, val_string_ptrlen) /* - * RSA key exchange. + * RSA key exchange, and also the BinarySource get function + * get_ssh1_rsa_priv_agent, which is a convenient way to make an + * RSAKey for RSA kex testing purposes. */ FUNC1(val_rsakex, ssh_rsakex_newkey, val_string_ptrlen) FUNC1(uint, ssh_rsakex_klen, val_rsakex) FUNC3(val_string, ssh_rsakex_encrypt, val_rsakex, hashalg, val_string_ptrlen) -FUNC3(val_mpint, ssh_rsakex_decrypt, val_rsakex, hashalg, val_string_ptrlen) +FUNC3(opt_val_mpint, ssh_rsakex_decrypt, val_rsakex, hashalg, val_string_ptrlen) +FUNC1(val_rsakex, get_rsa_ssh1_priv_agent, val_string_binarysource) /* * Bare RSA keys as used in SSH-1. The construction API functions @@ -202,13 +227,14 @@ FUNC3(val_mpint, ssh_rsakex_decrypt, val_rsakex, hashalg, val_string_ptrlen) FUNC0(val_rsa, rsa_new) FUNC3(void, get_rsa_ssh1_pub, val_string_binarysource, val_rsa, rsaorder) FUNC2(void, get_rsa_ssh1_priv, val_string_binarysource, val_rsa) -FUNC2(val_string, rsa_ssh1_encrypt, val_string_ptrlen, val_rsa) +FUNC2(opt_val_string, rsa_ssh1_encrypt, val_string_ptrlen, val_rsa) FUNC2(val_mpint, rsa_ssh1_decrypt, val_mpint, val_rsa) FUNC2(val_string, rsa_ssh1_decrypt_pkcs1, val_mpint, val_rsa) FUNC1(val_string_asciz, rsastr_fmt, val_rsa) FUNC1(val_string_asciz, rsa_ssh1_fingerprint, val_rsa) FUNC3(void, rsa_ssh1_public_blob, out_val_string_binarysink, val_rsa, rsaorder) FUNC1(int, rsa_ssh1_public_blob_len, val_string_ptrlen) +FUNC2(void, rsa_ssh1_private_blob_agent, out_val_string_binarysink, val_rsa) /* * The PRNG type. Similarly to hashes and MACs, I've invented an extra @@ -222,6 +248,56 @@ FUNC1(void, prng_seed_finish, val_prng) FUNC2(val_string, prng_read, val_prng, uint) FUNC3(void, prng_add_entropy, val_prng, uint, val_string_ptrlen) +/* + * Key load/save functions, or rather, the BinarySource / strbuf API + * that sits just inside the file I/O versions. + */ +FUNC2(boolean, ppk_encrypted_s, val_string_binarysource, out_opt_val_string_asciz) +FUNC2(boolean, rsa1_encrypted_s, val_string_binarysource, out_opt_val_string_asciz) +FUNC5(boolean, ppk_loadpub_s, val_string_binarysource, out_opt_val_string_asciz, out_val_string_binarysink, out_opt_val_string_asciz, out_opt_val_string_asciz_const) +FUNC4(int, rsa1_loadpub_s, val_string_binarysource, out_val_string_binarysink, out_opt_val_string_asciz, out_opt_val_string_asciz_const) +FUNC4(opt_val_key, ppk_load_s, val_string_binarysource, out_opt_val_string_asciz, opt_val_string_asciz, out_opt_val_string_asciz_const) +FUNC5(int, rsa1_load_s, val_string_binarysource, val_rsa, out_opt_val_string_asciz, opt_val_string_asciz, out_opt_val_string_asciz_const) +FUNC8(val_string, ppk_save_sb, val_key, opt_val_string_asciz, opt_val_string_asciz, uint, argon2flavour, uint, uint, uint) +FUNC3(val_string, rsa1_save_sb, val_rsa, opt_val_string_asciz, opt_val_string_asciz) + +FUNC2(val_string_asciz, ssh2_fingerprint_blob, val_string_ptrlen, fptype) + +/* + * Password hashing. + */ +FUNC9(val_string, argon2, argon2flavour, uint, uint, uint, uint, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) +FUNC2(val_string, argon2_long_hash, uint, val_string_ptrlen) + +/* + * Key generation functions. + */ +FUNC3(val_key, rsa_generate, uint, boolean, val_pgc) +FUNC2(val_key, dsa_generate, uint, val_pgc) +FUNC1(opt_val_key, ecdsa_generate, uint) +FUNC1(opt_val_key, eddsa_generate, uint) +FUNC3(val_rsa, rsa1_generate, uint, boolean, val_pgc) +FUNC1(val_pgc, primegen_new_context, primegenpolicy) +FUNC2(opt_val_mpint, primegen_generate, val_pgc, consumed_val_pcs) +FUNC2(val_string, primegen_mpu_certificate, val_pgc, val_mpint) +FUNC1(val_pcs, pcs_new, uint) +FUNC3(val_pcs, pcs_new_with_firstbits, uint, uint, uint) +FUNC3(void, pcs_require_residue, val_pcs, val_mpint, val_mpint) +FUNC2(void, pcs_require_residue_1, val_pcs, val_mpint) +FUNC2(void, pcs_require_residue_1_mod_prime, val_pcs, val_mpint) +FUNC3(void, pcs_avoid_residue_small, val_pcs, uint, uint) +FUNC1(void, pcs_try_sophie_germain, val_pcs) +FUNC1(void, pcs_set_oneshot, val_pcs) +FUNC1(void, pcs_ready, val_pcs) +FUNC4(void, pcs_inspect, val_pcs, out_val_mpint, out_val_mpint, out_val_mpint) +FUNC1(val_mpint, pcs_generate, val_pcs) +FUNC0(val_pockle, pockle_new) +FUNC1(uint, pockle_mark, val_pockle) +FUNC2(void, pockle_release, val_pockle, uint) +FUNC2(pocklestatus, pockle_add_small_prime, val_pockle, val_mpint) +FUNC4(pocklestatus, pockle_add_prime, val_pockle, val_mpint, mpint_list, val_mpint) +FUNC2(val_string, pockle_mpu, val_pockle, val_mpint) + /* * Miscellaneous. */ @@ -233,13 +309,12 @@ FUNC2(val_string, des3_encrypt_pubkey, val_string_ptrlen, val_string_ptrlen) FUNC2(val_string, des3_decrypt_pubkey, val_string_ptrlen, val_string_ptrlen) FUNC3(val_string, des3_encrypt_pubkey_ossh, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC3(val_string, des3_decrypt_pubkey_ossh, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) -FUNC2(val_string, aes256_encrypt_pubkey, val_string_ptrlen, val_string_ptrlen) -FUNC2(val_string, aes256_decrypt_pubkey, val_string_ptrlen, val_string_ptrlen) +FUNC3(val_string, aes256_encrypt_pubkey, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) +FUNC3(val_string, aes256_decrypt_pubkey, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC1(uint, crc32_rfc1662, val_string_ptrlen) FUNC1(uint, crc32_ssh1, val_string_ptrlen) FUNC2(uint, crc32_update, uint, val_string_ptrlen) FUNC2(boolean, crcda_detect, val_string_ptrlen, val_string_ptrlen) -FUNC5(val_mpint, primegen, uint, uint, uint, val_mpint, uint) /* * These functions aren't part of PuTTY's own API, but are additions @@ -247,4 +322,5 @@ FUNC5(val_mpint, primegen, uint, uint, uint, val_mpint, uint) */ FUNC1(void, random_queue, val_string_ptrlen) FUNC0(uint, random_queue_len) +FUNC2(void, random_make_prng, hashalg, val_string_ptrlen) FUNC0(void, random_clear) diff --git a/testsc.c b/testsc.c index 2005a21..f04f718 100644 --- a/testsc.c +++ b/testsc.c @@ -81,7 +81,7 @@ #include "mpint.h" #include "ecc.h" -static NORETURN void fatal_error(const char *p, ...) +static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...) { va_list ap; fprintf(stderr, "testsc: "); @@ -93,6 +93,13 @@ static NORETURN void fatal_error(const char *p, ...) } void out_of_memory(void) { fatal_error("out of memory"); } +FILE *f_open(const Filename *filename, char const *mode, bool is_private) +{ unreachable("this is a stub needed to link, and should never be called"); } +void old_keyfile_warning(void) +{ unreachable("this is a stub needed to link, and should never be called"); } +/* For platforms where getticks is defined within this code base */ +unsigned long (getticks)(void) +{ unreachable("this is a stub needed to link, and should never be called"); } /* * A simple deterministic PRNG, without any of the Fortuna @@ -104,6 +111,7 @@ static uint64_t random_counter = 0; static const char *random_seedstr = NULL; static uint8_t random_buf[MAX_HASH_LEN]; static size_t random_buf_limit = 0; +static ssh_hash *random_hash; static void random_seed(const char *seedstr) { @@ -118,12 +126,12 @@ void random_read(void *vbuf, size_t size) uint8_t *buf = (uint8_t *)vbuf; while (size-- > 0) { if (random_buf_limit == 0) { - ssh_hash *h = ssh_hash_new(&ssh_sha256); - put_asciz(h, random_seedstr); - put_uint64(h, random_counter); + ssh_hash_reset(random_hash); + put_asciz(random_hash, random_seedstr); + put_uint64(random_hash, random_counter); random_counter++; - random_buf_limit = ssh_hash_alg(h)->hlen; - ssh_hash_final(h, random_buf); + random_buf_limit = ssh_hash_alg(random_hash)->hlen; + ssh_hash_digest(random_hash, random_buf); } *buf++ = random_buf[random_buf_limit--]; } @@ -160,7 +168,7 @@ VOLATILE_WRAPPED_DEFN(, void, log_to_file, (const char *filename)) static const char *outdir = NULL; char *log_filename(const char *basename, size_t index) { - return dupprintf("%s/%s.%04zu", outdir, basename, index); + return dupprintf("%s/%s.%04"SIZEu, outdir, basename, index); } static char *last_filename; @@ -267,6 +275,12 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x)) X(Y, ssh_sha256_sw) \ X(Y, ssh_sha384) \ X(Y, ssh_sha512) \ + X(Y, ssh_sha3_224) \ + X(Y, ssh_sha3_256) \ + X(Y, ssh_sha3_384) \ + X(Y, ssh_sha3_512) \ + X(Y, ssh_shake256_114bytes) \ + X(Y, ssh_blake2b) \ /* end of list */ #define HASH_TESTLIST(X, name) X(hash_ ## name) @@ -289,6 +303,7 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x)) X(mp_mul) \ X(mp_rshift_safe) \ X(mp_divmod) \ + X(mp_nthroot) \ X(mp_modadd) \ X(mp_modsub) \ X(mp_modmul) \ @@ -315,6 +330,7 @@ VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x)) CIPHERS(CIPHER_TESTLIST, X) \ MACS(MAC_TESTLIST, X) \ HASHES(HASH_TESTLIST, X) \ + X(argon2) \ /* end of list */ static void test_mp_get_nbits(void) @@ -572,6 +588,23 @@ static void test_mp_divmod(void) mp_free(r); } +static void test_mp_nthroot(void) +{ + mp_int *x = mp_new(256), *remainder = mp_new(256); + + for (size_t i = 0; i < looplimit(32); i++) { + uint8_t sizes[1]; + random_read(sizes, 1); + mp_random_bits_into(x, sizes[0]); + log_start(); + mp_free(mp_nthroot(x, 3, remainder)); + log_end(); + } + + mp_free(x); + mp_free(remainder); +} + static void test_mp_modarith( mp_int *(*mp_modarith)(mp_int *x, mp_int *y, mp_int *modulus)) { @@ -1320,12 +1353,6 @@ static void test_mac(const ssh2_macalg *malg) size_t maclen = malg->len; uint8_t *data = snewn(datalen + maclen, uint8_t); - /* Preliminarily key the MAC, to avoid the divergence of control - * flow in which hmac_key() avoids some free()s the first time - * through */ - random_read(mkey, malg->keylen); - ssh2_mac_setkey(m, make_ptrlen(mkey, malg->keylen)); - for (size_t i = 0; i < looplimit(16); i++) { random_read(mkey, malg->keylen); random_read(data, datalen); @@ -1386,12 +1413,47 @@ struct test { void (*testfn)(void); }; +static void test_argon2(void) +{ + /* + * We can only expect the Argon2i variant to pass this stringent + * test for no data-dependency, because the other two variants of + * Argon2 have _deliberate_ data-dependency. + */ + size_t inlen = 48+16+24+8; + uint8_t *indata = snewn(inlen, uint8_t); + ptrlen password = make_ptrlen(indata, 48); + ptrlen salt = make_ptrlen(indata+48, 16); + ptrlen secret = make_ptrlen(indata+48+16, 24); + ptrlen assoc = make_ptrlen(indata+48+16+24, 8); + + strbuf *outdata = strbuf_new(); + strbuf_append(outdata, 256); + + for (size_t i = 0; i < looplimit(16); i++) { + strbuf_clear(outdata); + random_read(indata, inlen); + + log_start(); + argon2(Argon2i, 32, 2, 2, 144, password, salt, secret, assoc, outdata); + log_end(); + } + + sfree(indata); + strbuf_free(outdata); +} + static const struct test tests[] = { #define STRUCT_TEST(X) { #X, test_##X }, TESTLIST(STRUCT_TEST) #undef STRUCT_TEST }; +void dputs(const char *buf) +{ + fputs(buf, stderr); +} + int main(int argc, char **argv) { bool doing_opts = true; @@ -1401,6 +1463,7 @@ int main(int argc, char **argv) bool test_names_given = false; memset(tests_to_run, 1, sizeof(tests_to_run)); + random_hash = ssh_hash_new(&ssh_sha256); while (--argc > 0) { char *p = *++argv; @@ -1454,6 +1517,15 @@ int main(int argc, char **argv) if (is_dry_run) { printf("Dry run (DynamoRIO instrumentation not detected)\n"); } else { + /* Print the address of main() in this run. The idea is that + * if this image is compiled to be position-independent, then + * PC values in the logs won't match the ones you get if you + * disassemble the binary, so it'll be harder to match up the + * log messages to the code. But if you know the address of a + * fixed (and not inlined) function in both worlds, you can + * find out the offset between them. */ + printf("Live run, main = %p\n", (void *)main); + if (!outdir) { fprintf(stderr, "expected -O option\n"); return 1; @@ -1561,11 +1633,13 @@ int main(int argc, char **argv) } } + ssh_hash_free(random_hash); + if (npass == nrun) { printf("All tests passed\n"); return 0; } else { - printf("%zu tests failed\n", nrun - npass); + printf("%"SIZEu" tests failed\n", nrun - npass); return 1; } } diff --git a/testzlib.c b/testzlib.c index 95419e9..0e7b424 100644 --- a/testzlib.c +++ b/testzlib.c @@ -24,6 +24,11 @@ void out_of_memory(void) exit(1); } +void dputs(const char *buf) +{ + fputs(buf, stderr); +} + int main(int argc, char **argv) { unsigned char buf[16], *outbuf; diff --git a/tree234.c b/tree234.c index 0ec2b52..a590382 100644 --- a/tree234.c +++ b/tree234.c @@ -1072,7 +1072,7 @@ int n_errors = 0; /* * Error reporting function. */ -void error(char *fmt, ...) +PRINTF_LIKE(1, 2) void error(char *fmt, ...) { va_list ap; printf("ERROR: "); @@ -1211,12 +1211,12 @@ int chknode(chkctx * ctx, int level, node234 * node, void verify(void) { - chkctx ctx; + chkctx ctx[1]; int i; void *p; - ctx.treedepth = -1; /* depth unknown yet */ - ctx.elemcount = 0; /* no elements seen yet */ + ctx->treedepth = -1; /* depth unknown yet */ + ctx->elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ @@ -1225,7 +1225,7 @@ void verify(void) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } - printf("tree depth: %d\n", ctx.treedepth); + printf("tree depth: %d\n", ctx->treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ @@ -1236,17 +1236,17 @@ void verify(void) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } - if (ctx.elemcount != i) { + if (ctx->elemcount != i) { error("tree really contains %d elements, enum gave %d", - ctx.elemcount, i); + ctx->elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); - if (ctx.elemcount != i) { + if (ctx->elemcount != i) { error("tree really contains %d elements, count234 gave %d", - ctx.elemcount, i); + ctx->elemcount, i); } } diff --git a/unix/Makefile.gtk b/unix/Makefile.gtk index e8982b1..0d7c3a1 100644 --- a/unix/Makefile.gtk +++ b/unix/Makefile.gtk @@ -67,11 +67,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=-DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # @@ -109,8 +104,8 @@ GTK_CONFIG = sh -c 'pkg-config gtk+-3.0 x11 $$0 2>/dev/null || pkg-config gtk+-2 unexport CFLAGS # work around a weird issue with krb5-config -CFLAGS = -O2 -Wall -Werror -std=gnu99 -Wvla -g -I.././ -I../charset/ \ - -I../windows/ -I../unix/ $(shell $(GTK_CONFIG) --cflags) -D _FILE_OFFSET_BITS=64 +CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g -I.././ -I../charset/ -I../windows/ \ + -I../unix/ $(shell $(GTK_CONFIG) --cflags) -D _FILE_OFFSET_BITS=64 XLDFLAGS = $(LDFLAGS) $(shell $(GTK_CONFIG) --libs) ULDFLAGS = $(LDFLAGS) ifeq (,$(findstring NO_GSSAPI,$(COMPAT))) @@ -136,84 +131,72 @@ man1dir=$(mandir)/man1 .SUFFIXES: -all: cgtest fuzzterm osxlaunch pageant plink pscp psftp pterm ptermapp putty \ - puttyapp puttygen puttytel testcrypt testsc testzlib uppity +all: cgtest fuzzterm osxlaunch pageant plink pscp psftp psocks psusan pterm \ + ptermapp putty puttyapp puttygen puttytel testcrypt testsc \ + testzlib uppity -cgtest: cgtest.o conf.o ecc.o import.o marshal.o memory.o misc.o mpint.o \ - notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o sshblowf.o \ - sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o \ - sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o \ - sshrsag.o sshsh256.o sshsh512.o sshsha.o stripctrl.o time.o \ +cgtest: cgtest.o conf.o console.o ecc.o import.o marshal.o memory.o \ + millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ + primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o - $(CC) -o $@ cgtest.o conf.o ecc.o import.o marshal.o memory.o misc.o \ - mpint.o notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o \ - sshblowf.o sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o \ - sshhmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - stripctrl.o time.o tree234.o utils.o uxcons.o uxgen.o \ - uxmisc.o uxnogtk.o uxnoise.o uxpoll.o uxstore.o uxutils.o \ - version.o wcwidth.o $(ULDFLAGS) + $(CC) -o $@ cgtest.o conf.o console.o ecc.o import.o marshal.o \ + memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ + tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ + uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ + $(ULDFLAGS) fuzzterm: be_none.o callback.o conf.o config.o dialog.o fromucs.o fuzzterm.o \ localenc.o logging.o macenc.o marshal.o memory.o mimeenc.o \ - minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o sercfg.o \ - settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ - toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ + minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o settings.o \ + slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ + tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o $(CC) -o $@ be_none.o callback.o conf.o config.o dialog.o fromucs.o \ fuzzterm.o localenc.o logging.o macenc.o marshal.o memory.o \ mimeenc.o minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o \ - sercfg.o settings.o slookup.o stripctrl.o terminal.o time.o \ - timing.o toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o \ - uxnogtk.o uxprint.o uxstore.o uxucs.o version.o wcwidth.o \ - xenc.o $(ULDFLAGS) + settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ + uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ + $(ULDFLAGS) osxlaunch: osxlaunch.o $(CC) -o $@ osxlaunch.o $(ULDFLAGS) -pageant: aqsync.o be_misc.o be_none.o callback.o conf.o ecc.o errsock.o \ - gtkask.o gtkmisc.o logging.o marshal.o memory.o misc.o \ - mpint.o nocproxy.o nogss.o nullplug.o pageant.o proxy.o \ - settings.o sshaes.o sshauxcrypt.o sshdes.o sshdss.o sshecc.o \ - sshhmac.o sshmd5.o sshprng.o sshpubk.o sshrsa.o sshsh256.o \ - sshsh512.o sshsha.o stripctrl.o time.o timing.o tree234.o \ - utils.o ux_x11.o uxagentc.o uxagentsock.o uxcons.o \ - uxfdsock.o uxmisc.o uxnet.o uxnoise.o uxpeer.o uxpgnt.o \ - uxpoll.o uxproxy.o uxsel.o uxsignal.o uxstore.o uxutils.o \ - version.o wcwidth.o x11fwd.o - $(CC) -o $@ aqsync.o be_misc.o be_none.o callback.o conf.o ecc.o \ +pageant: aqsync.o be_misc.o be_none.o callback.o conf.o console.o ecc.o \ errsock.o gtkask.o gtkmisc.o logging.o marshal.o memory.o \ misc.o mpint.o nocproxy.o nogss.o nullplug.o pageant.o \ - proxy.o settings.o sshaes.o sshauxcrypt.o sshdes.o sshdss.o \ - sshecc.o sshhmac.o sshmd5.o sshprng.o sshpubk.o sshrsa.o \ - sshsh256.o sshsh512.o sshsha.o stripctrl.o time.o timing.o \ - tree234.o utils.o ux_x11.o uxagentc.o uxagentsock.o uxcons.o \ + proxy.o settings.o sshaes.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshdes.o sshdss.o sshecc.o sshhmac.o sshmd5.o \ + sshprng.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o stripctrl.o time.o timing.o tree234.o utils.o \ + ux_x11.o uxagentc.o uxagentsock.o uxcliloop.o uxcons.o \ uxfdsock.o uxmisc.o uxnet.o uxnoise.o uxpeer.o uxpgnt.o \ uxpoll.o uxproxy.o uxsel.o uxsignal.o uxstore.o uxutils.o \ - version.o wcwidth.o x11fwd.o $(XLDFLAGS) + version.o wcwidth.o x11fwd.o + $(CC) -o $@ aqsync.o be_misc.o be_none.o callback.o conf.o console.o \ + ecc.o errsock.o gtkask.o gtkmisc.o logging.o marshal.o \ + memory.o misc.o mpint.o nocproxy.o nogss.o nullplug.o \ + pageant.o proxy.o settings.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshblake2.o sshdes.o sshdss.o sshecc.o \ + sshhmac.o sshmd5.o sshprng.o sshpubk.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o timing.o \ + tree234.o utils.o ux_x11.o uxagentc.o uxagentsock.o \ + uxcliloop.o uxcons.o uxfdsock.o uxmisc.o uxnet.o uxnoise.o \ + uxpeer.o uxpgnt.o uxpoll.o uxproxy.o uxsel.o uxsignal.o \ + uxstore.o uxutils.o version.o wcwidth.o x11fwd.o $(XLDFLAGS) -plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o ldisc.o logging.o mainchan.o \ - marshal.o memory.o misc.o mpint.o noterm.o nullplug.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ - sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-client.o ssh1login.o \ - ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ - ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ - ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o time.o \ - timing.o tree234.o utils.o ux_x11.o uxagentc.o uxcons.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o \ - uxshare.o uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ - wildcard.o x11fwd.o - $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ - cmdline.o conf.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ +plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o clicons.o cmdline.o \ + conf.o console.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o noterm.o \ nullplug.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ @@ -221,36 +204,41 @@ plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o time.o \ - timing.o tree234.o utils.o ux_x11.o uxagentc.o uxcons.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o \ - uxshare.o uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ - wildcard.o x11fwd.o $(ULDFLAGS) - -pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ - memory.o misc.o mpint.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o pscp.o psftpcommon.o settings.o sftp.o \ - sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o supdup.o telnet.o time.o timing.o tree234.o \ + utils.o ux_x11.o uxagentc.o uxcliloop.o uxcons.o uxfdsock.o \ + uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ + uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o uxshare.o \ + uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o + $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ + clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ + errsock.o ldisc.o logging.o mainchan.o marshal.o memory.o \ + misc.o mpint.o noterm.o nullplug.o pgssapi.o pinger.o \ + portfwd.o proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ - ssh2userauth.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ - sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ - sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshshare.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o uxagentc.o uxcons.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ - uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o - $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ - cmdline.o conf.o cproxy.o ecc.o errsock.o logging.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ + telnet.o time.o timing.o tree234.o utils.o ux_x11.o \ + uxagentc.o uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o \ + uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxplink.o uxpoll.o \ + uxproxy.o uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o \ + uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ + $(ULDFLAGS) + +pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ + conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o pscp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ @@ -258,35 +246,39 @@ pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o time.o timing.o \ - tree234.o utils.o uxagentc.o uxcons.o uxfdsock.o uxgss.o \ - uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxpoll.o \ - uxproxy.o uxsel.o uxsftp.o uxshare.o uxstore.o uxutils.o \ - version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) - -psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ - memory.o misc.o mpint.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o psftp.o psftpcommon.o settings.o sftp.o \ - sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ + uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ + uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ + uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o + $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ + clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ + errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ + mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ + pscp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ - ssh2userauth.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ - sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ - sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshshare.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o uxagentc.o uxcons.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ - uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o - $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ - cmdline.o conf.o cproxy.o ecc.o errsock.o logging.o \ + uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ + $(ULDFLAGS) + +psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ + conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ @@ -294,23 +286,99 @@ psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ + uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ + uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ + uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o + $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ + clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ + errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ + mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ + psftp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ + ssh2censor.o ssh2connection.o ssh2connection-client.o \ + ssh2kex-client.o ssh2transhk.o ssh2transport.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ + uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ + uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ + uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ + $(ULDFLAGS) + +psocks: be_misc.o callback.o conf.o console.o errsock.o logging.o marshal.o \ + memory.o misc.o nocproxy.o norand.o portfwd.o proxy.o \ + psocks.o sshutils.o stripctrl.o time.o timing.o tree234.o \ + utils.o uxcliloop.o uxcons.o uxfdsock.o uxmisc.o uxnet.o \ + uxnogtk.o uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsignal.o \ + uxsocks.o version.o wcwidth.o + $(CC) -o $@ be_misc.o callback.o conf.o console.o errsock.o \ + logging.o marshal.o memory.o misc.o nocproxy.o norand.o \ + portfwd.o proxy.o psocks.o sshutils.o stripctrl.o time.o \ + timing.o tree234.o utils.o uxcliloop.o uxcons.o uxfdsock.o \ + uxmisc.o uxnet.o uxnogtk.o uxpeer.o uxpoll.o uxproxy.o \ + uxsel.o uxsignal.o uxsocks.o version.o wcwidth.o $(ULDFLAGS) + +psusan: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ + logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ + mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o portfwd.o \ + primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ + settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ + ssh1censor.o ssh1connection.o ssh1connection-server.o \ + ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ + ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ + uxcliloop.o uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ + uxpeer.o uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o \ + uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ + wcwidth.o wildcard.o x11fwd.o + $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ + errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ + mpint.o mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o \ + portfwd.o primecandidate.o procnet.o proxy.o scpserver.o \ + sesschan.o settings.o sftpcommon.o sftpserver.o \ + smallprimes.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-server.o ssh1login-server.o ssh2bpp.o \ + ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ + ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ + ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprime.o \ + sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o sshserver.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o sshutils.o \ sshverstring.o sshzlib.o stripctrl.o time.o timing.o \ - tree234.o utils.o uxagentc.o uxcons.o uxfdsock.o uxgss.o \ - uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxpoll.o \ - uxproxy.o uxsel.o uxsftp.o uxshare.o uxstore.o uxutils.o \ - version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) + tree234.o utils.o ux_x11.o uxagentsock.o uxcliloop.o \ + uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ + uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o uxsftpserver.o \ + uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o $(ULDFLAGS) pterm: be_none.o callback.o cmdline.o conf.o config.o dialog.o fromucs.o \ gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o gtkmain.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ marshal.o memory.o mimeenc.o minibidi.o misc.o miscucs.o \ - nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o sessprep.o \ - settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ - toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ + nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o settings.o \ + slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ + tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ xpmpterm.o @@ -318,33 +386,33 @@ pterm: be_none.o callback.o cmdline.o conf.o config.o dialog.o fromucs.o \ fromucs.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmain.o gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o \ macenc.o marshal.o memory.o mimeenc.o minibidi.o misc.o \ - miscucs.o nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o \ - sessprep.o settings.o slookup.o stripctrl.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o utils.o uxcfg.o \ - uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o x11misc.o xenc.o \ - xkeysym.o xpmptcfg.o xpmpterm.o $(XLDFLAGS) + miscucs.o nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o \ + settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ + uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ + version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ + xpmpterm.o $(XLDFLAGS) ptermapp: be_none.o callback.o conf.o config.o dialog.o fromucs.o gtkapp.o \ gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o gtkmisc.o \ gtkwin.o ldisc.o localenc.o logging.o macenc.o marshal.o \ memory.o mimeenc.o minibidi.o misc.o miscucs.o nocmdline.o \ - nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o sessprep.o \ - settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ - toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ + nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o settings.o \ + slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ + tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ - xpmpterm.o cencode.o cdecode.o + xpmpterm.o $(CC) -o $@ be_none.o callback.o conf.o config.o dialog.o fromucs.o \ gtkapp.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ marshal.o memory.o mimeenc.o minibidi.o misc.o miscucs.o \ - nocmdline.o nocproxy.o nogss.o sbcs.o sbcsdat.o sercfg.o \ - sessprep.o settings.o slookup.o stripctrl.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o utils.o uxcfg.o \ - uxmisc.o uxprint.o uxpterm.o uxpty.o uxsel.o uxsignal.o \ - uxstore.o uxucs.o version.o wcwidth.o x11misc.o xenc.o \ - xkeysym.o xpmptcfg.o xpmpterm.o cencode.o cdecode.o $(XLDFLAGS) + nocmdline.o nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o \ + settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ + uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ + version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ + xpmpterm.o $(XLDFLAGS) putty: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ config.o cproxy.o dialog.o ecc.o errsock.o fromucs.o \ @@ -352,48 +420,48 @@ putty: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ mainchan.o marshal.o memory.o mimeenc.o minibidi.o misc.o \ miscucs.o mpint.o nullplug.o pgssapi.o pinger.o portfwd.o \ - proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o sessprep.o \ + proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o utils.o ux_x11.o \ - uxagentc.o uxcfg.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ - uxnoise.o uxpeer.o uxpoll.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o uxucs.o \ - uxutils.o version.o wcwidth.o wildcard.o x11fwd.o x11misc.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o \ - cencode.o cdecode.o + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ + uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ + uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ + uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ + wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ + xpmpucfg.o xpmputty.o $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ cmdline.o conf.o config.o cproxy.o dialog.o ecc.o errsock.o \ fromucs.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmain.o gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o \ macenc.o mainchan.o marshal.o memory.o mimeenc.o minibidi.o \ misc.o miscucs.o mpint.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o \ - sessprep.o settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ + portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ + settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o utils.o ux_x11.o \ - uxagentc.o uxcfg.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ - uxnoise.o uxpeer.o uxpoll.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o uxucs.o \ - uxutils.o version.o wcwidth.o wildcard.o x11fwd.o x11misc.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o \ - cencode.o cdecode.o $(XLDFLAGS) + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ + uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ + uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ + uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ + wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ + xpmpucfg.o xpmputty.o $(XLDFLAGS) puttyapp: agentf.o aqsync.o be_all_s.o be_misc.o callback.o conf.o config.o \ cproxy.o dialog.o ecc.o errsock.o fromucs.o gtkapp.o \ @@ -401,150 +469,167 @@ puttyapp: agentf.o aqsync.o be_all_s.o be_misc.o callback.o conf.o config.o \ gtkwin.o ldisc.o localenc.o logging.o macenc.o mainchan.o \ marshal.o memory.o mimeenc.o minibidi.o misc.o miscucs.o \ mpint.o nocmdline.o nullplug.o pgssapi.o pinger.o portfwd.o \ - proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o sessprep.o \ + proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o utils.o ux_x11.o \ - uxagentc.o uxcfg.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ - uxnoise.o uxpeer.o uxpoll.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o uxucs.o \ - uxutils.o version.o wcwidth.o wildcard.o x11fwd.o x11misc.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ + uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ + uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ + uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ + wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ + xpmpucfg.o xpmputty.o $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o conf.o \ config.o cproxy.o dialog.o ecc.o errsock.o fromucs.o \ gtkapp.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ mainchan.o marshal.o memory.o mimeenc.o minibidi.o misc.o \ miscucs.o mpint.o nocmdline.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o \ - sessprep.o settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ + portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ + settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o terminal.o \ - time.o timing.o toucs.o tree234.o utf8.o utils.o ux_x11.o \ - uxagentc.o uxcfg.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ - uxnoise.o uxpeer.o uxpoll.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o uxucs.o \ - uxutils.o version.o wcwidth.o wildcard.o x11fwd.o x11misc.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS) + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ + uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ + uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ + uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ + wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ + xpmpucfg.o xpmputty.o $(XLDFLAGS) -puttygen: cmdgen.o conf.o ecc.o import.o marshal.o memory.o misc.o mpint.o \ - notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o sshblowf.o \ - sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o \ - sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o \ - sshrsag.o sshsh256.o sshsh512.o sshsha.o stripctrl.o time.o \ +puttygen: cmdgen.o conf.o console.o ecc.o import.o marshal.o memory.o \ + millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ + primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o - $(CC) -o $@ cmdgen.o conf.o ecc.o import.o marshal.o memory.o misc.o \ - mpint.o notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o \ - sshblowf.o sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o \ - sshhmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - stripctrl.o time.o tree234.o utils.o uxcons.o uxgen.o \ - uxmisc.o uxnogtk.o uxnoise.o uxpoll.o uxstore.o uxutils.o \ - version.o wcwidth.o $(ULDFLAGS) + $(CC) -o $@ cmdgen.o conf.o console.o ecc.o import.o marshal.o \ + memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ + tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ + uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ + $(ULDFLAGS) puttytel: be_misc.o be_nos_s.o callback.o cmdline.o conf.o config.o dialog.o \ errsock.o fromucs.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o \ gtkfont.o gtkmain.o gtkmisc.o gtkwin.o ldisc.o localenc.o \ logging.o macenc.o marshal.o memory.o mimeenc.o minibidi.o \ - misc.o miscucs.o nocproxy.o nogss.o pinger.o proxy.o raw.o \ - rlogin.o sbcs.o sbcsdat.o sercfg.o sessprep.o settings.o \ - slookup.o stripctrl.o telnet.o terminal.o time.o timing.o \ - toucs.o tree234.o utf8.o utils.o uxcfg.o uxfdsock.o uxmisc.o \ - uxnet.o uxpeer.o uxpoll.o uxprint.o uxproxy.o uxputty.o \ - uxsel.o uxser.o uxsignal.o uxstore.o uxucs.o uxutils.o \ - version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmpucfg.o \ - xpmputty.o + misc.o miscucs.o nocproxy.o nogss.o norand.o pinger.o \ + proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ + settings.o slookup.o stripctrl.o supdup.o telnet.o \ + terminal.o time.o timing.o toucs.o tree234.o utf8.o utils.o \ + uxcfg.o uxfdsock.o uxmisc.o uxnet.o uxpeer.o uxpoll.o \ + uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ + uxstore.o uxucs.o uxutils.o version.o wcwidth.o x11misc.o \ + xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(CC) -o $@ be_misc.o be_nos_s.o callback.o cmdline.o conf.o \ config.o dialog.o errsock.o fromucs.o gtkcfg.o gtkcols.o \ gtkcomm.o gtkdlg.o gtkfont.o gtkmain.o gtkmisc.o gtkwin.o \ ldisc.o localenc.o logging.o macenc.o marshal.o memory.o \ mimeenc.o minibidi.o misc.o miscucs.o nocproxy.o nogss.o \ - pinger.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sercfg.o \ - sessprep.o settings.o slookup.o stripctrl.o telnet.o \ - terminal.o time.o timing.o toucs.o tree234.o utf8.o utils.o \ - uxcfg.o uxfdsock.o uxmisc.o uxnet.o uxpeer.o uxpoll.o \ - uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ - uxstore.o uxucs.o uxutils.o version.o wcwidth.o x11misc.o \ - xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS) + norand.o pinger.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o \ + sessprep.o settings.o slookup.o stripctrl.o supdup.o \ + telnet.o terminal.o time.o timing.o toucs.o tree234.o utf8.o \ + utils.o uxcfg.o uxfdsock.o uxmisc.o uxnet.o uxpeer.o \ + uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ + uxsignal.o uxstore.o uxucs.o uxutils.o version.o wcwidth.o \ + x11misc.o xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS) -testcrypt: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshauxcrypt.o \ - sshblowf.o sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o testcrypt.o \ - tree234.o utils.o uxutils.o - $(CC) -o $@ ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o sshmd5.o \ - sshprime.o sshprng.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - testcrypt.o tree234.o utils.o uxutils.o $(ULDFLAGS) +testcrypt: ecc.o marshal.o memory.o millerrabin.o mpint.o mpunsafe.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o ssharcf.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ + sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ + sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o testcrypt.o tree234.o utils.o uxutils.o + $(CC) -o $@ ecc.o marshal.o memory.o millerrabin.o mpint.o \ + mpunsafe.o pockle.o primecandidate.o smallprimes.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o \ + sshprng.o sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o sshsha3.o testcrypt.o tree234.o utils.o uxutils.o \ + $(ULDFLAGS) -testsc: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshauxcrypt.o \ - sshblowf.o sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshhmac.o sshmac.o sshmd5.o sshrsa.o \ - sshsh256.o sshsh512.o sshsha.o testsc.o tree234.o utils.o \ - uxutils.o wildcard.o +testsc: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshargon2.o \ + sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o \ + sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshsha3.o testsc.o tree234.o utils.o uxutils.o \ + wildcard.o $(CC) -o $@ ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o sshmac.o \ - sshmd5.o sshrsa.o sshsh256.o sshsh512.o sshsha.o testsc.o \ - tree234.o utils.o uxutils.o wildcard.o $(ULDFLAGS) + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o \ + sshhmac.o sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshsha3.o testsc.o tree234.o utils.o \ + uxutils.o wildcard.o $(ULDFLAGS) testzlib: marshal.o memory.o sshzlib.o testzlib.o utils.o $(CC) -o $@ marshal.o memory.o sshzlib.o testzlib.o utils.o \ $(ULDFLAGS) uppity: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ - logging.o marshal.o memory.o misc.o mpint.o nullplug.o \ - pgssapi.o portfwd.o procnet.o proxy.o scpserver.o sesschan.o \ - settings.o sftpcommon.o sftpserver.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-server.o ssh1login-server.o \ - ssh2bpp.o ssh2censor.o ssh2connection.o \ - ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ - ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o \ - sshrand.o sshrsa.o sshrsag.o sshserver.o sshsh256.o \ - sshsh512.o sshsha.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o uxserver.o \ - uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ - wcwidth.o wildcard.o x11fwd.o + logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ + mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ + primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ + settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ + ssh1censor.o ssh1connection.o ssh1connection-server.o \ + ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ + ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ + uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ + uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ + uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ + version.o wcwidth.o wildcard.o x11fwd.o $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ - errsock.o logging.o marshal.o memory.o misc.o mpint.o \ - nullplug.o pgssapi.o portfwd.o procnet.o proxy.o scpserver.o \ - sesschan.o settings.o sftpcommon.o sftpserver.o ssh1bpp.o \ + errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ + mpint.o mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ + primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ + settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ - ssh1login-server.o ssh2bpp.o ssh2censor.o ssh2connection.o \ - ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ - ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o \ - sshrand.o sshrsa.o sshrsag.o sshserver.o sshsh256.o \ - sshsh512.o sshsha.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o uxserver.o \ - uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ - wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) + ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ + ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ + uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ + uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ + uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ + version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) agentf.o: ../agentf.c ../putty.h ../ssh.h ../pageant.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -587,15 +672,20 @@ callback.o: ../callback.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../callback.c -cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cgtest.c -cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ +clicons.o: ../clicons.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../clicons.c +cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h ../mpint.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c @@ -615,6 +705,11 @@ config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c +console.o: ../console.c ../putty.h ../misc.h ../console.h ../defs.h \ + ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ + ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../console.c cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -637,10 +732,11 @@ errsock.o: ../errsock.c ../tree234.h ../putty.h ../network.h ../defs.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../errsock.c fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c -fuzzterm.o: ../fuzzterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../tree234.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h +fuzzterm.o: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../tree234.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../fuzzterm.c gtkapp.o: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -670,10 +766,11 @@ gtkcomm.o: ../unix/gtkcomm.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcomm.c gtkdlg.o: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h ../unix/gtkcols.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ - ../storage.h ../dialog.h ../tree234.h ../licence.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../storage.h ../dialog.h ../tree234.h ../licence.h ../ssh.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../tree234.h \ @@ -733,6 +830,10 @@ marshal.o: ../marshal.c ../marshal.h ../misc.h ../defs.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../marshal.c memory.o: ../memory.c ../defs.h ../puttymem.h ../misc.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../memory.c +millerrabin.o: ../millerrabin.c ../ssh.h ../sshkeygen.h ../mpint.h \ + ../mpunsafe.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../millerrabin.c mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c minibidi.o: ../minibidi.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ @@ -753,6 +854,9 @@ miscucs.o: ../miscucs.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ mpint.o: ../mpint.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpint.c +mpunsafe.o: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ + ../mpint_i.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpunsafe.c nocmdline.o: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -768,6 +872,11 @@ nogss.o: ../nogss.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c +norand.o: ../norand.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../norand.c noterm.o: ../noterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ @@ -801,12 +910,20 @@ pinger.o: ../pinger.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c +pockle.o: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h ../mpunsafe.h \ + ../tree234.h ../puttymem.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pockle.c portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c +primecandidate.o: ../primecandidate.c ../ssh.h ../mpint.h ../mpunsafe.h \ + ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../primecandidate.c procnet.o: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/procnet.c proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ @@ -832,6 +949,12 @@ psftpcommon.o: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftpcommon.c +psocks.o: ../psocks.c ../putty.h ../misc.h ../ssh.h ../sshchan.h ../psocks.h \ + ../defs.h ../puttyps.h ../network.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psocks.c raw.o: ../raw.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -852,12 +975,6 @@ scpserver.o: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h ../sshchan.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../scpserver.c -sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c sesschan.o: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h ../sshserver.h \ ../sftp.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ @@ -895,6 +1012,10 @@ sizetip.o: ../windows/sizetip.c ../putty.h ../defs.h ../puttyps.h \ slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c +smallprimes.o: ../smallprimes.c ../ssh.h ../sshkeygen.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../smallprimes.c ssh.o: ../ssh.c ../putty.h ../pageant.h ../tree234.h ../storage.h \ ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h \ @@ -943,11 +1064,11 @@ ssh1login.o: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h ../sshbpp.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login.c ssh1login-server.o: ../ssh1login-server.c ../putty.h ../mpint.h ../ssh.h \ - ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ + ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login-server.c ssh2bpp.o: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -998,12 +1119,13 @@ ssh2kex-client.o: ../ssh2kex-client.c ../putty.h ../ssh.h ../sshbpp.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-client.c ssh2kex-server.o: ../ssh2kex-server.c ../putty.h ../ssh.h ../sshbpp.h \ - ../sshppl.h ../sshcr.h ../sshserver.h ../storage.h \ - ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ - ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ - ../pgssapi.h ../windows/winhelp.h ../charset/charset.h + ../sshppl.h ../sshcr.h ../sshserver.h ../sshkeygen.h \ + ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ + ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-server.c ssh2transhk.o: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1041,6 +1163,12 @@ sshaes.o: ../sshaes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c +sshargon2.o: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ + ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshargon2.c sshauxcrypt.o: ../sshauxcrypt.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h @@ -1049,6 +1177,9 @@ sshbcrypt.o: ../sshbcrypt.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbcrypt.c +sshblake2.o: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblake2.c sshblowf.o: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h @@ -1081,17 +1212,17 @@ sshdss.o: ../sshdss.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c -sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../mpint.h ../defs.h \ - ../puttymem.h ../marshal.h ../tree234.h ../network.h \ - ../sshttymodes.h +sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h ../mpint.h \ + ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ + ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecc.c -sshecdsag.o: ../sshecdsag.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshecdsag.o: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecdsag.c sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ @@ -1108,11 +1239,11 @@ sshmac.o: ../sshmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c -sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h ../sshkeygen.h \ + ../puttymem.h ../tree234.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c -sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint.h ../defs.h \ +sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ @@ -1134,13 +1265,13 @@ sshrsa.o: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c -sshrsag.o: ../sshrsag.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshrsag.o: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c sshserver.o: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h ../sshppl.h \ - ../sshserver.h ../sshgssc.h ../sshgss.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshchan.h ../sshserver.h ../sshgssc.h ../sshgss.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h @@ -1154,12 +1285,21 @@ sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c +sshsha3.o: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha3.c sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshshare.c +sshutils.o: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshutils.c sshverstring.o: ../sshverstring.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -1174,6 +1314,11 @@ stripctrl.o: ../stripctrl.c ../putty.h ../terminal.h ../misc.h ../marshal.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../stripctrl.c +supdup.o: ../supdup.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../supdup.c telnet.o: ../telnet.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -1184,9 +1329,9 @@ terminal.o: ../terminal.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c -testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../misc.h ../mpint.h ../ecc.h \ - ../testcrypt.h ../puttymem.h ../tree234.h ../network.h \ - ../sshttymodes.h ../marshal.h +testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h ../misc.h \ + ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ + ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testcrypt.c testsc.o: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h ../mpint.h \ ../ecc.h ../puttyps.h ../network.h ../marshal.h \ @@ -1210,7 +1355,8 @@ tree234.o: ../tree234.c ../defs.h ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c -utils.o: ../utils.c ../defs.h ../misc.h ../puttymem.h ../marshal.h +utils.o: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h ../marshal.h \ + ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../utils.c ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1235,8 +1381,13 @@ uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c -uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ +uxcliloop.o: ../unix/uxcliloop.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcliloop.c +uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../console.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h @@ -1289,10 +1440,11 @@ uxpgnt.o: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h ../pageant.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpgnt.c -uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h +uxplink.o: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h ../tree234.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c uxpoll.o: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1309,21 +1461,28 @@ uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c +uxpsusan.o: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpsusan.c uxpterm.o: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c -uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../tree234.h ../sshttymodes.h \ - ../sshsignals.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../puttymem.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c -uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../unix/gtkcompat.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ +uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h ../tree234.h \ + ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../puttymem.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c +uxputty.o: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ + ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1347,11 +1506,11 @@ uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../defs.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c -uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sftp.h \ - ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sshserver.h \ + ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftpserver.c uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../ssh.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ @@ -1361,6 +1520,12 @@ uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxshare.c uxsignal.o: ../unix/uxsignal.c ../defs.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c +uxsocks.o: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsocks.c uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -1371,13 +1536,17 @@ uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c -uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ +uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../unix/uxutils.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxutils.c -version.o: ../version.c ../empty.h ../version.h +version.o: ../version.c ../putty.h ../ssh.h ../empty.h ../version.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c wcwidth.o: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ @@ -1389,10 +1558,10 @@ wildcard.o: ../wildcard.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c -wincapi.o: ../windows/wincapi.c ../putty.h ../windows/wincapi.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ +wincapi.o: ../windows/wincapi.c ../putty.h ../ssh.h ../windows/wincapi.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincapi.c wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ @@ -1401,11 +1570,16 @@ wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c -wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +wincliloop.o: ../windows/wincliloop.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincliloop.c +wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ + ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ @@ -1418,17 +1592,18 @@ windefs.o: ../windows/windefs.c ../putty.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ - ../storage.h ../dialog.h ../licence.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winseat.h ../storage.h ../dialog.h ../licence.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ - ../windows/win_res.h ../windows/winsecur.h ../tree234.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../windows/win_res.h ../windows/winsecur.h \ + ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ ../sshgssc.h ../misc.h ../defs.h ../puttyps.h ../network.h \ @@ -1468,10 +1643,11 @@ winmiscs.o: ../windows/winmiscs.c ../putty.h ../defs.h ../puttyps.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmiscs.c -winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../defs.h \ - ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ - ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h +winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../ssh.h \ + ../defs.h ../puttyps.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c winnohlp.o: ../windows/winnohlp.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1498,23 +1674,26 @@ winnps.o: ../windows/winnps.c ../tree234.h ../putty.h ../network.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnps.c -winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../licence.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ - ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h +winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../licence.h ../windows/winsecur.h ../windows/puttygen-rc.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ - ../windows/winsecur.h ../pageant.h ../licence.h ../defs.h \ - ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../pageant.h \ + ../licence.h ../windows/pageant-rc.h ../defs.h ../puttyps.h \ + ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c winpgntc.o: ../windows/winpgntc.c ../putty.h ../pageant.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ @@ -1538,6 +1717,16 @@ winsecur.o: ../windows/winsecur.c ../putty.h ../windows/winsecur.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsecur.c +winselcli.o: ../windows/winselcli.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselcli.c +winselgui.o: ../windows/winselgui.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselgui.c winser.o: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -1556,6 +1745,12 @@ winshare.o: ../windows/winshare.c ../tree234.h ../putty.h ../network.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winshare.c +winsocks.o: ../windows/winsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsocks.c winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -1608,12 +1803,6 @@ xpmpucfg.o: ../unix/xpmpucfg.c xpmputty.o: ../unix/xpmputty.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c -cencode.o: ../windows/cencode.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/cencode.c - -cdecode.o: ../windows/cdecode.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/cdecode.c - install: mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) $(INSTALL_PROGRAM) -m 755 pageant $(DESTDIR)$(bindir)/pageant @@ -1644,6 +1833,6 @@ install-strip: $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" clean: - rm -f *.o cgtest fuzzterm osxlaunch pageant plink pscp psftp pterm ptermapp putty puttyapp puttygen puttytel testcrypt testsc testzlib uppity + rm -f *.o cgtest fuzzterm osxlaunch pageant plink pscp psftp psocks psusan pterm ptermapp putty puttyapp puttygen puttytel testcrypt testsc testzlib uppity FORCE: diff --git a/unix/Makefile.ux b/unix/Makefile.ux index a2e4060..6cd66c6 100644 --- a/unix/Makefile.ux +++ b/unix/Makefile.ux @@ -67,11 +67,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=-DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # @@ -101,8 +96,8 @@ CC = $(TOOLPATH)cc unexport CFLAGS # work around a weird issue with krb5-config -CFLAGS = -O2 -Wall -Werror -std=gnu99 -Wvla -g -I.././ -I../charset/ \ - -I../windows/ -I../unix/ -D _FILE_OFFSET_BITS=64 +CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g -I.././ -I../charset/ -I../windows/ \ + -I../unix/ -D _FILE_OFFSET_BITS=64 ULDFLAGS = $(LDFLAGS) INSTALL=install INSTALL_PROGRAM=$(INSTALL) @@ -117,63 +112,48 @@ man1dir=$(mandir)/man1 .SUFFIXES: -all: cgtest fuzzterm osxlaunch plink pscp psftp puttygen testcrypt testsc \ - testzlib uppity +all: cgtest fuzzterm osxlaunch plink pscp psftp psocks psusan puttygen \ + testcrypt testsc testzlib uppity -cgtest: cgtest.o conf.o ecc.o import.o marshal.o memory.o misc.o mpint.o \ - notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o sshblowf.o \ - sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o \ - sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o \ - sshrsag.o sshsh256.o sshsh512.o sshsha.o stripctrl.o time.o \ +cgtest: cgtest.o conf.o console.o ecc.o import.o marshal.o memory.o \ + millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ + primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o - $(CC) -o $@ cgtest.o conf.o ecc.o import.o marshal.o memory.o misc.o \ - mpint.o notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o \ - sshblowf.o sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o \ - sshhmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - stripctrl.o time.o tree234.o utils.o uxcons.o uxgen.o \ - uxmisc.o uxnogtk.o uxnoise.o uxpoll.o uxstore.o uxutils.o \ - version.o wcwidth.o $(ULDFLAGS) + $(CC) -o $@ cgtest.o conf.o console.o ecc.o import.o marshal.o \ + memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ + tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ + uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ + $(ULDFLAGS) fuzzterm: be_none.o callback.o conf.o config.o dialog.o fromucs.o fuzzterm.o \ localenc.o logging.o macenc.o marshal.o memory.o mimeenc.o \ - minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o sercfg.o \ - settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ - toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ + minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o settings.o \ + slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ + tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o $(CC) -o $@ be_none.o callback.o conf.o config.o dialog.o fromucs.o \ fuzzterm.o localenc.o logging.o macenc.o marshal.o memory.o \ mimeenc.o minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o \ - sercfg.o settings.o slookup.o stripctrl.o terminal.o time.o \ - timing.o toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o \ - uxnogtk.o uxprint.o uxstore.o uxucs.o version.o wcwidth.o \ - xenc.o $(ULDFLAGS) + settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ + toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ + uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ + $(ULDFLAGS) osxlaunch: osxlaunch.o $(CC) -o $@ osxlaunch.o $(ULDFLAGS) -plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o ldisc.o logging.o mainchan.o \ - marshal.o memory.o misc.o mpint.o noterm.o nullplug.o \ - pgssapi.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ - sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-client.o ssh1login.o \ - ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ - ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ - ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o time.o \ - timing.o tree234.o utils.o ux_x11.o uxagentc.o uxcons.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o \ - uxshare.o uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ - wildcard.o x11fwd.o - $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ - cmdline.o conf.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ +plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o clicons.o cmdline.o \ + conf.o console.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o noterm.o \ nullplug.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ @@ -181,36 +161,41 @@ plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o time.o \ - timing.o tree234.o utils.o ux_x11.o uxagentc.o uxcons.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o \ - uxshare.o uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ - wildcard.o x11fwd.o $(ULDFLAGS) - -pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ - memory.o misc.o mpint.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o pscp.o psftpcommon.o settings.o sftp.o \ - sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o supdup.o telnet.o time.o timing.o tree234.o \ + utils.o ux_x11.o uxagentc.o uxcliloop.o uxcons.o uxfdsock.o \ + uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ + uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o uxshare.o \ + uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o + $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ + clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ + errsock.o ldisc.o logging.o mainchan.o marshal.o memory.o \ + misc.o mpint.o noterm.o nullplug.o pgssapi.o pinger.o \ + portfwd.o proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ - ssh2userauth.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ - sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ - sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshshare.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o uxagentc.o uxcons.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ - uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o - $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ - cmdline.o conf.o cproxy.o ecc.o errsock.o logging.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ + telnet.o time.o timing.o tree234.o utils.o ux_x11.o \ + uxagentc.o uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o \ + uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxplink.o uxpoll.o \ + uxproxy.o uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o \ + uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ + $(ULDFLAGS) + +pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ + conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o pscp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ @@ -218,35 +203,39 @@ pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o time.o timing.o \ - tree234.o utils.o uxagentc.o uxcons.o uxfdsock.o uxgss.o \ - uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxpoll.o \ - uxproxy.o uxsel.o uxsftp.o uxshare.o uxstore.o uxutils.o \ - version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) - -psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ - memory.o misc.o mpint.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o psftp.o psftpcommon.o settings.o sftp.o \ - sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ + uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ + uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ + uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o + $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ + clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ + errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ + mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ + pscp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ - ssh2userauth.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ - sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ - sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshshare.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o uxagentc.o uxcons.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ - uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o - $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ - cmdline.o conf.o cproxy.o ecc.o errsock.o logging.o \ + uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ + $(ULDFLAGS) + +psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ + conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ @@ -254,94 +243,185 @@ psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ + uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ + uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ + uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o + $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ + clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ + errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ + mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ + psftp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ + ssh2censor.o ssh2connection.o ssh2connection-client.o \ + ssh2kex-client.o ssh2transhk.o ssh2transport.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ + uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ + uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ + uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ + $(ULDFLAGS) + +psocks: be_misc.o callback.o conf.o console.o errsock.o logging.o marshal.o \ + memory.o misc.o nocproxy.o norand.o portfwd.o proxy.o \ + psocks.o sshutils.o stripctrl.o time.o timing.o tree234.o \ + utils.o uxcliloop.o uxcons.o uxfdsock.o uxmisc.o uxnet.o \ + uxnogtk.o uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsignal.o \ + uxsocks.o version.o wcwidth.o + $(CC) -o $@ be_misc.o callback.o conf.o console.o errsock.o \ + logging.o marshal.o memory.o misc.o nocproxy.o norand.o \ + portfwd.o proxy.o psocks.o sshutils.o stripctrl.o time.o \ + timing.o tree234.o utils.o uxcliloop.o uxcons.o uxfdsock.o \ + uxmisc.o uxnet.o uxnogtk.o uxpeer.o uxpoll.o uxproxy.o \ + uxsel.o uxsignal.o uxsocks.o version.o wcwidth.o $(ULDFLAGS) + +psusan: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ + logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ + mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o portfwd.o \ + primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ + settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ + ssh1censor.o ssh1connection.o ssh1connection-server.o \ + ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ + ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ + uxcliloop.o uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ + uxpeer.o uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o \ + uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ + wcwidth.o wildcard.o x11fwd.o + $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ + errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ + mpint.o mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o \ + portfwd.o primecandidate.o procnet.o proxy.o scpserver.o \ + sesschan.o settings.o sftpcommon.o sftpserver.o \ + smallprimes.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-server.o ssh1login-server.o ssh2bpp.o \ + ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ + ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ + ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprime.o \ + sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o sshserver.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o sshutils.o \ sshverstring.o sshzlib.o stripctrl.o time.o timing.o \ - tree234.o utils.o uxagentc.o uxcons.o uxfdsock.o uxgss.o \ - uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxpoll.o \ - uxproxy.o uxsel.o uxsftp.o uxshare.o uxstore.o uxutils.o \ - version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) + tree234.o utils.o ux_x11.o uxagentsock.o uxcliloop.o \ + uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ + uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o uxsftpserver.o \ + uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ + wildcard.o x11fwd.o $(ULDFLAGS) -puttygen: cmdgen.o conf.o ecc.o import.o marshal.o memory.o misc.o mpint.o \ - notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o sshblowf.o \ - sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o \ - sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o \ - sshrsag.o sshsh256.o sshsh512.o sshsha.o stripctrl.o time.o \ +puttygen: cmdgen.o conf.o console.o ecc.o import.o marshal.o memory.o \ + millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ + primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o - $(CC) -o $@ cmdgen.o conf.o ecc.o import.o marshal.o memory.o misc.o \ - mpint.o notiming.o sshaes.o sshauxcrypt.o sshbcrypt.o \ - sshblowf.o sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o \ - sshhmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - stripctrl.o time.o tree234.o utils.o uxcons.o uxgen.o \ - uxmisc.o uxnogtk.o uxnoise.o uxpoll.o uxstore.o uxutils.o \ - version.o wcwidth.o $(ULDFLAGS) + $(CC) -o $@ cmdgen.o conf.o console.o ecc.o import.o marshal.o \ + memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ + tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ + uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ + $(ULDFLAGS) -testcrypt: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshauxcrypt.o \ - sshblowf.o sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o testcrypt.o \ - tree234.o utils.o uxutils.o - $(CC) -o $@ ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o sshmd5.o \ - sshprime.o sshprng.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - testcrypt.o tree234.o utils.o uxutils.o $(ULDFLAGS) +testcrypt: ecc.o marshal.o memory.o millerrabin.o mpint.o mpunsafe.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o ssharcf.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ + sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ + sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o testcrypt.o tree234.o utils.o uxutils.o + $(CC) -o $@ ecc.o marshal.o memory.o millerrabin.o mpint.o \ + mpunsafe.o pockle.o primecandidate.o smallprimes.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o \ + sshprng.o sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ + sshsha.o sshsha3.o testcrypt.o tree234.o utils.o uxutils.o \ + $(ULDFLAGS) -testsc: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshauxcrypt.o \ - sshblowf.o sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshhmac.o sshmac.o sshmd5.o sshrsa.o \ - sshsh256.o sshsh512.o sshsha.o testsc.o tree234.o utils.o \ - uxutils.o wildcard.o +testsc: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshargon2.o \ + sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o \ + sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshsha3.o testsc.o tree234.o utils.o uxutils.o \ + wildcard.o $(CC) -o $@ ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o sshmac.o \ - sshmd5.o sshrsa.o sshsh256.o sshsh512.o sshsha.o testsc.o \ - tree234.o utils.o uxutils.o wildcard.o $(ULDFLAGS) + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o \ + sshhmac.o sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshsha3.o testsc.o tree234.o utils.o \ + uxutils.o wildcard.o $(ULDFLAGS) testzlib: marshal.o memory.o sshzlib.o testzlib.o utils.o $(CC) -o $@ marshal.o memory.o sshzlib.o testzlib.o utils.o \ $(ULDFLAGS) uppity: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ - logging.o marshal.o memory.o misc.o mpint.o nullplug.o \ - pgssapi.o portfwd.o procnet.o proxy.o scpserver.o sesschan.o \ - settings.o sftpcommon.o sftpserver.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-server.o ssh1login-server.o \ - ssh2bpp.o ssh2censor.o ssh2connection.o \ - ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ - ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o \ - sshrand.o sshrsa.o sshrsag.o sshserver.o sshsh256.o \ - sshsh512.o sshsha.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o uxserver.o \ - uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ - wcwidth.o wildcard.o x11fwd.o + logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ + mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ + primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ + settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ + ssh1censor.o ssh1connection.o ssh1connection-server.o \ + ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ + ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ + uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ + uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ + uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ + version.o wcwidth.o wildcard.o x11fwd.o $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ - errsock.o logging.o marshal.o memory.o misc.o mpint.o \ - nullplug.o pgssapi.o portfwd.o procnet.o proxy.o scpserver.o \ - sesschan.o settings.o sftpcommon.o sftpserver.o ssh1bpp.o \ + errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ + mpint.o mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ + primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ + settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ - ssh1login-server.o ssh2bpp.o ssh2censor.o ssh2connection.o \ - ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ - ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o \ - sshrand.o sshrsa.o sshrsag.o sshserver.o sshsh256.o \ - sshsh512.o sshsha.o sshverstring.o sshzlib.o stripctrl.o \ - time.o timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ - uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ - uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o uxserver.o \ - uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ - wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) + ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ + ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ + timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ + uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ + uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ + uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ + version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) agentf.o: ../agentf.c ../putty.h ../ssh.h ../pageant.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -384,15 +464,20 @@ callback.o: ../callback.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../callback.c -cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cgtest.c -cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ +clicons.o: ../clicons.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../clicons.c +cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h ../mpint.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c @@ -412,6 +497,11 @@ config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c +console.o: ../console.c ../putty.h ../misc.h ../console.h ../defs.h \ + ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ + ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../console.c cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -434,10 +524,11 @@ errsock.o: ../errsock.c ../tree234.h ../putty.h ../network.h ../defs.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../errsock.c fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c -fuzzterm.o: ../fuzzterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../tree234.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h +fuzzterm.o: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../tree234.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../fuzzterm.c gtkapp.o: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -467,10 +558,11 @@ gtkcomm.o: ../unix/gtkcomm.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcomm.c gtkdlg.o: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h ../unix/gtkcols.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ - ../storage.h ../dialog.h ../tree234.h ../licence.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../storage.h ../dialog.h ../tree234.h ../licence.h ../ssh.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../tree234.h \ @@ -530,6 +622,10 @@ marshal.o: ../marshal.c ../marshal.h ../misc.h ../defs.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../marshal.c memory.o: ../memory.c ../defs.h ../puttymem.h ../misc.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../memory.c +millerrabin.o: ../millerrabin.c ../ssh.h ../sshkeygen.h ../mpint.h \ + ../mpunsafe.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../millerrabin.c mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c minibidi.o: ../minibidi.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ @@ -550,6 +646,9 @@ miscucs.o: ../miscucs.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ mpint.o: ../mpint.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpint.c +mpunsafe.o: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ + ../mpint_i.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpunsafe.c nocmdline.o: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -565,6 +664,11 @@ nogss.o: ../nogss.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c +norand.o: ../norand.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../norand.c noterm.o: ../noterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ @@ -598,12 +702,20 @@ pinger.o: ../pinger.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c +pockle.o: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h ../mpunsafe.h \ + ../tree234.h ../puttymem.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pockle.c portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c +primecandidate.o: ../primecandidate.c ../ssh.h ../mpint.h ../mpunsafe.h \ + ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../primecandidate.c procnet.o: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/procnet.c proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ @@ -629,6 +741,12 @@ psftpcommon.o: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftpcommon.c +psocks.o: ../psocks.c ../putty.h ../misc.h ../ssh.h ../sshchan.h ../psocks.h \ + ../defs.h ../puttyps.h ../network.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psocks.c raw.o: ../raw.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -649,12 +767,6 @@ scpserver.o: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h ../sshchan.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../scpserver.c -sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c sesschan.o: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h ../sshserver.h \ ../sftp.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ @@ -692,6 +804,10 @@ sizetip.o: ../windows/sizetip.c ../putty.h ../defs.h ../puttyps.h \ slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c +smallprimes.o: ../smallprimes.c ../ssh.h ../sshkeygen.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../smallprimes.c ssh.o: ../ssh.c ../putty.h ../pageant.h ../tree234.h ../storage.h \ ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h \ @@ -740,11 +856,11 @@ ssh1login.o: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h ../sshbpp.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login.c ssh1login-server.o: ../ssh1login-server.c ../putty.h ../mpint.h ../ssh.h \ - ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ + ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login-server.c ssh2bpp.o: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -795,12 +911,13 @@ ssh2kex-client.o: ../ssh2kex-client.c ../putty.h ../ssh.h ../sshbpp.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-client.c ssh2kex-server.o: ../ssh2kex-server.c ../putty.h ../ssh.h ../sshbpp.h \ - ../sshppl.h ../sshcr.h ../sshserver.h ../storage.h \ - ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ - ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ - ../pgssapi.h ../windows/winhelp.h ../charset/charset.h + ../sshppl.h ../sshcr.h ../sshserver.h ../sshkeygen.h \ + ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ + ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-server.c ssh2transhk.o: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -838,6 +955,12 @@ sshaes.o: ../sshaes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c +sshargon2.o: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ + ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshargon2.c sshauxcrypt.o: ../sshauxcrypt.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h @@ -846,6 +969,9 @@ sshbcrypt.o: ../sshbcrypt.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbcrypt.c +sshblake2.o: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblake2.c sshblowf.o: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h @@ -878,17 +1004,17 @@ sshdss.o: ../sshdss.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c -sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../mpint.h ../defs.h \ - ../puttymem.h ../marshal.h ../tree234.h ../network.h \ - ../sshttymodes.h +sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h ../mpint.h \ + ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ + ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecc.c -sshecdsag.o: ../sshecdsag.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshecdsag.o: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecdsag.c sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ @@ -905,11 +1031,11 @@ sshmac.o: ../sshmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c -sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h ../sshkeygen.h \ + ../puttymem.h ../tree234.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c -sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint.h ../defs.h \ +sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ @@ -931,13 +1057,13 @@ sshrsa.o: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c -sshrsag.o: ../sshrsag.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshrsag.o: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c sshserver.o: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h ../sshppl.h \ - ../sshserver.h ../sshgssc.h ../sshgss.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshchan.h ../sshserver.h ../sshgssc.h ../sshgss.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h @@ -951,12 +1077,21 @@ sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c +sshsha3.o: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha3.c sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshshare.c +sshutils.o: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshutils.c sshverstring.o: ../sshverstring.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -971,6 +1106,11 @@ stripctrl.o: ../stripctrl.c ../putty.h ../terminal.h ../misc.h ../marshal.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../stripctrl.c +supdup.o: ../supdup.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../supdup.c telnet.o: ../telnet.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -981,9 +1121,9 @@ terminal.o: ../terminal.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c -testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../misc.h ../mpint.h ../ecc.h \ - ../testcrypt.h ../puttymem.h ../tree234.h ../network.h \ - ../sshttymodes.h ../marshal.h +testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h ../misc.h \ + ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ + ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testcrypt.c testsc.o: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h ../mpint.h \ ../ecc.h ../puttyps.h ../network.h ../marshal.h \ @@ -1007,7 +1147,8 @@ tree234.o: ../tree234.c ../defs.h ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c -utils.o: ../utils.c ../defs.h ../misc.h ../puttymem.h ../marshal.h +utils.o: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h ../marshal.h \ + ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../utils.c ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1032,8 +1173,13 @@ uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c -uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ +uxcliloop.o: ../unix/uxcliloop.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcliloop.c +uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../console.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h @@ -1086,10 +1232,11 @@ uxpgnt.o: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h ../pageant.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpgnt.c -uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h +uxplink.o: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h ../tree234.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c uxpoll.o: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1106,21 +1253,28 @@ uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c +uxpsusan.o: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpsusan.c uxpterm.o: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c -uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../tree234.h ../sshttymodes.h \ - ../sshsignals.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../puttymem.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c -uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../unix/gtkcompat.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ +uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h ../tree234.h \ + ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../puttymem.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c +uxputty.o: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ + ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1144,11 +1298,11 @@ uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../defs.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c -uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sftp.h \ - ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sshserver.h \ + ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftpserver.c uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../ssh.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ @@ -1158,6 +1312,12 @@ uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxshare.c uxsignal.o: ../unix/uxsignal.c ../defs.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c +uxsocks.o: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsocks.c uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -1168,13 +1328,17 @@ uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c -uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ +uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../unix/uxutils.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxutils.c -version.o: ../version.c ../empty.h ../version.h +version.o: ../version.c ../putty.h ../ssh.h ../empty.h ../version.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c wcwidth.o: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ @@ -1186,10 +1350,10 @@ wildcard.o: ../wildcard.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c -wincapi.o: ../windows/wincapi.c ../putty.h ../windows/wincapi.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ +wincapi.o: ../windows/wincapi.c ../putty.h ../ssh.h ../windows/wincapi.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincapi.c wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ @@ -1198,11 +1362,16 @@ wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c -wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +wincliloop.o: ../windows/wincliloop.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincliloop.c +wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ + ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ @@ -1215,17 +1384,18 @@ windefs.o: ../windows/windefs.c ../putty.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ - ../storage.h ../dialog.h ../licence.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winseat.h ../storage.h ../dialog.h ../licence.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ - ../windows/win_res.h ../windows/winsecur.h ../tree234.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../windows/win_res.h ../windows/winsecur.h \ + ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ ../sshgssc.h ../misc.h ../defs.h ../puttyps.h ../network.h \ @@ -1265,10 +1435,11 @@ winmiscs.o: ../windows/winmiscs.c ../putty.h ../defs.h ../puttyps.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmiscs.c -winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../defs.h \ - ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ - ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h +winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../ssh.h \ + ../defs.h ../puttyps.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c winnohlp.o: ../windows/winnohlp.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ @@ -1295,23 +1466,26 @@ winnps.o: ../windows/winnps.c ../tree234.h ../putty.h ../network.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnps.c -winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../licence.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ - ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h +winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../licence.h ../windows/winsecur.h ../windows/puttygen-rc.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ - ../windows/winsecur.h ../pageant.h ../licence.h ../defs.h \ - ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../pageant.h \ + ../licence.h ../windows/pageant-rc.h ../defs.h ../puttyps.h \ + ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c winpgntc.o: ../windows/winpgntc.c ../putty.h ../pageant.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ @@ -1335,6 +1509,16 @@ winsecur.o: ../windows/winsecur.c ../putty.h ../windows/winsecur.h ../defs.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsecur.c +winselcli.o: ../windows/winselcli.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselcli.c +winselgui.o: ../windows/winselgui.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselgui.c winser.o: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -1353,6 +1537,12 @@ winshare.o: ../windows/winshare.c ../tree234.h ../putty.h ../network.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winshare.c +winsocks.o: ../windows/winsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsocks.c winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -1407,6 +1597,6 @@ xpmputty.o: ../unix/xpmputty.c clean: - rm -f *.o cgtest fuzzterm osxlaunch plink pscp psftp puttygen testcrypt testsc testzlib uppity + rm -f *.o cgtest fuzzterm osxlaunch plink pscp psftp psocks psusan puttygen testcrypt testsc testzlib uppity FORCE: diff --git a/unix/gtkask.c b/unix/gtkask.c index 6105a6a..4e95ba1 100644 --- a/unix/gtkask.c +++ b/unix/gtkask.c @@ -146,6 +146,23 @@ static void add_text_to_passphrase(struct askpass_ctx *ctx, gchar *str) visually_acknowledge_keypress(ctx); } +static void cancel_askpass(struct askpass_ctx *ctx, const char *msg) +{ + smemclr(ctx->passphrase, ctx->passsize); + ctx->passphrase = NULL; + ctx->error_message = dupstr(msg); + gtk_main_quit(); +} + +static gboolean askpass_dialog_closed(GtkWidget *widget, GdkEvent *event, + gpointer data) +{ + struct askpass_ctx *ctx = (struct askpass_ctx *)data; + cancel_askpass(ctx, "passphrase input cancelled"); + /* Don't destroy dialog yet, so gtk_askpass_cleanup() can do its work */ + return true; +} + static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct askpass_ctx *ctx = (struct askpass_ctx *)data; @@ -155,10 +172,7 @@ static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) gtk_main_quit(); } else if (event->keyval == GDK_KEY_Escape && event->type == GDK_KEY_PRESS) { - smemclr(ctx->passphrase, ctx->passsize); - ctx->passphrase = NULL; - ctx->error_message = dupstr("passphrase input cancelled"); - gtk_main_quit(); + cancel_askpass(ctx, "passphrase input cancelled"); } else { #if GTK_CHECK_VERSION(2,0,0) if (gtk_im_context_filter_keypress(ctx->imc, event)) @@ -380,10 +394,7 @@ static gboolean try_grab_keyboard(gpointer vctx) * to give the user time to release that key. */ if (++ctx->nattempts >= 4) { - smemclr(ctx->passphrase, ctx->passsize); - ctx->passphrase = NULL; - ctx->error_message = dupstr("unable to grab keyboard after 5 seconds"); - gtk_main_quit(); + cancel_askpass(ctx, "unable to grab keyboard after 5 seconds"); } else { g_timeout_add(1000/8, try_grab_keyboard, ctx); } @@ -426,12 +437,21 @@ static const char *gtk_askpass_setup(struct askpass_ctx *ctx, ctx->dialog = our_dialog_new(); gtk_window_set_title(GTK_WINDOW(ctx->dialog), window_title); gtk_window_set_position(GTK_WINDOW(ctx->dialog), GTK_WIN_POS_CENTER); + g_signal_connect(G_OBJECT(ctx->dialog), "delete-event", + G_CALLBACK(askpass_dialog_closed), ctx); ctx->promptlabel = gtk_label_new(prompt_text); align_label_left(GTK_LABEL(ctx->promptlabel)); gtk_widget_show(ctx->promptlabel); gtk_label_set_line_wrap(GTK_LABEL(ctx->promptlabel), true); #if GTK_CHECK_VERSION(3,0,0) gtk_label_set_width_chars(GTK_LABEL(ctx->promptlabel), 48); +#endif + int margin = string_width("MM"); +#if GTK_CHECK_VERSION(3,12,0) + gtk_widget_set_margin_start(ctx->promptlabel, margin); + gtk_widget_set_margin_end(ctx->promptlabel, margin); +#else + gtk_misc_set_padding(GTK_MISC(ctx->promptlabel), margin, 0); #endif our_dialog_add_to_content_area(GTK_WINDOW(ctx->dialog), ctx->promptlabel, true, true, 0); @@ -569,7 +589,7 @@ const bool buildinfo_gtk_relevant = true; char *gtk_askpass_main(const char *display, const char *wintitle, const char *prompt, bool *success) { - struct askpass_ctx actx, *ctx = &actx; + struct askpass_ctx ctx[1]; const char *err; ctx->passphrase = NULL; diff --git a/unix/gtkcompat.h b/unix/gtkcompat.h index b34eda1..6ab5c80 100644 --- a/unix/gtkcompat.h +++ b/unix/gtkcompat.h @@ -34,7 +34,10 @@ #define g_object_get_data gtk_object_get_data #define g_object_set_data gtk_object_set_data #define g_object_set_data_full gtk_object_set_data_full -#define g_object_ref_sink gtk_object_sink +#define g_object_ref_sink(x) do { \ + gtk_object_ref(x); \ + gtk_object_sink(x); \ + } while (0) #define GDK_GRAB_SUCCESS GrabSuccess diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index cd3f809..8e7421d 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -27,14 +27,11 @@ #include "x11misc.h" #endif -#ifdef TESTMODE -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ -#endif - #include "storage.h" #include "dialog.h" #include "tree234.h" #include "licence.h" +#include "ssh.h" #if GTK_CHECK_VERSION(2,0,0) /* Decide which of GtkFileChooserDialog and GtkFileSelection to use */ @@ -978,14 +975,11 @@ void dlg_set_focus(union control *ctrl, dlgparam *dp) * Radio buttons: we find the currently selected button and * focus it. */ - { - int i; - for (i = 0; i < ctrl->radio.nbuttons; i++) - if (gtk_toggle_button_get_active - (GTK_TOGGLE_BUTTON(uc->buttons[i]))) { - gtk_widget_grab_focus(uc->buttons[i]); - } - } + for (int i = 0; i < ctrl->radio.nbuttons; i++) + if (gtk_toggle_button_get_active + (GTK_TOGGLE_BUTTON(uc->buttons[i]))) { + gtk_widget_grab_focus(uc->buttons[i]); + } break; case CTRL_LISTBOX: #if !GTK_CHECK_VERSION(2,4,0) @@ -1889,21 +1883,19 @@ GtkWidget *layout_ctrls( GtkWidget *w = NULL; switch (ctrl->generic.type) { - case CTRL_COLUMNS: - { - static const int simplecols[1] = { 100 }; - columns_set_cols(cols, ctrl->columns.ncols, - (ctrl->columns.percentages ? - ctrl->columns.percentages : simplecols)); - } + case CTRL_COLUMNS: { + static const int simplecols[1] = { 100 }; + columns_set_cols(cols, ctrl->columns.ncols, + (ctrl->columns.percentages ? + ctrl->columns.percentages : simplecols)); continue; /* no actual control created */ - case CTRL_TABDELAY: - { - struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl); - if (uc) - columns_taborder_last(cols, uc->toplevel); - } + } + case CTRL_TABDELAY: { + struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl); + if (uc) + columns_taborder_last(cols, uc->toplevel); continue; /* no actual control created */ + } } uc = snew(struct uctrl); @@ -1951,223 +1943,220 @@ GtkWidget *layout_ctrls( ctrl->checkbox.shortcut, SHORTCUT_UCTRL, uc); left = true; break; - case CTRL_RADIO: + case CTRL_RADIO: { /* * Radio buttons get to go inside their own Columns, no * matter what. */ - { - gint i, *percentages; - GSList *group; - - w = columns_new(0); - if (ctrl->generic.label) { - GtkWidget *label = gtk_label_new(ctrl->generic.label); - columns_add(COLUMNS(w), label, 0, 1); - columns_force_left_align(COLUMNS(w), label); - gtk_widget_show(label); - shortcut_add(scs, label, ctrl->radio.shortcut, - SHORTCUT_UCTRL, uc); - uc->label = label; - } - percentages = g_new(gint, ctrl->radio.ncolumns); - for (i = 0; i < ctrl->radio.ncolumns; i++) { - percentages[i] = - ((100 * (i+1) / ctrl->radio.ncolumns) - - 100 * i / ctrl->radio.ncolumns); - } - columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns, - percentages); - g_free(percentages); - group = NULL; - - uc->nbuttons = ctrl->radio.nbuttons; - uc->buttons = snewn(uc->nbuttons, GtkWidget *); - - for (i = 0; i < ctrl->radio.nbuttons; i++) { - GtkWidget *b; - gint colstart; - - b = (gtk_radio_button_new_with_label - (group, ctrl->radio.buttons[i])); - uc->buttons[i] = b; - group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(b)); - colstart = i % ctrl->radio.ncolumns; - columns_add(COLUMNS(w), b, colstart, - (i == ctrl->radio.nbuttons-1 ? - ctrl->radio.ncolumns - colstart : 1)); - columns_force_left_align(COLUMNS(w), b); - gtk_widget_show(b); - g_signal_connect(G_OBJECT(b), "toggled", - G_CALLBACK(button_toggled), dp); - g_signal_connect(G_OBJECT(b), "focus_in_event", - G_CALLBACK(widget_focus), dp); - if (ctrl->radio.shortcuts) { - shortcut_add(scs, gtk_bin_get_child(GTK_BIN(b)), - ctrl->radio.shortcuts[i], - SHORTCUT_UCTRL, uc); - } - } + gint i, *percentages; + GSList *group; + + w = columns_new(0); + if (ctrl->generic.label) { + GtkWidget *label = gtk_label_new(ctrl->generic.label); + columns_add(COLUMNS(w), label, 0, 1); + columns_force_left_align(COLUMNS(w), label); + gtk_widget_show(label); + shortcut_add(scs, label, ctrl->radio.shortcut, + SHORTCUT_UCTRL, uc); + uc->label = label; + } + percentages = g_new(gint, ctrl->radio.ncolumns); + for (i = 0; i < ctrl->radio.ncolumns; i++) { + percentages[i] = + ((100 * (i+1) / ctrl->radio.ncolumns) - + 100 * i / ctrl->radio.ncolumns); + } + columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns, + percentages); + g_free(percentages); + group = NULL; + + uc->nbuttons = ctrl->radio.nbuttons; + uc->buttons = snewn(uc->nbuttons, GtkWidget *); + + for (i = 0; i < ctrl->radio.nbuttons; i++) { + GtkWidget *b; + gint colstart; + + b = (gtk_radio_button_new_with_label + (group, ctrl->radio.buttons[i])); + uc->buttons[i] = b; + group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(b)); + colstart = i % ctrl->radio.ncolumns; + columns_add(COLUMNS(w), b, colstart, + (i == ctrl->radio.nbuttons-1 ? + ctrl->radio.ncolumns - colstart : 1)); + columns_force_left_align(COLUMNS(w), b); + gtk_widget_show(b); + g_signal_connect(G_OBJECT(b), "toggled", + G_CALLBACK(button_toggled), dp); + g_signal_connect(G_OBJECT(b), "focus_in_event", + G_CALLBACK(widget_focus), dp); + if (ctrl->radio.shortcuts) { + shortcut_add(scs, gtk_bin_get_child(GTK_BIN(b)), + ctrl->radio.shortcuts[i], + SHORTCUT_UCTRL, uc); + } } break; - case CTRL_EDITBOX: - { - GtkWidget *signalobject; + } + case CTRL_EDITBOX: { + GtkWidget *signalobject; - if (ctrl->editbox.has_list) { + if (ctrl->editbox.has_list) { #if !GTK_CHECK_VERSION(2,4,0) - /* - * GTK 1 combo box. - */ - w = gtk_combo_new(); - gtk_combo_set_value_in_list(GTK_COMBO(w), false, true); - uc->entry = GTK_COMBO(w)->entry; - uc->list = GTK_COMBO(w)->list; - signalobject = uc->entry; + /* + * GTK 1 combo box. + */ + w = gtk_combo_new(); + gtk_combo_set_value_in_list(GTK_COMBO(w), false, true); + uc->entry = GTK_COMBO(w)->entry; + uc->list = GTK_COMBO(w)->list; + signalobject = uc->entry; #else - /* - * GTK 2 combo box. - */ - uc->listmodel = gtk_list_store_new(2, G_TYPE_INT, - G_TYPE_STRING); - w = gtk_combo_box_new_with_model_and_entry - (GTK_TREE_MODEL(uc->listmodel)); - g_object_set(G_OBJECT(w), "entry-text-column", 1, - (const char *)NULL); - /* We cannot support password combo boxes. */ - assert(!ctrl->editbox.password); - uc->combo = w; - signalobject = uc->combo; + /* + * GTK 2 combo box. + */ + uc->listmodel = gtk_list_store_new(2, G_TYPE_INT, + G_TYPE_STRING); + w = gtk_combo_box_new_with_model_and_entry + (GTK_TREE_MODEL(uc->listmodel)); + g_object_set(G_OBJECT(w), "entry-text-column", 1, + (const char *)NULL); + /* We cannot support password combo boxes. */ + assert(!ctrl->editbox.password); + uc->combo = w; + signalobject = uc->combo; #endif - } else { - w = gtk_entry_new(); - if (ctrl->editbox.password) - gtk_entry_set_visibility(GTK_ENTRY(w), false); - uc->entry = w; - signalobject = w; - } - uc->entrysig = - g_signal_connect(G_OBJECT(signalobject), "changed", - G_CALLBACK(editbox_changed), dp); - g_signal_connect(G_OBJECT(signalobject), "key_press_event", - G_CALLBACK(editbox_key), dp); - g_signal_connect(G_OBJECT(signalobject), "focus_in_event", - G_CALLBACK(widget_focus), dp); - g_signal_connect(G_OBJECT(signalobject), "focus_out_event", - G_CALLBACK(editbox_lostfocus), dp); - g_signal_connect(G_OBJECT(signalobject), "focus_out_event", - G_CALLBACK(editbox_lostfocus), dp); + } else { + w = gtk_entry_new(); + if (ctrl->editbox.password) + gtk_entry_set_visibility(GTK_ENTRY(w), false); + uc->entry = w; + signalobject = w; + } + uc->entrysig = + g_signal_connect(G_OBJECT(signalobject), "changed", + G_CALLBACK(editbox_changed), dp); + g_signal_connect(G_OBJECT(signalobject), "key_press_event", + G_CALLBACK(editbox_key), dp); + g_signal_connect(G_OBJECT(signalobject), "focus_in_event", + G_CALLBACK(widget_focus), dp); + g_signal_connect(G_OBJECT(signalobject), "focus_out_event", + G_CALLBACK(editbox_lostfocus), dp); + g_signal_connect(G_OBJECT(signalobject), "focus_out_event", + G_CALLBACK(editbox_lostfocus), dp); #if !GTK_CHECK_VERSION(3,0,0) - /* - * Edit boxes, for some strange reason, have a minimum - * width of 150 in GTK 1.2. We don't want this - we'd - * rather the edit boxes acquired their natural width - * from the column layout of the rest of the box. - */ - { - GtkRequisition req; - gtk_widget_size_request(w, &req); - gtk_widget_set_size_request(w, 10, req.height); - } + /* + * Edit boxes, for some strange reason, have a minimum + * width of 150 in GTK 1.2. We don't want this - we'd + * rather the edit boxes acquired their natural width + * from the column layout of the rest of the box. + */ + { + GtkRequisition req; + gtk_widget_size_request(w, &req); + gtk_widget_set_size_request(w, 10, req.height); + } #else - /* - * In GTK 3, this is still true, but there's a special - * method for GtkEntry in particular to fix it. - */ - if (GTK_IS_ENTRY(w)) - gtk_entry_set_width_chars(GTK_ENTRY(w), 1); -#endif - - if (ctrl->generic.label) { - GtkWidget *label, *container; - - label = gtk_label_new(ctrl->generic.label); - - shortcut_add(scs, label, ctrl->editbox.shortcut, - SHORTCUT_FOCUS, uc->entry); - - container = columns_new(4); - if (ctrl->editbox.percentwidth == 100) { - columns_add(COLUMNS(container), label, 0, 1); - columns_force_left_align(COLUMNS(container), label); - columns_add(COLUMNS(container), w, 0, 1); - } else { - gint percentages[2]; - percentages[1] = ctrl->editbox.percentwidth; - percentages[0] = 100 - ctrl->editbox.percentwidth; - columns_set_cols(COLUMNS(container), 2, percentages); - columns_add(COLUMNS(container), label, 0, 1); - columns_force_left_align(COLUMNS(container), label); - columns_add(COLUMNS(container), w, 1, 1); - columns_force_same_height(COLUMNS(container), - label, w); - } - gtk_widget_show(label); - gtk_widget_show(w); + /* + * In GTK 3, this is still true, but there's a special + * method for GtkEntry in particular to fix it. + */ + if (GTK_IS_ENTRY(w)) + gtk_entry_set_width_chars(GTK_ENTRY(w), 1); +#endif - w = container; - uc->label = label; - } + if (ctrl->generic.label) { + GtkWidget *label, *container; + + label = gtk_label_new(ctrl->generic.label); + + shortcut_add(scs, label, ctrl->editbox.shortcut, + SHORTCUT_FOCUS, uc->entry); + + container = columns_new(4); + if (ctrl->editbox.percentwidth == 100) { + columns_add(COLUMNS(container), label, 0, 1); + columns_force_left_align(COLUMNS(container), label); + columns_add(COLUMNS(container), w, 0, 1); + } else { + gint percentages[2]; + percentages[1] = ctrl->editbox.percentwidth; + percentages[0] = 100 - ctrl->editbox.percentwidth; + columns_set_cols(COLUMNS(container), 2, percentages); + columns_add(COLUMNS(container), label, 0, 1); + columns_force_left_align(COLUMNS(container), label); + columns_add(COLUMNS(container), w, 1, 1); + columns_force_same_height(COLUMNS(container), + label, w); + } + gtk_widget_show(label); + gtk_widget_show(w); + + w = container; + uc->label = label; } break; + } case CTRL_FILESELECT: - case CTRL_FONTSELECT: - { - GtkWidget *ww; - const char *browsebtn = - (ctrl->generic.type == CTRL_FILESELECT ? - "Browse..." : "Change..."); - - gint percentages[] = { 75, 25 }; - w = columns_new(4); - columns_set_cols(COLUMNS(w), 2, percentages); - - if (ctrl->generic.label) { - ww = gtk_label_new(ctrl->generic.label); - columns_add(COLUMNS(w), ww, 0, 2); - columns_force_left_align(COLUMNS(w), ww); - gtk_widget_show(ww); - shortcut_add(scs, ww, - (ctrl->generic.type == CTRL_FILESELECT ? - ctrl->fileselect.shortcut : - ctrl->fontselect.shortcut), - SHORTCUT_UCTRL, uc); - uc->label = ww; - } + case CTRL_FONTSELECT: { + GtkWidget *ww; + const char *browsebtn = + (ctrl->generic.type == CTRL_FILESELECT ? + "Browse..." : "Change..."); + + gint percentages[] = { 75, 25 }; + w = columns_new(4); + columns_set_cols(COLUMNS(w), 2, percentages); - uc->entry = ww = gtk_entry_new(); + if (ctrl->generic.label) { + ww = gtk_label_new(ctrl->generic.label); + columns_add(COLUMNS(w), ww, 0, 2); + columns_force_left_align(COLUMNS(w), ww); + gtk_widget_show(ww); + shortcut_add(scs, ww, + (ctrl->generic.type == CTRL_FILESELECT ? + ctrl->fileselect.shortcut : + ctrl->fontselect.shortcut), + SHORTCUT_UCTRL, uc); + uc->label = ww; + } + + uc->entry = ww = gtk_entry_new(); #if !GTK_CHECK_VERSION(3,0,0) - { - GtkRequisition req; - gtk_widget_size_request(ww, &req); - gtk_widget_set_size_request(ww, 10, req.height); - } + { + GtkRequisition req; + gtk_widget_size_request(ww, &req); + gtk_widget_set_size_request(ww, 10, req.height); + } #else - gtk_entry_set_width_chars(GTK_ENTRY(ww), 1); + gtk_entry_set_width_chars(GTK_ENTRY(ww), 1); #endif - columns_add(COLUMNS(w), ww, 0, 1); - gtk_widget_show(ww); + columns_add(COLUMNS(w), ww, 0, 1); + gtk_widget_show(ww); - uc->button = ww = gtk_button_new_with_label(browsebtn); - columns_add(COLUMNS(w), ww, 1, 1); - gtk_widget_show(ww); + uc->button = ww = gtk_button_new_with_label(browsebtn); + columns_add(COLUMNS(w), ww, 1, 1); + gtk_widget_show(ww); - columns_force_same_height(COLUMNS(w), uc->entry, uc->button); + columns_force_same_height(COLUMNS(w), uc->entry, uc->button); - g_signal_connect(G_OBJECT(uc->entry), "key_press_event", - G_CALLBACK(editbox_key), dp); - uc->entrysig = - g_signal_connect(G_OBJECT(uc->entry), "changed", - G_CALLBACK(editbox_changed), dp); - g_signal_connect(G_OBJECT(uc->entry), "focus_in_event", - G_CALLBACK(widget_focus), dp); - g_signal_connect(G_OBJECT(uc->button), "focus_in_event", - G_CALLBACK(widget_focus), dp); - g_signal_connect(G_OBJECT(ww), "clicked", - G_CALLBACK(filefont_clicked), dp); - } + g_signal_connect(G_OBJECT(uc->entry), "key_press_event", + G_CALLBACK(editbox_key), dp); + uc->entrysig = + g_signal_connect(G_OBJECT(uc->entry), "changed", + G_CALLBACK(editbox_changed), dp); + g_signal_connect(G_OBJECT(uc->entry), "focus_in_event", + G_CALLBACK(widget_focus), dp); + g_signal_connect(G_OBJECT(uc->button), "focus_in_event", + G_CALLBACK(widget_focus), dp); + g_signal_connect(G_OBJECT(ww), "clicked", + G_CALLBACK(filefont_clicked), dp); break; + } case CTRL_LISTBOX: #if GTK_CHECK_VERSION(2,0,0) @@ -2502,6 +2491,28 @@ GtkWidget *layout_ctrls( COLUMN_SPAN(ctrl->generic.column)); if (left) columns_force_left_align(cols, w); + if (ctrl->generic.align_next_to) { + /* + * Implement align_next_to by simply forcing the two + * controls to have the same height of size allocation. At + * least for the controls we're currently doing this with, + * the GTK layout system will automatically vertically + * centre each control within its allocation, which will + * get the two controls aligned alongside each other + * reasonably well. + */ + struct uctrl *uc2 = dlg_find_byctrl( + dp, ctrl->generic.align_next_to); + assert(uc2); + columns_force_same_height(cols, w, uc2->toplevel); + +#if GTK_CHECK_VERSION(3, 10, 0) + /* Slightly nicer to align baselines than just vertically + * centring, where the option is available */ + gtk_widget_set_valign(w, GTK_ALIGN_BASELINE); + gtk_widget_set_valign(uc2->toplevel, GTK_ALIGN_BASELINE); +#endif + } gtk_widget_show(w); uc->toplevel = w; @@ -3190,10 +3201,10 @@ GtkWidget *create_config_box(const char *title, Conf *conf, dp->ntreeitems = nselparams; dp->treeitems = snewn(dp->ntreeitems, GtkWidget *); for (index = 0; index < nselparams; index++) { - g_signal_connect(G_OBJECT(selparams[index].treeitem), "select", + g_signal_connect(G_OBJECT(selparams[index]->treeitem), "select", G_CALLBACK(treeitem_sel), - &selparams[index]); - dp->treeitems[index] = selparams[index].treeitem; + selparams[index]); + dp->treeitems[index] = selparams[index]->treeitem; } #endif @@ -3279,24 +3290,25 @@ static void messagebox_handler(union control *ctrl, dlgparam *dp, dlg_end(dp, ctrl->generic.context.i); } -const struct message_box_button button_array_yn[] = { +static const struct message_box_button button_array_yn[] = { {"Yes", 'y', +1, 1}, {"No", 'n', -1, 0}, }; const struct message_box_buttons buttons_yn = { button_array_yn, lenof(button_array_yn), }; -const struct message_box_button button_array_ok[] = { +static const struct message_box_button button_array_ok[] = { {"OK", 'o', 1, 1}, }; const struct message_box_buttons buttons_ok = { button_array_ok, lenof(button_array_ok), }; -GtkWidget *create_message_box( +static GtkWidget *create_message_box_general( GtkWidget *parentwin, const char *title, const char *msg, int minwid, bool selectable, const struct message_box_buttons *buttons, - post_dialog_fn_t after, void *afterctx) + post_dialog_fn_t after, void *afterctx, + GtkWidget *(*action_postproc)(GtkWidget *, void *), void *postproc_ctx) { GtkWidget *window, *w0, *w1; struct controlset *s0, *s1; @@ -3366,6 +3378,8 @@ GtkWidget *create_message_box( window = our_dialog_new(); gtk_window_set_title(GTK_WINDOW(window), title); w0 = layout_ctrls(dp, NULL, &scs, s0, GTK_WINDOW(window)); + if (action_postproc) + w0 = action_postproc(w0, postproc_ctx); our_dialog_set_action_area(GTK_WINDOW(window), w0); gtk_widget_show(w0); w1 = layout_ctrls(dp, NULL, &scs, s1, GTK_WINDOW(window)); @@ -3424,20 +3438,34 @@ GtkWidget *create_message_box( return window; } -struct verify_ssh_host_key_result_ctx { +GtkWidget *create_message_box( + GtkWidget *parentwin, const char *title, const char *msg, int minwid, + bool selectable, const struct message_box_buttons *buttons, + post_dialog_fn_t after, void *afterctx) +{ + return create_message_box_general( + parentwin, title, msg, minwid, selectable, buttons, after, afterctx, + NULL /* action_postproc */, NULL /* postproc_ctx */); +} + +struct verify_ssh_host_key_dialog_ctx { char *host; int port; char *keytype; char *keystr; + char *more_info; void (*callback)(void *callback_ctx, int result); void *callback_ctx; Seat *seat; + + GtkWidget *main_dialog; + GtkWidget *more_info_dialog; }; static void verify_ssh_host_key_result_callback(void *vctx, int result) { - struct verify_ssh_host_key_result_ctx *ctx = - (struct verify_ssh_host_key_result_ctx *)vctx; + struct verify_ssh_host_key_dialog_ctx *ctx = + (struct verify_ssh_host_key_dialog_ctx *)vctx; if (result >= 0) { int logical_result; @@ -3466,15 +3494,54 @@ static void verify_ssh_host_key_result_callback(void *vctx, int result) */ unregister_dialog(ctx->seat, DIALOG_SLOT_NETWORK_PROMPT); + if (ctx->more_info_dialog) + gtk_widget_destroy(ctx->more_info_dialog); + sfree(ctx->host); sfree(ctx->keytype); sfree(ctx->keystr); + sfree(ctx->more_info); sfree(ctx); } +static GtkWidget *add_more_info_button(GtkWidget *w, void *vctx) +{ + GtkWidget *box = gtk_hbox_new(false, 10); + gtk_widget_show(box); + gtk_box_pack_end(GTK_BOX(box), w, false, true, 0); + GtkWidget *button = gtk_button_new_with_label("More info..."); + gtk_widget_show(button); + gtk_box_pack_start(GTK_BOX(box), button, false, true, 0); + *(GtkWidget **)vctx = button; + return box; +} + +static void more_info_closed(void *vctx, int result) +{ + struct verify_ssh_host_key_dialog_ctx *ctx = + (struct verify_ssh_host_key_dialog_ctx *)vctx; + + ctx->more_info_dialog = NULL; +} + +static void more_info_button_clicked(GtkButton *button, gpointer vctx) +{ + struct verify_ssh_host_key_dialog_ctx *ctx = + (struct verify_ssh_host_key_dialog_ctx *)vctx; + + if (ctx->more_info_dialog) + return; + + ctx->more_info_dialog = create_message_box( + ctx->main_dialog, "Host key information", ctx->more_info, + string_width("SHA256 fingerprint: ecdsa-sha2-nistp521 521 " + "abcdefghkmnopqrsuvwxyzABCDEFGHJKLMNOPQRSTUW"), true, + &buttons_ok, more_info_closed, ctx); +} + int gtk_seat_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { static const char absenttxt[] = @@ -3514,7 +3581,7 @@ int gtk_seat_verify_ssh_host_key( char *text; int ret; - struct verify_ssh_host_key_result_ctx *result_ctx; + struct verify_ssh_host_key_dialog_ctx *result_ctx; GtkWidget *mainwin, *msgbox; /* @@ -3525,9 +3592,13 @@ int gtk_seat_verify_ssh_host_key( if (ret == 0) /* success - key matched OK */ return 1; - text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprint); + FingerprintType fptype_default = + ssh2_pick_default_fingerprint(fingerprints); + + text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, + fingerprints[fptype_default]); - result_ctx = snew(struct verify_ssh_host_key_result_ctx); + result_ctx = snew(struct verify_ssh_host_key_dialog_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->host = dupstr(host); @@ -3537,9 +3608,40 @@ int gtk_seat_verify_ssh_host_key( result_ctx->seat = seat; mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); - msgbox = create_message_box( - mainwin, "PuTTY Security Alert", text, string_width(fingerprint), true, - &buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx); + GtkWidget *more_info_button = NULL; + msgbox = create_message_box_general( + mainwin, "PuTTY Security Alert", text, + string_width(fingerprints[fptype_default]), true, + &buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx, + add_more_info_button, &more_info_button); + + result_ctx->main_dialog = msgbox; + result_ctx->more_info_dialog = NULL; + + strbuf *sb = strbuf_new(); + if (fingerprints[SSH_FPTYPE_SHA256]) + strbuf_catf(sb, "SHA256 fingerprint: %s\n", + fingerprints[SSH_FPTYPE_SHA256]); + if (fingerprints[SSH_FPTYPE_MD5]) + strbuf_catf(sb, "MD5 fingerprint: %s\n", + fingerprints[SSH_FPTYPE_MD5]); + strbuf_catf(sb, "Full text of host's public key:"); + /* We have to manually wrap the public key, or else the GtkLabel + * will resize itself to accommodate the longest word, which will + * lead to a hilariously wide message box. */ + for (const char *p = keydisp, *q = p + strlen(p); p < q ;) { + size_t linelen = q-p; + if (linelen > 72) + linelen = 72; + put_byte(sb, '\n'); + put_data(sb, p, linelen); + p += linelen; + } + result_ctx->more_info = strbuf_to_str(sb); + + g_signal_connect(G_OBJECT(more_info_button), "clicked", + G_CALLBACK(more_info_button_clicked), result_ctx); + register_dialog(seat, DIALOG_SLOT_NETWORK_PROMPT, msgbox); sfree(text); @@ -3654,7 +3756,7 @@ void old_keyfile_warning(void) void nonfatal_message_box(void *window, const char *msg) { - char *title = dupcat(appname, " Error", NULL); + char *title = dupcat(appname, " Error"); create_message_box( window, title, msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), @@ -3675,6 +3777,11 @@ void nonfatal(const char *p, ...) static GtkWidget *aboutbox = NULL; +static void about_window_destroyed(GtkWidget *widget, gpointer data) +{ + aboutbox = NULL; +} + static void about_close_clicked(GtkButton *button, gpointer data) { gtk_widget_destroy(aboutbox); @@ -3694,7 +3801,7 @@ static void licence_clicked(GtkButton *button, gpointer data) { char *title; - title = dupcat(appname, " Licence", NULL); + title = dupcat(appname, " Licence"); assert(aboutbox != NULL); create_message_box(aboutbox, title, LICENCE_TEXT("\n\n"), string_width("LONGISH LINE OF TEXT SO THE LICENCE" @@ -3716,10 +3823,13 @@ void about_box(void *window) aboutbox = our_dialog_new(); gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10); - title = dupcat("About ", appname, NULL); + title = dupcat("About ", appname); gtk_window_set_title(GTK_WINDOW(aboutbox), title); sfree(title); + g_signal_connect(G_OBJECT(aboutbox), "destroy", + G_CALLBACK(about_window_destroyed), NULL); + w = gtk_button_new_with_label("Close"); gtk_widget_set_can_default(w, true); gtk_window_set_default(GTK_WINDOW(aboutbox), w); @@ -3835,7 +3945,7 @@ static void eventlog_list_handler(union control *ctrl, dlgparam *dp, /* * Construct the data to use as the selection. */ - es->seldata->len = 0; + strbuf_clear(es->seldata); for (i = 0; i < es->ninitial; i++) { if (dlg_listbox_issel(ctrl, dp, i)) strbuf_catf(es->seldata, "%s\n", es->events_initial[i]); @@ -3932,7 +4042,7 @@ void showeventlog(eventlog_stuff *es, void *parentwin) c->listbox.percentages[2] = 65; es->window = window = our_dialog_new(); - title = dupcat(appname, " Event Log", (const char *)NULL); + title = dupcat(appname, " Event Log"); gtk_window_set_title(GTK_WINDOW(window), title); sfree(title); w0 = layout_ctrls(&es->dp, NULL, &es->scs, s0, GTK_WINDOW(window)); @@ -4025,7 +4135,7 @@ void logevent_dlg(eventlog_stuff *es, const char *string) strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm); sfree(*location); - *location = dupcat(timebuf, string, NULL); + *location = dupcat(timebuf, string); if (es->window) { dlg_listbox_add(es->listctrl, &es->dp, *location); } diff --git a/unix/gtkfont.c b/unix/gtkfont.c index db3dc58..b910d14 100644 --- a/unix/gtkfont.c +++ b/unix/gtkfont.c @@ -216,18 +216,18 @@ struct x11font { unifont u; }; -static const struct UnifontVtable x11font_vtable = { - x11font_create, - NULL, /* no fallback fonts in X11 */ - x11font_destroy, - x11font_has_glyph, - x11font_draw_text, - x11font_draw_combining, - x11font_enum_fonts, - x11font_canonify_fontname, - x11font_scale_fontname, - x11font_size_increment, - "server", +static const UnifontVtable x11font_vtable = { + .create = x11font_create, + .create_fallback = NULL, /* no fallback fonts in X11 */ + .destroy = x11font_destroy, + .has_glyph = x11font_has_glyph, + .draw_text = x11font_draw_text, + .draw_combining = x11font_draw_combining, + .enum_fonts = x11font_enum_fonts, + .canonify_fontname = x11font_canonify_fontname, + .scale_fontname = x11font_scale_fontname, + .size_increment = x11font_size_increment, + .prefix = "server", }; #define XLFD_STRING_PARTS_LIST(S,I) \ @@ -461,7 +461,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, reg = XGetAtomName(disp, (Atom)registry_ret); enc = XGetAtomName(disp, (Atom)encoding_ret); if (reg && enc) { - char *encoding = dupcat(reg, "-", enc, NULL); + char *encoding = dupcat(reg, "-", enc); pubcs = realcs = charset_from_xenc(encoding); /* @@ -511,6 +511,7 @@ static unifont *x11font_create(GtkWidget *widget, const char *name, xfont->u.height = xfont->u.ascent + xfont->u.descent; xfont->u.public_charset = pubcs; xfont->u.want_fallback = true; + xfont->u.strikethrough_y = xfont->u.ascent - (xfont->u.ascent * 3 / 8); #ifdef DRAW_TEXT_GDK xfont->u.preferred_drawtype = DRAWTYPE_GDK; #elif defined DRAW_TEXT_CAIRO @@ -1107,7 +1108,7 @@ static void x11font_enum_fonts(GtkWidget *widget, * font, which we do by examining the spacing field * again. */ - flags = FONTFLAG_SERVERSIDE; + int flags = FONTFLAG_SERVERSIDE; if (!strchr("CcMm", xlfd->spacing[0])) flags |= FONTFLAG_NONMONOSPACED; @@ -1280,8 +1281,7 @@ static char *x11font_size_increment(unifont *font, int increment) if (xlfd_best) { char *bare_returned_name = xlfd_recompose(xlfd_best); returned_name = dupcat( - xfont->u.vt->prefix, ":", bare_returned_name, - (const char *)NULL); + xfont->u.vt->prefix, ":", bare_returned_name); sfree(bare_returned_name); } @@ -1354,18 +1354,18 @@ struct pangofont { struct unifont u; }; -static const struct UnifontVtable pangofont_vtable = { - pangofont_create, - pangofont_create_fallback, - pangofont_destroy, - pangofont_has_glyph, - pangofont_draw_text, - pangofont_draw_combining, - pangofont_enum_fonts, - pangofont_canonify_fontname, - pangofont_scale_fontname, - pangofont_size_increment, - "client", +static const UnifontVtable pangofont_vtable = { + .create = pangofont_create, + .create_fallback = pangofont_create_fallback, + .destroy = pangofont_destroy, + .has_glyph = pangofont_has_glyph, + .draw_text = pangofont_draw_text, + .draw_combining = pangofont_draw_combining, + .enum_fonts = pangofont_enum_fonts, + .canonify_fontname = pangofont_canonify_fontname, + .scale_fontname = pangofont_scale_fontname, + .size_increment = pangofont_size_increment, + .prefix = "client", }; /* @@ -1459,9 +1459,14 @@ static unifont *pangofont_create_internal(GtkWidget *widget, pfont->u.vt = &pangofont_vtable; pfont->u.width = PANGO_PIXELS(pango_font_metrics_get_approximate_digit_width(metrics)); - pfont->u.ascent = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics)); - pfont->u.descent = PANGO_PIXELS(pango_font_metrics_get_descent(metrics)); + pfont->u.ascent = + PANGO_PIXELS_CEIL(pango_font_metrics_get_ascent(metrics)); + pfont->u.descent = + PANGO_PIXELS_CEIL(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; + pfont->u.strikethrough_y = + PANGO_PIXELS(pango_font_metrics_get_ascent(metrics) - + pango_font_metrics_get_strikethrough_position(metrics)); pfont->u.want_fallback = false; #ifdef DRAW_TEXT_CAIRO pfont->u.preferred_drawtype = DRAWTYPE_CAIRO; @@ -2042,8 +2047,7 @@ static char *pangofont_size_increment(unifont *font, int increment) } else { pango_font_description_set_size(desc, size); newname = pango_font_description_to_string(desc); - retname = dupcat(pfont->u.vt->prefix, ":", - newname, (const char *)NULL); + retname = dupcat(pfont->u.vt->prefix, ":", newname); g_free(newname); } @@ -2192,18 +2196,18 @@ struct multifont { struct unifont u; }; -static const struct UnifontVtable multifont_vtable = { - NULL, /* creation is done specially */ - NULL, - multifont_destroy, - NULL, - multifont_draw_text, - multifont_draw_combining, - NULL, - NULL, - NULL, - multifont_size_increment, - "client", +static const UnifontVtable multifont_vtable = { + .create = NULL, /* creation is done specially */ + .create_fallback = NULL, + .destroy = multifont_destroy, + .has_glyph = NULL, + .draw_text = multifont_draw_text, + .draw_combining = multifont_draw_combining, + .enum_fonts = NULL, + .canonify_fontname = NULL, + .scale_fontname = NULL, + .size_increment = multifont_size_increment, + .prefix = "client", }; unifont *multifont_create(GtkWidget *widget, const char *name, @@ -2242,6 +2246,7 @@ unifont *multifont_create(GtkWidget *widget, const char *name, mfont->u.ascent = font->ascent; mfont->u.descent = font->descent; mfont->u.height = font->height; + mfont->u.strikethrough_y = font->strikethrough_y; mfont->u.public_charset = font->public_charset; mfont->u.want_fallback = false; /* shouldn't be needed, but just in case */ mfont->u.preferred_drawtype = font->preferred_drawtype; @@ -2622,6 +2627,10 @@ static void unifontsel_setup_sizelist(unifontsel_internal *fs, */ for (i = start; i < end; i++) { info = (fontinfo *)index234(fs->fonts_by_selorder, i); + if (!info) { + /* _shouldn't_ happen unless font list is completely funted */ + break; + } if (info->flags &~ fs->filter_flags) { info->sizeindex = -1; continue; /* we're filtering out this font */ @@ -3130,9 +3139,11 @@ static void family_changed(GtkTreeSelection *treeselection, gpointer data) gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1); info = (fontinfo *)index234(fs->fonts_by_selorder, minval); - info = update_for_intended_size(fs, info); if (!info) return; /* _shouldn't_ happen unless font list is completely funted */ + info = update_for_intended_size(fs, info); + if (!info) + return; /* similarly shouldn't happen */ if (!info->size) fs->selsize = fs->intendedsize; /* font is scalable */ unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize, @@ -3157,9 +3168,11 @@ static void style_changed(GtkTreeSelection *treeselection, gpointer data) if (minval < 0) return; /* somehow a charset heading got clicked */ info = (fontinfo *)index234(fs->fonts_by_selorder, minval); - info = update_for_intended_size(fs, info); if (!info) return; /* _shouldn't_ happen unless font list is completely funted */ + info = update_for_intended_size(fs, info); + if (!info) + return; /* similarly shouldn't happen */ if (!info->size) fs->selsize = fs->intendedsize; /* font is scalable */ unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize, @@ -3182,6 +3195,8 @@ static void size_changed(GtkTreeSelection *treeselection, gpointer data) gtk_tree_model_get(treemodel, &treeiter, 1, &minval, 2, &size, -1); info = (fontinfo *)index234(fs->fonts_by_selorder, minval); + if (!info) + return; /* _shouldn't_ happen unless font list is completely funted */ unifontsel_select_font(fs, info, info->size ? info->size : size, 3, true); } @@ -3780,15 +3795,14 @@ char *unifontsel_get_name(unifontsel *fontsel) name = fs->selected->fontclass->scale_fontname (GTK_WIDGET(fs->u.window), fs->selected->realname, fs->selsize); if (name) { - char *ret = dupcat(fs->selected->fontclass->prefix, ":", - name, NULL); + char *ret = dupcat(fs->selected->fontclass->prefix, ":", name); sfree(name); return ret; } } return dupcat(fs->selected->fontclass->prefix, ":", - fs->selected->realname, NULL); + fs->selected->realname); } #endif /* GTK_CHECK_VERSION(2,0,0) */ diff --git a/unix/gtkfont.h b/unix/gtkfont.h index 5b70a3f..e43a748 100644 --- a/unix/gtkfont.h +++ b/unix/gtkfont.h @@ -49,7 +49,8 @@ /* * Exports from gtkfont.c. */ -struct UnifontVtable; /* contents internal to gtkfont.c */ +typedef struct UnifontVtable UnifontVtable; /* contents internal to + * gtkfont.c */ typedef struct unifont { const struct UnifontVtable *vt; /* @@ -66,7 +67,7 @@ typedef struct unifont { /* * Font dimensions needed by clients. */ - int width, height, ascent, descent; + int width, height, ascent, descent, strikethrough_y; /* * Indicates whether this font is capable of handling all glyphs diff --git a/unix/gtkmain.c b/unix/gtkmain.c index 520cd6e..ec8f7da 100644 --- a/unix/gtkmain.c +++ b/unix/gtkmain.c @@ -315,22 +315,25 @@ bool do_cmdline(int argc, char **argv, bool do_everything, Conf *conf) char *val; /* - * Macros to make argument handling easier. Note that because - * they need to call `continue', they cannot be contained in - * the usual do {...} while (0) wrapper to make them - * syntactically single statements; hence it is not legal to - * use one of these macros as an unbraced statement between - * `if' and `else'. + * Macros to make argument handling easier. + * + * Note that because they need to call `continue', they cannot be + * contained in the usual do {...} while (0) wrapper to make them + * syntactically single statements. I use the alternative if (1) + * {...} else ((void)0). */ -#define EXPECTS_ARG { \ - if (--argc <= 0) { \ - err = true; \ - fprintf(stderr, "%s: %s expects an argument\n", appname, p); \ - continue; \ - } else \ - val = *++argv; \ -} -#define SECOND_PASS_ONLY do { if (!do_everything) continue; } while (0) +#define EXPECTS_ARG if (1) { \ + if (--argc <= 0) { \ + err = true; \ + fprintf(stderr, "%s: %s expects an argument\n", appname, p); \ + continue; \ + } else \ + val = *++argv; \ + } else ((void)0) +#define SECOND_PASS_ONLY if (1) { \ + if (!do_everything) \ + continue; \ + } else ((void)0) while (--argc > 0) { const char *p = *++argv; @@ -566,7 +569,7 @@ static void post_initial_config_box(void *vctx, int result) if (result > 0) { new_session_window(ctx.conf, ctx.geometry_string); - } else if (result == 0) { + } else { /* In this main(), which only runs one session in total, a * negative result from the initial config box means we simply * terminate. */ diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 7261658..61e7718 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -5,8 +5,6 @@ #define _GNU_SOURCE -#include - #include #include #include @@ -29,8 +27,6 @@ #include #endif -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ - #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" @@ -48,16 +44,8 @@ #include "x11misc.h" -/* far2l base64 */ -#include <../windows/cencode.h> -#include <../windows/cdecode.h> - -/* Colours come in two flavours: configurable, and xterm-extended. */ -#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */ -#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) - GdkAtom compound_text_atom, utf8_string_atom; -GdkAtom clipboard_atom +static GdkAtom clipboard_atom #if GTK_CHECK_VERSION(2,0,0) /* GTK1 will have to fill this in at startup */ = GDK_SELECTION_CLIPBOARD #endif @@ -137,6 +125,7 @@ struct GtkFrontend { */ cairo_surface_t *surface; #endif + int backing_w, backing_h; #if GTK_CHECK_VERSION(2,0,0) GtkIMContext *imc; #endif @@ -144,7 +133,7 @@ struct GtkFrontend { int xpos, ypos, gravity; bool gotpos; GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; - GdkColor cols[NALLCOLOURS]; + GdkColor cols[OSC4_NCOLOURS]; /* indexed by xterm colour indices */ #if !GTK_CHECK_VERSION(3,0,0) GdkColormap *colmap; #endif @@ -190,6 +179,7 @@ struct GtkFrontend { int system_mod_mask; #endif bool send_raw_mouse; + bool pointer_indicates_raw_mouse; unifont_drawctx uctx; #if GTK_CHECK_VERSION(2,0,0) GdkPixbuf *trust_sigil_pb; @@ -241,7 +231,7 @@ static void post_fatal_message_box(void *vctx, int result) static void common_connfatal_message_box( GtkFrontend *inst, const char *msg, post_dialog_fn_t postfn) { - char *title = dupcat(appname, " Fatal Error", NULL); + char *title = dupcat(appname, " Fatal Error"); GtkWidget *dialog = create_message_box( inst->window, title, msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), @@ -355,13 +345,27 @@ static int gtk_seat_get_userpass_input(Seat *seat, prompts_t *p, static bool gtk_seat_is_utf8(Seat *seat) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); - return win_is_utf8(&inst->termwin); + return inst->ucsdata.line_codepage == CS_UTF8; +} + +static void get_window_pixel_size(GtkFrontend *inst, int *w, int *h) +{ + /* + * I assume that when the GTK version of this call is available + * we should use it. Not sure how it differs from the GDK one, + * though. + */ +#if GTK_CHECK_VERSION(2,0,0) + gtk_window_get_size(GTK_WINDOW(inst->window), w, h); +#else + gdk_window_get_size(gtk_widget_get_window(inst->window), w, h); +#endif } static bool gtk_seat_get_window_pixel_size(Seat *seat, int *w, int *h) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); - win_get_pixels(&inst->termwin, w, h); + get_window_pixel_size(inst, w, h); return true; } @@ -380,30 +384,34 @@ static const char *gtk_seat_get_x_display(Seat *seat); static bool gtk_seat_get_windowid(Seat *seat, long *id); #endif static bool gtk_seat_set_trust_status(Seat *seat, bool trusted); +static bool gtk_seat_get_cursor_position(Seat *seat, int *x, int *y); static const SeatVtable gtk_seat_vt = { - gtk_seat_output, - gtk_seat_eof, - gtk_seat_get_userpass_input, - gtk_seat_notify_remote_exit, - gtk_seat_connection_fatal, - gtk_seat_update_specials_menu, - gtk_seat_get_ttymode, - gtk_seat_set_busy_status, - gtk_seat_verify_ssh_host_key, - gtk_seat_confirm_weak_crypto_primitive, - gtk_seat_confirm_weak_cached_hostkey, - gtk_seat_is_utf8, - nullseat_echoedit_update, - gtk_seat_get_x_display, + .output = gtk_seat_output, + .eof = gtk_seat_eof, + .get_userpass_input = gtk_seat_get_userpass_input, + .notify_remote_exit = gtk_seat_notify_remote_exit, + .connection_fatal = gtk_seat_connection_fatal, + .update_specials_menu = gtk_seat_update_specials_menu, + .get_ttymode = gtk_seat_get_ttymode, + .set_busy_status = gtk_seat_set_busy_status, + .verify_ssh_host_key = gtk_seat_verify_ssh_host_key, + .confirm_weak_crypto_primitive = gtk_seat_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = gtk_seat_confirm_weak_cached_hostkey, + .is_utf8 = gtk_seat_is_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = gtk_seat_get_x_display, #ifdef NOT_X_WINDOWS - nullseat_get_windowid, + .get_windowid = nullseat_get_windowid, #else - gtk_seat_get_windowid, -#endif - gtk_seat_get_window_pixel_size, - gtk_seat_stripctrl_new, - gtk_seat_set_trust_status, + .get_windowid = gtk_seat_get_windowid, +#endif + .get_window_pixel_size = gtk_seat_get_window_pixel_size, + .stripctrl_new = gtk_seat_stripctrl_new, + .set_trust_status = gtk_seat_set_trust_status, + .verbose = nullseat_verbose_yes, + .interactive = nullseat_interactive_yes, + .get_cursor_position = gtk_seat_get_cursor_position, }; static void gtk_eventlog(LogPolicy *lp, const char *string) @@ -430,9 +438,10 @@ static void gtk_logging_error(LogPolicy *lp, const char *event) } static const LogPolicyVtable gtk_logpolicy_vt = { - gtk_eventlog, - gtk_askappend, - gtk_logging_error, + .eventlog = gtk_eventlog, + .askappend = gtk_askappend, + .logging_error = gtk_logging_error, + .verbose = null_lp_verbose_yes, }; /* @@ -567,51 +576,6 @@ static void gtkwin_set_maximised(TermWin *tw, bool maximised) #endif } -/* - * Report whether the window is minimised, for terminal reports. - */ -static bool gtkwin_is_minimised(TermWin *tw) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - return !gdk_window_is_viewable(gtk_widget_get_window(inst->window)); -} - -/* - * Report the window's position, for terminal reports. - */ -static void gtkwin_get_pos(TermWin *tw, int *x, int *y) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - /* - * I assume that when the GTK version of this call is available - * we should use it. Not sure how it differs from the GDK one, - * though. - */ -#if GTK_CHECK_VERSION(2,0,0) - gtk_window_get_position(GTK_WINDOW(inst->window), x, y); -#else - gdk_window_get_position(gtk_widget_get_window(inst->window), x, y); -#endif -} - -/* - * Report the window's pixel size, for terminal reports. - */ -static void gtkwin_get_pixels(TermWin *tw, int *x, int *y) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - /* - * I assume that when the GTK version of this call is available - * we should use it. Not sure how it differs from the GDK one, - * though. - */ -#if GTK_CHECK_VERSION(2,0,0) - gtk_window_get_size(GTK_WINDOW(inst->window), x, y); -#else - gdk_window_get_size(gtk_widget_get_window(inst->window), x, y); -#endif -} - /* * Find out whether a dialog box already exists for this window in a * particular DialogSlot. If it does, uniconify it (if we can) and @@ -631,15 +595,6 @@ static bool find_and_raise_dialog(GtkFrontend *inst, enum DialogSlot slot) return true; } -/* - * Return the window or icon title. - */ -static const char *gtkwin_get_title(TermWin *tw, bool icon) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - return icon ? inst->icontitle : inst->wintitle; -} - static void warn_on_close_callback(void *vctx, int result) { GtkFrontend *inst = (GtkFrontend *)vctx; @@ -667,20 +622,38 @@ gint delete_window(GtkWidget *widget, GdkEvent *event, GtkFrontend *inst) * case we'll just re-emphasise that one. */ if (!find_and_raise_dialog(inst, DIALOG_SLOT_WARN_ON_CLOSE)) { - char *title = dupcat(appname, " Exit Confirmation", NULL); + char *title = dupcat(appname, " Exit Confirmation"); + char *msg, *additional = NULL; + if (inst->backend && inst->backend->vt->close_warn_text) { + additional = inst->backend->vt->close_warn_text(inst->backend); + } + msg = dupprintf("Are you sure you want to close this session?%s%s", + additional ? "\n" : "", + additional ? additional : ""); GtkWidget *dialog = create_message_box( - inst->window, title, - "Are you sure you want to close this session?", + inst->window, title, msg, string_width("Most of the width of the above text"), false, &buttons_yn, warn_on_close_callback, inst); register_dialog(&inst->seat, DIALOG_SLOT_WARN_ON_CLOSE, dialog); sfree(title); + sfree(msg); + sfree(additional); } return true; } return false; } +#if GTK_CHECK_VERSION(2,0,0) +static void window_state_event(GtkWidget *widget, GdkEventWindowState *event, + gpointer user_data) +{ + GtkFrontend *inst = (GtkFrontend *)user_data; + term_notify_minimised( + inst->term, event->new_window_state & GDK_WINDOW_STATE_ICONIFIED); +} +#endif + static void update_mouseptr(GtkFrontend *inst) { switch (inst->busy_status) { @@ -688,7 +661,7 @@ static void update_mouseptr(GtkFrontend *inst) if (!inst->mouseptr_visible) { gdk_window_set_cursor(gtk_widget_get_window(inst->area), inst->blankcursor); - } else if (inst->send_raw_mouse) { + } else if (inst->pointer_indicates_raw_mouse) { gdk_window_set_cursor(gtk_widget_get_window(inst->area), inst->rawcursor); } else { @@ -753,6 +726,14 @@ static void drawing_area_setup(GtkFrontend *inst, int width, int height) new_scale = 1; #endif + int new_backing_w = w * inst->font_width + 2*inst->window_border; + int new_backing_h = h * inst->font_height + 2*inst->window_border; + new_backing_w *= new_scale; + new_backing_h *= new_scale; + + if (inst->backing_w != new_backing_w || inst->backing_h != new_backing_h) + inst->drawing_area_setup_needed = true; + /* * This event might be spurious; some GTK setups have been known * to call it when nothing at all has changed. Check if we have @@ -763,34 +744,28 @@ static void drawing_area_setup(GtkFrontend *inst, int width, int height) inst->drawing_area_setup_needed = false; inst->scale = new_scale; - - { - int backing_w = w * inst->font_width + 2*inst->window_border; - int backing_h = h * inst->font_height + 2*inst->window_border; - - backing_w *= inst->scale; - backing_h *= inst->scale; + inst->backing_w = new_backing_w; + inst->backing_h = new_backing_h; #ifndef NO_BACKING_PIXMAPS - if (inst->pixmap) { - gdk_pixmap_unref(inst->pixmap); - inst->pixmap = NULL; - } + if (inst->pixmap) { + gdk_pixmap_unref(inst->pixmap); + inst->pixmap = NULL; + } - inst->pixmap = gdk_pixmap_new(gtk_widget_get_window(inst->area), - backing_w, backing_h, -1); + inst->pixmap = gdk_pixmap_new(gtk_widget_get_window(inst->area), + inst->backing_w, inst->backing_h, -1); #endif #ifdef DRAW_TEXT_CAIRO - if (inst->surface) { - cairo_surface_destroy(inst->surface); - inst->surface = NULL; - } + if (inst->surface) { + cairo_surface_destroy(inst->surface); + inst->surface = NULL; + } - inst->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, - backing_w, backing_h); + inst->surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, inst->backing_w, inst->backing_h); #endif - } draw_backing_rect(inst); @@ -854,6 +829,18 @@ static void area_check_scale(GtkFrontend *inst) } #endif +static gboolean window_configured( + GtkWidget *widget, GdkEventConfigure *event, gpointer data) +{ + GtkFrontend *inst = (GtkFrontend *)data; + if (inst->term) { + term_notify_window_pos(inst->term, event->x, event->y); + term_notify_window_size_pixels( + inst->term, event->width, event->height); + } + return false; +} + #if GTK_CHECK_VERSION(3,10,0) static gboolean area_configured( GtkWidget *widget, GdkEventConfigure *event, gpointer data) @@ -1035,189 +1022,10 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) bool force_format_numeric_keypad = false; bool generated_something = false; char num_keypad_key = '\0'; + const char *event_string = event->string ? event->string : ""; noise_ultralight(NOISE_SOURCE_KEY, event->keyval); - // far2l - - /* - FILE *f; f = fopen("putty.log", "a"); - fprintf(f, "key_event: type=%d keyval=%d state=%d " - "hardware_keycode=%d is_modifier=%s string=[%s]\n", - event->type, event->keyval, event->state, - event->hardware_keycode, - event->is_modifier ? "true" : "false", - event->string); - fclose(f); - */ - - if (inst->term->far2l_ext) { - // far2l_ext keyboard input event structure - unsigned short repeat = 1; // 2 - unsigned short vkc = 0; // 2 - unsigned short vsc = 0; // 2 - unsigned int ctrl = 0; // 4 - gunichar uchar; // 4 - char type; // 1 - - if (event->type == GDK_KEY_PRESS) { type = 'K'; } - else if (event->type == GDK_KEY_RELEASE) { type = 'k'; } - else { return false; } - - // fixme: is where any way to distinguish left and right modifier key presses? - if (event->state & GDK_CONTROL_MASK) { ctrl |= 0x0008; } // LEFT_CTRL_PRESSED - if (event->state & GDK_MOD1_MASK) { ctrl |= 0x0002; } // LEFT_ALT_PRESSED - if (event->state & GDK_SHIFT_MASK) { ctrl |= 0x0010; } // SHIFT_PRESSED - - uchar = g_utf8_get_char(event->string); - - switch (event->hardware_keycode) { - - // todo: ctrl, shift, alt, - // numlock, capslock, scrolllock, winkey, menukey, - // numpad, replace stubs with correspoding VK codes - - case 83: vkc = 0x25; break; // VK_LEFT - case 85: vkc = 0x27; break; // VK_RIGHT - case 80: vkc = 0x26; break; // VK_UP - case 84: vkc = 0x28; break; // VK_DOWN - - case 67: vkc = 0x70; break; // VK_F1 - case 68: vkc = 0x71; break; // VK_F2 - case 69: vkc = 0x72; break; // VK_F3 - case 70: vkc = 0x73; break; // VK_F4 - case 71: vkc = 0x74; break; // VK_F5 - case 72: vkc = 0x75; break; // VK_F6 - case 73: vkc = 0x76; break; // VK_F7 - case 74: vkc = 0x77; break; // VK_F8 - case 75: vkc = 0x78; break; // VK_F9 - case 76: vkc = 0x79; break; // VK_F10 - case 95: vkc = 0x7A; break; // VK_F11 - case 96: vkc = 0x7B; break; // VK_F12 - - case 10: vkc = 0x31; break; // 1 - case 11: vkc = 0x32; break; // 2 - case 12: vkc = 0x33; break; // 3 - case 13: vkc = 0x34; break; // 4 - case 14: vkc = 0x35; break; // 5 - case 15: vkc = 0x36; break; // 6 - case 16: vkc = 0x37; break; // 7 - case 17: vkc = 0x38; break; // 8 - case 18: vkc = 0x39; break; // 9 - case 19: vkc = 0x30; break; // 0 - case 20: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 21: vkc = 0xBD; break; // stub: VK_OEM_MINUS - - case 24: vkc = 0x51; break; // Q - case 25: vkc = 0x52; break; // W - case 26: vkc = 0x45; break; // E - case 27: vkc = 0x52; break; // R - case 28: vkc = 0x54; break; // T - case 29: vkc = 0x59; break; // Y - case 30: vkc = 0x55; break; // U - case 31: vkc = 0x49; break; // I - case 32: vkc = 0x4F; break; // O - case 33: vkc = 0x50; break; // P - case 34: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 35: vkc = 0xBD; break; // stub: VK_OEM_MINUS - - case 38: vkc = 0x41; break; // A - case 39: vkc = 0x53; break; // S - case 40: vkc = 0x44; break; // D - case 41: vkc = 0x46; break; // F - case 42: vkc = 0x47; break; // G - case 43: vkc = 0x48; break; // H - case 44: vkc = 0x4A; break; // J - case 45: vkc = 0x4B; break; // K - case 46: vkc = 0x4C; break; // L - case 47: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 48: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 49: vkc = 0xBD; break; // stub: VK_OEM_MINUS - - case 51: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 52: vkc = 0x5A; break; // Z - case 53: vkc = 0x58; break; // X - case 54: vkc = 0x43; break; // C - case 55: vkc = 0x56; break; // V - case 56: vkc = 0x42; break; // B - case 57: vkc = 0x4E; break; // N - case 58: vkc = 0x4D; break; // M - case 59: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 60: vkc = 0xBD; break; // stub: VK_OEM_MINUS - case 61: vkc = 0xBD; break; // stub: VK_OEM_MINUS - - case 118: vkc = 0x2D; break; // VK_INSERT - case 119: vkc = 0x2E; break; // VK_DELETE - case 110: vkc = 0x24; break; // VK_HOME - case 115: vkc = 0x23; break; // VK_END - case 112: vkc = 0x21; break; // VK_PRIOR - case 117: vkc = 0x22; break; // VK_NEXT - - case 22: vkc = 0x08; break; // VK_BACK - case 23: vkc = 0x09; break; // VK_TAB - case 36: vkc = 0x0D; break; // VK_RETURN - case 9: vkc = 0x1B; break; // VK_ESCAPE - - case 106: vkc = 0x6F; break; // VK_DIVIDE - case 63: vkc = 0x6A; break; // VK_MULTIPLY - case 82: vkc = 0x6D; break; // VK_SUBTRACT - case 86: vkc = 0x6B; break; // VK_ADD - case 91: vkc = 0x6E; break; // VK_DECIMAL - - case 65: vkc = 0x20; break; // VK_SPACE - } - - char* kev = malloc(15); // keyboard event structure length - memcpy(kev, &repeat, sizeof(repeat)); - memcpy(kev + 2, &vkc, sizeof(vkc)); - memcpy(kev + 4, &vsc, sizeof(vsc)); - memcpy(kev + 6, &ctrl, sizeof(ctrl)); - memcpy(kev + 10, &uchar, sizeof(uchar)); - memcpy(kev + 14, &type, sizeof(type)); - - // base64-encode kev - // result in null-terminated char* out - base64_encodestate _state; - base64_init_encodestate(&_state); - char* out = malloc(15*2); - int count = base64_encode_block(kev, 15, out, &_state); - // finishing '=' characters - char* next_char = out + count; - switch (_state.step) - { - case step_B: - *next_char++ = base64_encode_value(_state.result); - *next_char++ = '='; - *next_char++ = '='; - break; - case step_C: - *next_char++ = base64_encode_value(_state.result); - *next_char++ = '='; - break; - case step_A: - break; - } - count = next_char - out; - out[count] = 0; - - // send escape seq - - char* str = "\x1b_f2l"; - backend_send(inst->backend, str, strlen(str)); - - backend_send(inst->backend, out, count); - - char* str2 = "\x07"; - backend_send(inst->backend, str2, strlen(str2)); - - // don't forget to free memory :) - free(out); - - // we should not do any other key processing in this mode - return true; - - } - #ifdef OSX_META_KEY_CONFIG if (event->state & inst->system_mod_mask) return false; /* let GTK process OS X Command key */ @@ -1266,8 +1074,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) char *old = state_string; state_string = dupcat(state_string, state_string[0] ? "|" : "", - mod_bits[i].name, - (char *)NULL); + mod_bits[i].name); sfree(old); val &= ~mod_bits[i].mod_bit; @@ -1287,11 +1094,11 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) string_string = dupstr(""); { int i; - for (i = 0; event->string[i]; i++) { + for (i = 0; event_string[i]; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", - (unsigned)event->string[i] & 0xFF); + (unsigned)event_string[i] & 0xFF); sfree(old); } } @@ -1398,7 +1205,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) if (digit < 0) inst->alt_keycode = -2; /* it's invalid */ else { -#ifdef KEY_EVENT_DIAGNOSTICS +#if defined(DEBUG) && defined(KEY_EVENT_DIAGNOSTICS) int old_keycode = inst->alt_keycode; #endif if (inst->alt_keycode == -1) @@ -1624,7 +1431,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * confirmation.) */ output_charset = CS_ISO8859_1; - strncpy(output+1, event->string, lenof(output)-1); + strncpy(output+1, event_string, lenof(output)-1); #else /* !GTK_CHECK_VERSION(2,0,0) */ /* * Most things can now be passed to @@ -1733,7 +1540,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) int ulen; wlen = mb_to_wc(DEFAULT_CODEPAGE, 0, - event->string, strlen(event->string), + event_string, strlen(event_string), widedata, lenof(widedata)-1); #ifdef KEY_EVENT_DIAGNOSTICS @@ -1854,36 +1661,40 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) * The translations below are in line with X11 policy as far * as I know. */ if ((event->state & GDK_CONTROL_MASK) && end == 2) { -#ifdef KEY_EVENT_DIAGNOSTICS - int orig = output[1]; -#endif + int orig = use_ucsoutput ? ucsoutput[1] : output[1]; + int new = orig; - if (output[1] >= '3' && output[1] <= '7') { + if (new >= '3' && new <= '7') { /* ^3,...,^7 map to 0x1B,...,0x1F */ - output[1] += '\x1B' - '3'; - } else if (output[1] == '2' || output[1] == ' ') { + new += '\x1B' - '3'; + } else if (new == '2' || new == ' ') { /* ^2 and ^Space are both ^@, i.e. \0 */ - output[1] = '\0'; - } else if (output[1] == '8') { + new = '\0'; + } else if (new == '8') { /* ^8 is DEL */ - output[1] = '\x7F'; - } else if (output[1] == '/') { + new = '\x7F'; + } else if (new == '/') { /* ^/ is the same as ^_ */ - output[1] = '\x1F'; - } else if (output[1] >= 0x40 && output[1] < 0x7F) { + new = '\x1F'; + } else if (new >= 0x40 && new < 0x7F) { /* Everything anywhere near the alphabetics just gets * masked. */ - output[1] &= 0x1F; + new &= 0x1F; } /* Anything else, e.g. '0', is unchanged. */ + if (orig == new) { #ifdef KEY_EVENT_DIAGNOSTICS - if (orig == output[1]) debug(" - manual Ctrl key handling did nothing\n"); - else +#endif + } else { +#ifdef KEY_EVENT_DIAGNOSTICS debug(" - manual Ctrl key handling: %02x -> %02x\n", - (unsigned)orig, (unsigned)output[1]); + (unsigned)orig, (unsigned)new); #endif + output[1] = new; + use_ucsoutput = false; + } } /* Control-Break sends a Break special to the backend */ @@ -2109,7 +1920,7 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) sfree(old); } debug(" - final output, special, generic encoding = [%s]\n", - charset_to_localenc(output_charset), string_string); + string_string); sfree(string_string); #endif /* @@ -2366,21 +2177,26 @@ gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; + GdkScrollDirection dir; #if GTK_CHECK_VERSION(3,4,0) gdouble dx, dy; if (gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy)) { return scroll_internal(inst, dy, event->state, event->x, event->y); - } else + } else if (!gdk_event_get_scroll_direction((GdkEvent *)event, &dir)) { return false; + } #else + dir = event->direction; +#endif + guint button; GdkEventButton *event_button; gboolean ret; - if (event->direction == GDK_SCROLL_UP) + if (dir == GDK_SCROLL_UP) button = 4; - else if (event->direction == GDK_SCROLL_DOWN) + else if (dir == GDK_SCROLL_DOWN) button = 5; else return false; @@ -2400,7 +2216,6 @@ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) ret = button_internal(inst, event_button); gdk_event_free((GdkEvent *)event_button); return ret; -#endif } #endif @@ -2596,14 +2411,16 @@ static void gtk_seat_set_busy_status(Seat *seat, BusyStatus status) update_mouseptr(inst); } -/* - * set or clear the "raw mouse message" mode - */ static void gtkwin_set_raw_mouse_mode(TermWin *tw, bool activate) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - activate = activate && !conf_get_bool(inst->conf, CONF_no_mouse_rep); inst->send_raw_mouse = activate; +} + +static void gtkwin_set_raw_mouse_mode_pointer(TermWin *tw, bool activate) +{ + GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); + inst->pointer_indicates_raw_mouse = activate; update_mouseptr(inst); } @@ -2644,7 +2461,7 @@ static void gtkwin_request_resize(TermWin *tw, int w, int h) * bogus size request which guarantees to be bigger than the * current size of the drawing area. */ - win_get_pixels(&inst->termwin, &large_x, &large_y); + get_window_pixel_size(inst, &large_x, &large_y); large_x += 32; large_y += 32; @@ -2702,25 +2519,6 @@ static void gtkwin_request_resize(TermWin *tw, int w, int h) } -static void real_palette_set(GtkFrontend *inst, int n, int r, int g, int b) -{ - inst->cols[n].red = r * 0x0101; - inst->cols[n].green = g * 0x0101; - inst->cols[n].blue = b * 0x0101; - -#if !GTK_CHECK_VERSION(3,0,0) - { - gboolean success[1]; - gdk_colormap_free_colors(inst->colmap, inst->cols + n, 1); - gdk_colormap_alloc_colors(inst->colmap, inst->cols + n, 1, - false, true, success); - if (!success[0]) - g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", - appname, n, r, g, b); - } -#endif -} - #if GTK_CHECK_VERSION(3,0,0) char *colour_to_css(const GdkColor *col) { @@ -2764,87 +2562,40 @@ void set_window_background(GtkFrontend *inst) set_gtk_widget_background(GTK_WIDGET(inst->window), &inst->cols[258]); } -static void gtkwin_palette_set(TermWin *tw, int n, int r, int g, int b) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - if (n >= 16) - n += 256 - 16; - if (n >= NALLCOLOURS) - return; - real_palette_set(inst, n, r, g, b); - if (n == 258) { - /* Default Background changed. Ensure space between text area and - * window border is redrawn */ - set_window_background(inst); - draw_backing_rect(inst); - gtk_widget_queue_draw(inst->area); - } -} - -static bool gtkwin_palette_get(TermWin *tw, int n, int *r, int *g, int *b) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - if (n < 0 || n >= NALLCOLOURS) - return false; - *r = inst->cols[n].red >> 8; - *g = inst->cols[n].green >> 8; - *b = inst->cols[n].blue >> 8; - return true; -} - -static void gtkwin_palette_reset(TermWin *tw) +static void gtkwin_palette_set(TermWin *tw, unsigned start, unsigned ncolours, + const rgb *colours) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - /* This maps colour indices in inst->conf to those used in inst->cols. */ - static const int ww[] = { - 256, 257, 258, 259, 260, 261, - 0, 8, 1, 9, 2, 10, 3, 11, - 4, 12, 5, 13, 6, 14, 7, 15 - }; - int i; - assert(lenof(ww) == NCFGCOLOURS); + assert(start <= OSC4_NCOLOURS); + assert(ncolours <= OSC4_NCOLOURS - start); #if !GTK_CHECK_VERSION(3,0,0) if (!inst->colmap) { inst->colmap = gdk_colormap_get_system(); } else { - gdk_colormap_free_colors(inst->colmap, inst->cols, NALLCOLOURS); + gdk_colormap_free_colors(inst->colmap, inst->cols, OSC4_NCOLOURS); } #endif - for (i = 0; i < NCFGCOLOURS; i++) { - inst->cols[ww[i]].red = - conf_get_int_int(inst->conf, CONF_colours, i*3+0) * 0x0101; - inst->cols[ww[i]].green = - conf_get_int_int(inst->conf, CONF_colours, i*3+1) * 0x0101; - inst->cols[ww[i]].blue = - conf_get_int_int(inst->conf, CONF_colours, i*3+2) * 0x0101; - } + for (unsigned i = 0; i < ncolours; i++) { + const rgb *in = &colours[i]; + GdkColor *out = &inst->cols[start + i]; - for (i = 0; i < NEXTCOLOURS; i++) { - if (i < 216) { - int r = i / 36, g = (i / 6) % 6, b = i % 6; - inst->cols[i+16].red = r ? r * 0x2828 + 0x3737 : 0; - inst->cols[i+16].green = g ? g * 0x2828 + 0x3737 : 0; - inst->cols[i+16].blue = b ? b * 0x2828 + 0x3737 : 0; - } else { - int shade = i - 216; - shade = shade * 0x0a0a + 0x0808; - inst->cols[i+16].red = inst->cols[i+16].green = - inst->cols[i+16].blue = shade; - } + out->red = in->r * 0x0101; + out->green = in->g * 0x0101; + out->blue = in->b * 0x0101; } #if !GTK_CHECK_VERSION(3,0,0) { - gboolean success[NALLCOLOURS]; - gdk_colormap_alloc_colors(inst->colmap, inst->cols, NALLCOLOURS, - false, true, success); - for (i = 0; i < NALLCOLOURS; i++) { + gboolean success[OSC4_NCOLOURS]; + gdk_colormap_alloc_colors(inst->colmap, inst->cols + start, + ncolours, false, true, success); + for (unsigned i = 0; i < ncolours; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", - appname, i, + appname, start + i, conf_get_int_int(inst->conf, CONF_colours, i*3+0), conf_get_int_int(inst->conf, CONF_colours, i*3+1), conf_get_int_int(inst->conf, CONF_colours, i*3+2)); @@ -2852,15 +2603,24 @@ static void gtkwin_palette_reset(TermWin *tw) } #endif - /* Since Default Background may have changed, ensure that space - * between text area and window border is refreshed. */ - set_window_background(inst); - if (inst->area && gtk_widget_get_window(inst->area)) { - draw_backing_rect(inst); - gtk_widget_queue_draw(inst->area); + if (start <= OSC4_COLOUR_bg && OSC4_COLOUR_bg < start + ncolours) { + /* Default Background has changed, so ensure that space between text + * area and window border is refreshed. */ + set_window_background(inst); + if (inst->area && gtk_widget_get_window(inst->area)) { + draw_backing_rect(inst); + gtk_widget_queue_draw(inst->area); + } } } +static void gtkwin_palette_get_overrides(TermWin *tw, Terminal *term) +{ + /* GTK has no analogue of Windows's 'standard system colours', so GTK PuTTY + * has no config option to override the normally configured colours from + * it */ +} + static struct clipboard_state *clipboard_from_atom( GtkFrontend *inst, GdkAtom atom) { @@ -3052,16 +2812,16 @@ static void gtkwin_clip_request_paste(TermWin *tw, int clipboard) * GtkTargetList.) But that work can wait until there's a need for it! */ +#ifndef NOT_X_WINDOWS + /* Store the data in a cut-buffer. */ static void store_cutbuffer(GtkFrontend *inst, char *ptr, int len) { -#ifndef NOT_X_WINDOWS if (inst->disp) { /* ICCCM says we must rotate the buffers before storing to buffer 0. */ XRotateBuffers(inst->disp, 1); XStoreBytes(inst->disp, ptr, len); } -#endif } /* Retrieve data from a cut-buffer. @@ -3069,7 +2829,6 @@ static void store_cutbuffer(GtkFrontend *inst, char *ptr, int len) */ static char *retrieve_cutbuffer(GtkFrontend *inst, int *nbytes) { -#ifndef NOT_X_WINDOWS char *ptr; if (!inst->disp) { *nbytes = 0; @@ -3081,12 +2840,10 @@ static char *retrieve_cutbuffer(GtkFrontend *inst, int *nbytes) ptr = 0; } return ptr; -#else - *nbytes = 0; - return NULL; -#endif } +#endif /* NOT_X_WINDOWS */ + static void gtkwin_clip_write( TermWin *tw, int clipboard, wchar_t *data, int *attr, truecolour *truecolour, int len, bool must_deselect) @@ -3167,9 +2924,11 @@ static void gtkwin_clip_write( sresize(state->pasteout_data, state->pasteout_data_len, char); } +#ifndef NOT_X_WINDOWS /* The legacy X cut buffers go with PRIMARY, not any other clipboard */ if (state->atom == GDK_SELECTION_PRIMARY) store_cutbuffer(inst, state->pasteout_data, state->pasteout_data_len); +#endif if (gtk_selection_owner_set(inst->area, state->atom, inst->input_event_time)) { @@ -3501,15 +3260,6 @@ static void gtkwin_set_icon_title(TermWin *tw, const char *title) set_window_titles(inst); } -void set_title_and_icon(GtkFrontend *inst, char *title, char *icon) -{ - sfree(inst->wintitle); - inst->wintitle = dupstr(title); - sfree(inst->icontitle); - inst->icontitle = dupstr(icon); - set_window_titles(inst); -} - static void gtkwin_set_scrollbar(TermWin *tw, int total, int start, int page) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); @@ -4064,6 +3814,14 @@ static void do_text_internal( y*inst->font_height + uheight + inst->window_border); } + if (attr & ATTR_STRIKE) { + int sheight = inst->fonts[fontid]->strikethrough_y; + draw_line(inst, x*inst->font_width+inst->window_border, + y*inst->font_height + sheight + inst->window_border, + (x+len)*widefactor*inst->font_width-1+inst->window_border, + y*inst->font_height + sheight + inst->window_border); + } + if ((lattr & LATTR_MODE) != LATTR_NORM) { draw_stretch_after(inst, x*inst->font_width+inst->window_border, @@ -4414,12 +4172,6 @@ static bool gtk_seat_get_windowid(Seat *seat, long *id) } #endif -static bool gtkwin_is_utf8(TermWin *tw) -{ - GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); - return inst->ucsdata.line_codepage == CS_UTF8; -} - char *setup_fonts_ucs(GtkFrontend *inst) { bool shadowbold = conf_get_bool(inst->conf, CONF_shadowbold); @@ -4592,7 +4344,7 @@ static void compute_geom_hints(GtkFrontend *inst, GdkGeometry *geom) * ourselves. */ { - struct find_app_menu_bar_ctx actx, *ctx = &actx; + struct find_app_menu_bar_ctx ctx[1]; ctx->area = inst->area; ctx->menubar = NULL; gtk_container_foreach(GTK_CONTAINER(inst->window), @@ -4626,6 +4378,7 @@ static void compute_geom_hints(GtkFrontend *inst, GdkGeometry *geom) void set_geom_hints(GtkFrontend *inst) { + const struct BackendVtable *vt; GdkGeometry geom; gint flags = GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC; compute_geom_hints(inst, &geom); @@ -4633,6 +4386,16 @@ void set_geom_hints(GtkFrontend *inst) if (inst->gotpos) flags |= GDK_HINT_USER_POS; #endif + vt = backend_vt_from_proto(conf_get_int(inst->conf, CONF_protocol)); + if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) { + /* Window resizing forbidden. Set both minimum and maximum + * dimensions to be the initial size. */ + geom.min_width = inst->width*inst->font_width + 2*inst->window_border; + geom.min_height = inst->height*inst->font_height + 2*inst->window_border; + geom.max_width = geom.min_width; + geom.max_height = geom.min_height; + flags |= GDK_HINT_MAX_SIZE; + } gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), NULL, &geom, flags); } @@ -4782,12 +4545,14 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) if (find_and_raise_dialog(inst, DIALOG_SLOT_RECONFIGURE)) return; - title = dupcat(appname, " Reconfiguration", NULL); + title = dupcat(appname, " Reconfiguration"); ctx = snew(struct after_change_settings_dialog_ctx); ctx->inst = inst; ctx->newconf = conf_copy(inst->conf); + term_pre_reconfig(inst->term, ctx->newconf); + dialog = create_config_box( title, ctx->newconf, true, inst->backend ? backend_cfg_info(inst->backend) : 0, @@ -4799,23 +4564,14 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) static void after_change_settings_dialog(void *vctx, int retval) { - /* This maps colour indices in inst->conf to those used in inst->cols. */ - static const int ww[] = { - 256, 257, 258, 259, 260, 261, - 0, 8, 1, 9, 2, 10, 3, 11, - 4, 12, 5, 13, 6, 14, 7, 15 - }; struct after_change_settings_dialog_ctx ctx = *(struct after_change_settings_dialog_ctx *)vctx; GtkFrontend *inst = ctx.inst; Conf *oldconf = inst->conf, *newconf = ctx.newconf; - int i, j; bool need_size; sfree(vctx); /* we've copied this already */ - assert(lenof(ww) == NCFGCOLOURS); - unregister_dialog(&inst->seat, DIALOG_SLOT_RECONFIGURE); if (retval > 0) { @@ -4840,37 +4596,6 @@ static void after_change_settings_dialog(void *vctx, int retval) cache_conf_values(inst); - /* - * Just setting inst->conf is sufficient to cause colour - * setting changes to appear on the next ESC]R palette - * reset. But we should also check whether any colour - * settings have been changed, and revert the ones that have - * to the new default, on the assumption that the user is - * most likely to want an immediate update. - */ - for (i = 0; i < NCFGCOLOURS; i++) { - for (j = 0; j < 3; j++) - if (conf_get_int_int(oldconf, CONF_colours, i*3+j) != - conf_get_int_int(newconf, CONF_colours, i*3+j)) - break; - if (j < 3) { - real_palette_set(inst, ww[i], - conf_get_int_int(newconf,CONF_colours,i*3+0), - conf_get_int_int(newconf,CONF_colours,i*3+1), - conf_get_int_int(newconf,CONF_colours,i*3+2)); - - /* - * If the default background has changed, we must - * repaint the space in between the window border - * and the text area. - */ - if (ww[i] == 258) { - set_window_background(inst); - draw_backing_rect(inst); - } - } - } - need_size = false; /* @@ -4889,15 +4614,6 @@ static void after_change_settings_dialog(void *vctx, int retval) ? 0 : 1); } - /* - * Change the window title, if required. - */ - if (strcmp(conf_get_str(oldconf, CONF_wintitle), - conf_get_str(newconf, CONF_wintitle))) - win_set_title(&inst->termwin, - conf_get_str(newconf, CONF_wintitle)); - set_window_titles(inst); - /* * Redo the whole tangled fonts and Unicode mess if * necessary. @@ -5227,17 +4943,16 @@ static void gtk_seat_update_specials_menu(Seat *seat) case SS_SEP: menuitem = gtk_menu_item_new(); break; - default: + default: { menuitem = gtk_menu_item_new_with_label(specials[i].name); - { - SessionSpecial *sc = snew(SessionSpecial); - *sc = specials[i]; /* structure copy */ - g_object_set_data_full(G_OBJECT(menuitem), "user-data", - sc, free_special_cmd); - } + SessionSpecial *sc = snew(SessionSpecial); + *sc = specials[i]; /* structure copy */ + g_object_set_data_full(G_OBJECT(menuitem), "user-data", + sc, free_special_cmd); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(special_menuitem), inst); break; + } } if (menuitem) { gtk_container_add(GTK_CONTAINER(menu), menuitem); @@ -5255,9 +4970,7 @@ static void gtk_seat_update_specials_menu(Seat *seat) static void start_backend(GtkFrontend *inst) { const struct BackendVtable *vt; - char *realhost; - const char *error; - char *s; + char *error, *realhost; vt = select_backend(inst->conf); @@ -5274,18 +4987,12 @@ static void start_backend(GtkFrontend *inst) seat_connection_fatal(&inst->seat, "Unable to open connection to %s:\n%s", conf_dest(inst->conf), error); + sfree(error); inst->exited = true; return; } - s = conf_get_str(inst->conf, CONF_wintitle); - if (s[0]) { - set_title_and_icon(inst, s, s); - } else { - char *title = make_default_wintitle(realhost); - set_title_and_icon(inst, title, title); - sfree(title); - } + term_setup_window_titles(inst->term, realhost); sfree(realhost); term_provide_backend(inst->term, inst->backend); @@ -5323,34 +5030,29 @@ static void get_monitor_geometry(GtkWidget *widget, GdkRectangle *geometry) #endif static const TermWinVtable gtk_termwin_vt = { - gtkwin_setup_draw_ctx, - gtkwin_draw_text, - gtkwin_draw_cursor, - gtkwin_draw_trust_sigil, - gtkwin_char_width, - gtkwin_free_draw_ctx, - gtkwin_set_cursor_pos, - gtkwin_set_raw_mouse_mode, - gtkwin_set_scrollbar, - gtkwin_bell, - gtkwin_clip_write, - gtkwin_clip_request_paste, - gtkwin_refresh, - gtkwin_request_resize, - gtkwin_set_title, - gtkwin_set_icon_title, - gtkwin_set_minimised, - gtkwin_is_minimised, - gtkwin_set_maximised, - gtkwin_move, - gtkwin_set_zorder, - gtkwin_palette_get, - gtkwin_palette_set, - gtkwin_palette_reset, - gtkwin_get_pos, - gtkwin_get_pixels, - gtkwin_get_title, - gtkwin_is_utf8, + .setup_draw_ctx = gtkwin_setup_draw_ctx, + .draw_text = gtkwin_draw_text, + .draw_cursor = gtkwin_draw_cursor, + .draw_trust_sigil = gtkwin_draw_trust_sigil, + .char_width = gtkwin_char_width, + .free_draw_ctx = gtkwin_free_draw_ctx, + .set_cursor_pos = gtkwin_set_cursor_pos, + .set_raw_mouse_mode = gtkwin_set_raw_mouse_mode, + .set_raw_mouse_mode_pointer = gtkwin_set_raw_mouse_mode_pointer, + .set_scrollbar = gtkwin_set_scrollbar, + .bell = gtkwin_bell, + .clip_write = gtkwin_clip_write, + .clip_request_paste = gtkwin_clip_request_paste, + .refresh = gtkwin_refresh, + .request_resize = gtkwin_request_resize, + .set_title = gtkwin_set_title, + .set_icon_title = gtkwin_set_icon_title, + .set_minimised = gtkwin_set_minimised, + .set_maximised = gtkwin_set_maximised, + .move = gtkwin_move, + .set_zorder = gtkwin_set_zorder, + .palette_set = gtkwin_palette_set, + .palette_get_overrides = gtkwin_palette_get_overrides, }; void new_session_window(Conf *conf, const char *geometry_string) @@ -5459,11 +5161,6 @@ void new_session_window(Conf *conf, const char *geometry_string) } } - /* - * Set up the colour map. - */ - win_palette_reset(&inst->termwin); - inst->width = conf_get_int(inst->conf, CONF_width); inst->height = conf_get_int(inst->conf, CONF_height); cache_conf_values(inst); @@ -5564,6 +5261,8 @@ void new_session_window(Conf *conf, const char *geometry_string) G_CALLBACK(area_realised), inst); g_signal_connect(G_OBJECT(inst->area), "size_allocate", G_CALLBACK(area_size_allocate), inst); + g_signal_connect(G_OBJECT(inst->window), "configure_event", + G_CALLBACK(window_configured), inst); #if GTK_CHECK_VERSION(3,10,0) g_signal_connect(G_OBJECT(inst->area), "configure_event", G_CALLBACK(area_configured), inst); @@ -5675,7 +5374,7 @@ void new_session_window(Conf *conf, const char *geometry_string) paste_clipboard_menuitem); MKMENUITEM("Copy All", copy_all_menuitem); MKSEP(); - s = dupcat("About ", appname, NULL); + s = dupcat("About ", appname); MKMENUITEM(s, about_menuitem); sfree(s); #undef MKMENUITEM @@ -5700,6 +5399,12 @@ void new_session_window(Conf *conf, const char *geometry_string) term_size(inst->term, inst->height, inst->width, conf_get_int(inst->conf, CONF_savelines)); +#if GTK_CHECK_VERSION(2,0,0) + /* Delay this signal connection until after inst->term exists */ + g_signal_connect(G_OBJECT(inst->window), "window_state_event", + G_CALLBACK(window_state_event), inst); +#endif + inst->exited = false; start_backend(inst); @@ -5714,3 +5419,13 @@ static bool gtk_seat_set_trust_status(Seat *seat, bool trusted) term_set_trust_status(inst->term, trusted); return true; } + +static bool gtk_seat_get_cursor_position(Seat *seat, int *x, int *y) +{ + GtkFrontend *inst = container_of(seat, GtkFrontend, seat); + if (inst->term) { + term_get_cursor_position(inst->term, x, y); + return true; + } + return false; +} diff --git a/unix/unix.h b/unix/unix.h index adaebea..2c0664b 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -101,18 +101,6 @@ unsigned long getticks(void); #define WCHAR wchar_t #define BYTE unsigned char -/* - * Unix-specific global flag - * - * FLAG_STDERR_TTY indicates that standard error might be a terminal and - * might get its configuration munged, so anything trying to output plain - * text (i.e. with newlines in it) will need to put it back into cooked - * mode first. Applications setting this flag should also call - * stderr_tty_init() before messing with any terminal modes, and can call - * premsg() before outputting text to stderr and postmsg() afterwards. - */ -#define FLAG_STDERR_TTY 0x1000 - #define PLATFORM_CLIPBOARDS(X) \ X(CLIP_PRIMARY, "X11 primary selection") \ X(CLIP_CLIPBOARD, "XDG clipboard") \ @@ -230,8 +218,8 @@ void logevent_dlg(eventlog_stuff *estuff, const char *string); int gtkdlg_askappend(Seat *seat, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); int gtk_seat_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx); int gtk_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, @@ -257,9 +245,6 @@ GtkWidget *create_message_box( post_dialog_fn_t after, void *afterctx); #endif -/* Things gtkwin.c needs from {ptermm,uxputty}.c */ -char *make_default_wintitle(char *hostname); - /* gtkwin.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); @@ -305,7 +290,7 @@ extern char *pty_osx_envrestore_prefix; /* Things provided by uxcons.c */ struct termios; -void stderr_tty_init(void); +void stderr_tty_init(void); /* call at startup if stderr might be a tty */ void premsg(struct termios *); void postmsg(struct termios *); @@ -456,4 +441,20 @@ static inline bool pollwrap_check_fd_rwx(pollwrapper *pw, int fd, int rwx) return (pollwrap_get_fd_rwx(pw, fd) & rwx) != 0; } +/* + * uxcliloop.c. + */ +typedef bool (*cliloop_pw_setup_t)(void *ctx, pollwrapper *pw); +typedef void (*cliloop_pw_check_t)(void *ctx, pollwrapper *pw); +typedef bool (*cliloop_continue_t)(void *ctx, bool found_any_fd, + bool ran_any_callback); + +void cli_main_loop(cliloop_pw_setup_t pw_setup, + cliloop_pw_check_t pw_check, + cliloop_continue_t cont, void *ctx); + +bool cliloop_no_pw_setup(void *ctx, pollwrapper *pw); +void cliloop_no_pw_check(void *ctx, pollwrapper *pw); +bool cliloop_always_continue(void *ctx, bool, bool); + #endif /* PUTTY_UNIX_H */ diff --git a/unix/ux_x11.c b/unix/ux_x11.c index bef036b..7a0c221 100644 --- a/unix/ux_x11.c +++ b/unix/ux_x11.c @@ -27,7 +27,7 @@ void platform_get_x11_auth(struct X11Display *disp, Conf *conf) if (!xauthfile) { xauthfile = getenv("HOME"); if (xauthfile) { - xauthfile = dupcat(xauthfile, "/.Xauthority", NULL); + xauthfile = dupcat(xauthfile, "/.Xauthority"); needs_free = true; } } @@ -127,7 +127,7 @@ int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, if (!tmpdir || !*tmpdir) tmpdir = "/tmp"; - authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX", NULL); + authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX"); { int oldumask = umask(077); diff --git a/unix/uxagentc.c b/unix/uxagentc.c index 30c2244..e4d671d 100644 --- a/unix/uxagentc.c +++ b/unix/uxagentc.c @@ -125,17 +125,30 @@ static void agent_select_result(int fd, int event) agent_cancel_query(conn); } +static const char *agent_socket_path(void) +{ + return getenv("SSH_AUTH_SOCK"); +} + +Socket *agent_connect(Plug *plug) +{ + const char *path = agent_socket_path(); + if (!path) + return new_error_socket_fmt(plug, "SSH_AUTH_SOCK not set"); + return sk_new(unix_sock_addr(path), 0, false, false, false, false, plug); +} + agent_pending_query *agent_query( strbuf *query, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { - char *name; + const char *name; int sock; struct sockaddr_un addr; int done; agent_pending_query *conn; - name = getenv("SSH_AUTH_SOCK"); + name = agent_socket_path(); if (!name || strlen(name) >= sizeof(addr.sun_path)) goto failure; diff --git a/unix/uxagentsock.c b/unix/uxagentsock.c index 3fbbb84..7e6187c 100644 --- a/unix/uxagentsock.c +++ b/unix/uxagentsock.c @@ -16,7 +16,7 @@ Socket *platform_make_agent_socket( Plug *plug, const char *dirprefix, char **error, char **name) { char *username, *socketdir, *socketname, *errw; - const char *errr; + const char *err; Socket *sock; *name = NULL; @@ -29,13 +29,14 @@ Socket *platform_make_agent_socket( if ((errw = make_dir_and_check_ours(socketdir)) != NULL) { *error = dupprintf("%s: %s\n", socketdir, errw); sfree(errw); + sfree(socketdir); return NULL; } socketname = dupprintf("%s/pageant.%d", socketdir, (int)getpid()); sock = new_unix_listener(unix_sock_addr(socketname), plug); - if ((errr = sk_socket_error(sock)) != NULL) { - *error = dupprintf("%s: %s\n", socketname, errr); + if ((err = sk_socket_error(sock)) != NULL) { + *error = dupprintf("%s: %s\n", socketname, err); sk_close(sock); sfree(socketname); rmdir(socketdir); diff --git a/unix/uxcfg.c b/unix/uxcfg.c index 6856bbf..8397a0a 100644 --- a/unix/uxcfg.c +++ b/unix/uxcfg.c @@ -67,13 +67,4 @@ void unix_setup_config_box(struct controlbox *b, bool midsession, int protocol) } } } - - /* - * Serial back end is available on Unix. However, we have to - * mask out a couple of the configuration options: mark and - * space parity are not conveniently supported, and neither is - * DSR/DTR flow control. - */ - if (!midsession || (protocol == PROT_SERIAL)) - ser_setup_config_box(b, midsession, 0x07, 0x07); } diff --git a/unix/uxcliloop.c b/unix/uxcliloop.c new file mode 100644 index 0000000..23a808d --- /dev/null +++ b/unix/uxcliloop.c @@ -0,0 +1,125 @@ +#include + +#include "putty.h" + +void cli_main_loop(cliloop_pw_setup_t pw_setup, + cliloop_pw_check_t pw_check, + cliloop_continue_t cont, void *ctx) +{ + unsigned long now = GETTICKCOUNT(); + + int *fdlist = NULL; + size_t fdsize = 0; + + pollwrapper *pw = pollwrap_new(); + + while (true) { + int rwx; + int ret; + int fdstate; + unsigned long next; + + pollwrap_clear(pw); + + if (!pw_setup(ctx, pw)) + break; /* our client signalled emergency exit */ + + /* Count the currently active fds. */ + size_t nfds = 0; + for (int fd = first_fd(&fdstate, &rwx); fd >= 0; + fd = next_fd(&fdstate, &rwx)) + nfds++; + + /* Expand the fdlist buffer if necessary. */ + sgrowarray(fdlist, fdsize, nfds); + + /* + * Add all currently open uxsel fds to pw, and store them in + * fdlist as well. + */ + size_t fdcount = 0; + for (int fd = first_fd(&fdstate, &rwx); fd >= 0; + fd = next_fd(&fdstate, &rwx)) { + fdlist[fdcount++] = fd; + pollwrap_add_fd_rwx(pw, fd, rwx); + } + + if (toplevel_callback_pending()) { + ret = pollwrap_poll_instant(pw); + } else if (run_timers(now, &next)) { + do { + unsigned long then; + long ticks; + + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; + + bool overflow = false; + if (ticks > INT_MAX) { + ticks = INT_MAX; + overflow = true; + } + + ret = pollwrap_poll_timeout(pw, ticks); + if (ret == 0 && !overflow) + now = next; + else + now = GETTICKCOUNT(); + } while (ret < 0 && errno == EINTR); + } else { + ret = pollwrap_poll_endless(pw); + } + + if (ret < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror("poll"); + exit(1); + } + + bool found_fd = (ret > 0); + + for (size_t i = 0; i < fdcount; i++) { + int fd = fdlist[i]; + int rwx = pollwrap_get_fd_rwx(pw, fd); + /* + * We must process exceptional notifications before + * ordinary readability ones, or we may go straight + * past the urgent marker. + */ + if (rwx & SELECT_X) + select_result(fd, SELECT_X); + if (rwx & SELECT_R) + select_result(fd, SELECT_R); + if (rwx & SELECT_W) + select_result(fd, SELECT_W); + } + + pw_check(ctx, pw); + + bool ran_callback = run_toplevel_callbacks(); + + if (!cont(ctx, found_fd, ran_callback)) + break; + } + + pollwrap_free(pw); + sfree(fdlist); +} + +bool cliloop_no_pw_setup(void *ctx, pollwrapper *pw) { return true; } +void cliloop_no_pw_check(void *ctx, pollwrapper *pw) {} +bool cliloop_always_continue(void *ctx, bool fd, bool cb) { return true; } + +/* + * Any application using this main loop doesn't need to do anything + * when uxsel adds or removes an fd, because we synchronously re-check + * the current list every time we go round the main loop above. + */ +uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } +void uxsel_input_remove(uxsel_id *id) { } diff --git a/unix/uxcons.c b/unix/uxcons.c index 4811c17..90e73a9 100644 --- a/unix/uxcons.c +++ b/unix/uxcons.c @@ -5,7 +5,6 @@ #include #include -#include #include #include @@ -17,8 +16,7 @@ #include "putty.h" #include "storage.h" #include "ssh.h" - -bool console_batch_mode = false; +#include "console.h" static struct termios orig_termios_stderr; static bool stderr_is_a_tty; @@ -26,7 +24,7 @@ static bool stderr_is_a_tty; void stderr_tty_init() { /* Ensure that if stderr is a tty, we can get it back to a sane state. */ - if ((flags & FLAG_STDERR_TTY) && isatty(STDERR_FILENO)) { + if (isatty(STDERR_FILENO)) { stderr_is_a_tty = true; tcgetattr(STDERR_FILENO, &orig_termios_stderr); } @@ -45,9 +43,6 @@ void postmsg(struct termios *cf) tcsetattr(STDERR_FILENO, TCSADRAIN, cf); } -/* - * Clean up and exit. - */ void cleanup_exit(int code) { /* @@ -58,9 +53,6 @@ void cleanup_exit(int code) exit(code); } -/* - * Various error message and/or fatal exit functions. - */ void console_print_error_msg(const char *prefix, const char *msg) { struct termios cf; @@ -73,49 +65,6 @@ void console_print_error_msg(const char *prefix, const char *msg) postmsg(&cf); } -void console_print_error_msg_fmt_v( - const char *prefix, const char *fmt, va_list ap) -{ - char *msg = dupvprintf(fmt, ap); - console_print_error_msg(prefix, msg); - sfree(msg); -} - -void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v(prefix, fmt, ap); - va_end(ap); -} - -void modalfatalbox(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap); - va_end(ap); - cleanup_exit(1); -} - -void nonfatal(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v("ERROR", fmt, ap); - va_end(ap); -} - -void console_connection_fatal(Seat *seat, const char *msg) -{ - console_print_error_msg("FATAL ERROR", msg); - cleanup_exit(1); -} - -void timer_change_notify(unsigned long next) -{ -} - /* * Wrapper around Unix read(2), suitable for use on a file descriptor * that's been set into nonblocking mode. Handles EAGAIN/EWOULDBLOCK @@ -154,61 +103,15 @@ static int block_and_read(int fd, void *buf, size_t len) } int console_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { int ret; - static const char absentmsg_batch[] = - "The server's host key is not cached. You have no guarantee\n" - "that the server is the computer you think it is.\n" - "The server's %s key fingerprint is:\n" - "%s\n" - "Connection abandoned.\n"; - static const char absentmsg[] = - "The server's host key is not cached. You have no guarantee\n" - "that the server is the computer you think it is.\n" - "The server's %s key fingerprint is:\n" - "%s\n" - "If you trust this host, enter \"y\" to add the key to\n" - "PuTTY's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, enter \"n\".\n" - "If you do not trust this host, press Return to abandon the\n" - "connection.\n" - "Store key in cache? (y/n) "; - - static const char wrongmsg_batch[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached. This means that either the server administrator\n" - "has changed the host key, or you have actually connected\n" - "to another computer pretending to be the server.\n" - "The new %s key fingerprint is:\n" - "%s\n" - "Connection abandoned.\n"; - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached. This means that either the server administrator\n" - "has changed the host key, or you have actually connected\n" - "to another computer pretending to be the server.\n" - "The new %s key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "enter \"y\" to update PuTTY's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, enter \"n\".\n" - "If you want to abandon the connection completely, press\n" - "Return to cancel. Pressing Return is the ONLY guaranteed\n" - "safe choice.\n" - "Update cached key? (y/n, Return cancels connection) "; - - static const char abandoned[] = "Connection abandoned.\n"; - char line[32]; struct termios cf; + const char *common_fmt, *intro, *prompt; /* * Verify the key. @@ -220,23 +123,30 @@ int console_verify_ssh_host_key( premsg(&cf); if (ret == 2) { /* key was different */ - if (console_batch_mode) { - fprintf(stderr, wrongmsg_batch, keytype, fingerprint); - return 0; - } - fprintf(stderr, wrongmsg, keytype, fingerprint); - fflush(stderr); + common_fmt = hk_wrongmsg_common_fmt; + intro = hk_wrongmsg_interactive_intro; + prompt = hk_wrongmsg_interactive_prompt; + } else { /* key was absent */ + common_fmt = hk_absentmsg_common_fmt; + intro = hk_absentmsg_interactive_intro; + prompt = hk_absentmsg_interactive_prompt; } - if (ret == 1) { /* key was absent */ - if (console_batch_mode) { - fprintf(stderr, absentmsg_batch, keytype, fingerprint); - return 0; - } - fprintf(stderr, absentmsg, keytype, fingerprint); - fflush(stderr); + + FingerprintType fptype_default = + ssh2_pick_default_fingerprint(fingerprints); + + fprintf(stderr, common_fmt, keytype, fingerprints[fptype_default]); + if (console_batch_mode) { + fputs(console_abandoned_msg, stderr); + return 0; } - { + fputs(intro, stderr); + fflush(stderr); + while (true) { + fputs(prompt, stderr); + fflush(stderr); + struct termios oldmode, newmode; tcgetattr(0, &oldmode); newmode = oldmode; @@ -246,15 +156,30 @@ int console_verify_ssh_host_key( if (block_and_read(0, line, sizeof(line) - 1) <= 0) /* handled below */; tcsetattr(0, TCSANOW, &oldmode); + + if (line[0] == 'i' || line[0] == 'I') { + fprintf(stderr, "Full public key:\n%s\n", keydisp); + if (fingerprints[SSH_FPTYPE_SHA256]) + fprintf(stderr, "SHA256 key fingerprint:\n%s\n", + fingerprints[SSH_FPTYPE_SHA256]); + if (fingerprints[SSH_FPTYPE_MD5]) + fprintf(stderr, "MD5 key fingerprint:\n%s\n", + fingerprints[SSH_FPTYPE_MD5]); + } else { + break; + } } - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { + /* In case of misplaced reflexes from another program, also recognise 'q' + * as 'abandon connection rather than trust this key' */ + if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && + line[0] != 'q' && line[0] != 'Q') { if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); postmsg(&cf); return 1; } else { - fprintf(stderr, abandoned); + fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } @@ -264,26 +189,19 @@ int console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { - static const char msg[] = - "The first %s supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Continue with connection? (y/n) "; - static const char msg_batch[] = - "The first %s supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Connection abandoned.\n"; - static const char abandoned[] = "Connection abandoned.\n"; - char line[32]; struct termios cf; premsg(&cf); + fprintf(stderr, weakcrypto_msg_common_fmt, algtype, algname); + if (console_batch_mode) { - fprintf(stderr, msg_batch, algtype, algname); + fputs(console_abandoned_msg, stderr); + postmsg(&cf); return 0; } - fprintf(stderr, msg, algtype, algname); + fputs(console_continue_prompt, stderr); fflush(stderr); { @@ -302,7 +220,7 @@ int console_confirm_weak_crypto_primitive( postmsg(&cf); return 1; } else { - fprintf(stderr, abandoned); + fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } @@ -312,32 +230,19 @@ int console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { - static const char msg[] = - "The first host key type we have stored for this server\n" - "is %s, which is below the configured warning threshold.\n" - "The server also provides the following types of host key\n" - "above the threshold, which we do not have stored:\n" - "%s\n" - "Continue with connection? (y/n) "; - static const char msg_batch[] = - "The first host key type we have stored for this server\n" - "is %s, which is below the configured warning threshold.\n" - "The server also provides the following types of host key\n" - "above the threshold, which we do not have stored:\n" - "%s\n" - "Connection abandoned.\n"; - static const char abandoned[] = "Connection abandoned.\n"; - char line[32]; struct termios cf; premsg(&cf); + fprintf(stderr, weakhk_msg_common_fmt, algname, betteralgs); + if (console_batch_mode) { - fprintf(stderr, msg_batch, algname, betteralgs); + fputs(console_abandoned_msg, stderr); + postmsg(&cf); return 0; } - fprintf(stderr, msg, algname, betteralgs); + fputs(console_continue_prompt, stderr); fflush(stderr); { @@ -356,7 +261,7 @@ int console_confirm_weak_cached_hostkey( postmsg(&cf); return 1; } else { - fprintf(stderr, abandoned); + fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } @@ -366,9 +271,8 @@ int console_confirm_weak_cached_hostkey( * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -static int console_askappend(LogPolicy *lp, Filename *filename, - void (*callback)(void *ctx, int result), - void *ctx) +int console_askappend(LogPolicy *lp, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx) { static const char msgtemplate[] = "The session log file \"%.*s\" already exists.\n" @@ -468,7 +372,7 @@ void old_keyfile_warning(void) postmsg(&cf); } -static void console_logging_error(LogPolicy *lp, const char *string) +void console_logging_error(LogPolicy *lp, const char *string) { /* Errors setting up logging are considered important, so they're * displayed to standard error even when not in verbose mode */ @@ -480,11 +384,11 @@ static void console_logging_error(LogPolicy *lp, const char *string) } -static void console_eventlog(LogPolicy *lp, const char *string) +void console_eventlog(LogPolicy *lp, const char *string) { /* Ordinary Event Log entries are displayed in the same way as * logging errors, but only in verbose mode */ - if (flags & FLAG_VERBOSE) + if (lp_verbose(lp)) console_logging_error(lp, string); } @@ -566,7 +470,6 @@ int console_get_userpass_input(prompts_t *p) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; - int len; prompt_t *pr = p->prompts[curr_prompt]; tcgetattr(infd, &oldmode); @@ -580,21 +483,21 @@ int console_get_userpass_input(prompts_t *p) console_write(outfp, ptrlen_from_asciz(pr->prompt)); - len = 0; + bool failed = false; while (1) { - int ret; + size_t toread = 65536; + size_t prev_result_len = pr->result->len; + void *ptr = strbuf_append(pr->result, toread); + int ret = read(infd, ptr, toread); - prompt_ensure_result_size(pr, len * 5 / 4 + 512); - ret = read(infd, pr->result + len, pr->resultsize - len - 1); if (ret <= 0) { - len = -1; + failed = true; break; } - len += ret; - if (pr->result[len - 1] == '\n') { - len--; + + strbuf_shrink_to(pr->result, prev_result_len + ret); + if (strbuf_chomp(pr->result, '\n')) break; - } } tcsetattr(infd, TCSANOW, &oldmode); @@ -602,12 +505,10 @@ int console_get_userpass_input(prompts_t *p) if (!pr->echo) console_write(outfp, PTRLEN_LITERAL("\n")); - if (len < 0) { + if (failed) { console_close(outfp, infd); return 0; /* failure due to read error */ } - - pr->result[len] = '\0'; } console_close(outfp, infd); @@ -627,10 +528,3 @@ bool is_interactive(void) char *platform_get_x_display(void) { return dupstr(getenv("DISPLAY")); } - -static const LogPolicyVtable default_logpolicy_vt = { - console_eventlog, - console_askappend, - console_logging_error, -}; -LogPolicy default_logpolicy[1] = {{ &default_logpolicy_vt }}; diff --git a/unix/uxfdsock.c b/unix/uxfdsock.c index 2dde833..7697d99 100644 --- a/unix/uxfdsock.c +++ b/unix/uxfdsock.c @@ -1,5 +1,5 @@ /* - * uxfdsick.c: implementation of Socket that just talks to two + * uxfdsock.c: implementation of Socket that just talks to two * existing input and output file descriptors. */ @@ -304,14 +304,14 @@ static void fdsocket_select_result_input_error(int fd, int event) } static const SocketVtable FdSocket_sockvt = { - fdsocket_plug, - fdsocket_close, - fdsocket_write, - fdsocket_write_oob, - fdsocket_write_eof, - fdsocket_set_frozen, - fdsocket_socket_error, - NULL, /* peer_info */ + .plug = fdsocket_plug, + .close = fdsocket_close, + .write = fdsocket_write, + .write_oob = fdsocket_write_oob, + .write_eof = fdsocket_write_eof, + .set_frozen = fdsocket_set_frozen, + .socket_error = fdsocket_socket_error, + .peer_info = NULL, }; Socket *make_fd_socket(int infd, int outfd, int inerrfd, Plug *plug) diff --git a/unix/uxnet.c b/unix/uxnet.c index a1db9ca..bd6ebb1 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -186,6 +186,11 @@ void sk_cleanup(void) SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_family) { + if (host[0] == '/') { + *canonicalname = dupstr(host); + return unix_sock_addr(host); + } + SockAddr *ret = snew(SockAddr); #ifndef NO_IPV6 struct addrinfo hints; @@ -252,7 +257,7 @@ SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_fami return ret; } /* This way we are always sure the h->h_name is valid :) */ - realhost->len = 0; + strbuf_clear(realhost); strbuf_catf(realhost, "%s", h->h_name); for (n = 0; h->h_addr_list[n]; n++); ret->addresses = snewn(n, unsigned long); @@ -267,7 +272,7 @@ SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_fami * success return from inet_addr. */ ret->superfamily = IP; - realhost->len = 0; + strbuf_clear(realhost); strbuf_catf(realhost, "%s", host); ret->addresses = snew(unsigned long); ret->naddresses = 1; @@ -500,15 +505,15 @@ static void sk_net_set_frozen(Socket *s, bool is_frozen); static SocketPeerInfo *sk_net_peer_info(Socket *s); static const char *sk_net_socket_error(Socket *s); -static struct SocketVtable NetSocket_sockvt = { - sk_net_plug, - sk_net_close, - sk_net_write, - sk_net_write_oob, - sk_net_write_eof, - sk_net_set_frozen, - sk_net_socket_error, - sk_net_peer_info, +static const SocketVtable NetSocket_sockvt = { + .plug = sk_net_plug, + .close = sk_net_close, + .write = sk_net_write, + .write_oob = sk_net_write_oob, + .write_eof = sk_net_write_eof, + .set_frozen = sk_net_set_frozen, + .socket_error = sk_net_socket_error, + .peer_info = sk_net_peer_info, }; static Socket *sk_net_accept(accept_ctx_t ctx, Plug *plug) @@ -575,7 +580,8 @@ static int try_connect(NetSocket *sock) { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); - plug_log(sock->plug, 0, &thisaddr, sock->port, NULL, 0); + plug_log(sock->plug, PLUGLOG_CONNECT_TRYING, + &thisaddr, sock->port, NULL, 0); } /* @@ -603,7 +609,7 @@ static int try_connect(NetSocket *sock) } } - if (sock->nodelay) { + if (sock->nodelay && family != AF_UNIX) { int b = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)) < 0) { @@ -705,7 +711,6 @@ static int try_connect(NetSocket *sock) break; #endif case AF_UNIX: - assert(sock->port == 0); /* to catch confused people */ assert(strlen(sock->addr->hostname) < sizeof u.su.sun_path); u.su.sun_family = AF_UNIX; strcpy(u.su.sun_path, sock->addr->hostname); @@ -732,6 +737,10 @@ static int try_connect(NetSocket *sock) */ sock->connected = true; sock->writable = true; + + SockAddr thisaddr = sk_extractaddr_tmp(sock->addr, &sock->step); + plug_log(sock->plug, PLUGLOG_CONNECT_SUCCESS, + &thisaddr, sock->port, NULL, 0); } uxsel_tell(sock); @@ -746,7 +755,8 @@ static int try_connect(NetSocket *sock) if (err) { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); - plug_log(sock->plug, 1, &thisaddr, sock->port, strerror(err), err); + plug_log(sock->plug, PLUGLOG_CONNECT_FAILED, + &thisaddr, sock->port, strerror(err), err); } return err; } @@ -1421,7 +1431,8 @@ static void net_select_result(int fd, int event) assert(s->addr); thisaddr = sk_extractaddr_tmp(s->addr, &s->step); - plug_log(s->plug, 1, &thisaddr, s->port, errmsg, err); + plug_log(s->plug, PLUGLOG_CONNECT_FAILED, + &thisaddr, s->port, errmsg, err); while (err && s->addr && sk_nextaddr(s->addr, &s->step)) { err = try_connect(s); @@ -1432,6 +1443,13 @@ static void net_select_result(int fd, int event) } if (!s->connected) return; /* another async attempt in progress */ + } else { + /* + * The connection attempt succeeded. + */ + SockAddr thisaddr = sk_extractaddr_tmp(s->addr, &s->step); + plug_log(s->plug, PLUGLOG_CONNECT_SUCCESS, + &thisaddr, s->port, NULL, 0); } } diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index 9c5f494..21087d6 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -11,10 +11,11 @@ #include #include +#include #include #include +#include -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "ssh.h" #include "misc.h" @@ -29,23 +30,139 @@ void cmdline_error(const char *fmt, ...) exit(1); } -FILE *pageant_logfp = NULL; -void pageant_log(void *ctx, const char *fmt, va_list ap) +static void setup_sigchld_handler(void); + +typedef enum RuntimePromptType { + RTPROMPT_UNAVAILABLE, + RTPROMPT_DEBUG, + RTPROMPT_GUI, +} RuntimePromptType; + +static const char *progname; + +struct uxpgnt_client { + FILE *logfp; + strbuf *prompt_buf; + RuntimePromptType prompt_type; + bool prompt_active; + PageantClientDialogId *dlgid; + int passphrase_fd; + int termination_pid; + + PageantListenerClient plc; +}; + +static void uxpgnt_log(PageantListenerClient *plc, const char *fmt, va_list ap) { - if (!pageant_logfp) + struct uxpgnt_client *upc = container_of(plc, struct uxpgnt_client, plc); + + if (!upc->logfp) return; - fprintf(pageant_logfp, "pageant: "); - vfprintf(pageant_logfp, fmt, ap); - fprintf(pageant_logfp, "\n"); + fprintf(upc->logfp, "pageant: "); + vfprintf(upc->logfp, fmt, ap); + fprintf(upc->logfp, "\n"); } -/* - * In Pageant our selects are synchronous, so these functions are - * empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } +static int make_pipe_to_askpass(const char *msg) +{ + int pipefds[2]; + + setup_sigchld_handler(); + + if (pipe(pipefds) < 0) + return -1; + + pid_t pid = fork(); + if (pid < 0) { + close(pipefds[0]); + close(pipefds[1]); + return -1; + } + + if (pid == 0) { + const char *args[5] = { + progname, "--gui-prompt", "--askpass", msg, NULL + }; + + dup2(pipefds[1], 1); + cloexec(pipefds[0]); + cloexec(pipefds[1]); + + /* + * See comment in fork_and_exec_self() in gtkmain.c. + */ + execv("/proc/self/exe", (char **)args); + execvp(progname, (char **)args); + perror("exec"); + _exit(127); + } + + close(pipefds[1]); + return pipefds[0]; +} + +static bool uxpgnt_ask_passphrase( + PageantListenerClient *plc, PageantClientDialogId *dlgid, + const char *comment) +{ + struct uxpgnt_client *upc = container_of(plc, struct uxpgnt_client, plc); + + assert(!upc->dlgid); /* Pageant core should be serialising requests */ + + char *msg = dupprintf( + "A client of Pageant wants to use the following encrypted key:\n" + "%s\n" + "If you intended this, enter the passphrase to decrypt the key.", + comment); + + switch (upc->prompt_type) { + case RTPROMPT_UNAVAILABLE: + sfree(msg); + return false; + + case RTPROMPT_GUI: + upc->passphrase_fd = make_pipe_to_askpass(msg); + sfree(msg); + if (upc->passphrase_fd < 0) + return false; /* something went wrong */ + break; + + case RTPROMPT_DEBUG: + fprintf(upc->logfp, "pageant passphrase request: %s\n", msg); + sfree(msg); + break; + } + + upc->prompt_active = true; + upc->dlgid = dlgid; + return true; +} + +static void passphrase_done(struct uxpgnt_client *upc, bool success) +{ + PageantClientDialogId *dlgid = upc->dlgid; + upc->dlgid = NULL; + upc->prompt_active = false; + + if (upc->logfp) + fprintf(upc->logfp, "pageant passphrase response: %s\n", + success ? "success" : "failure"); + + if (success) + pageant_passphrase_request_success( + dlgid, ptrlen_from_strbuf(upc->prompt_buf)); + else + pageant_passphrase_request_refused(dlgid); + + strbuf_free(upc->prompt_buf); + upc->prompt_buf = strbuf_new_nm(); +} + +static const PageantListenerClientVtable uxpgnt_vtable = { + .log = uxpgnt_log, + .ask_passphrase = uxpgnt_ask_passphrase, +}; /* * More stubs. @@ -66,14 +183,16 @@ static void usage(void) { printf("Pageant: SSH agent\n"); printf("%s\n", ver); - printf("Usage: pageant [key files]\n"); - printf(" pageant [key files] --exec [args]\n"); - printf(" pageant -a [key files]\n"); + printf("Usage: pageant [[--encrypted] key files]\n"); + printf(" pageant [[--encrypted] key files] --exec [args]\n"); + printf(" pageant -a [--encrypted] [key files]\n"); printf(" pageant -d [key identifiers]\n"); + printf(" pageant -D\n"); + printf(" pageant -r [key identifiers]\n"); + printf(" pageant -R\n"); printf(" pageant --public [key identifiers]\n"); printf(" pageant ( --public-openssh | -L ) [key identifiers]\n"); - printf(" pageant -l\n"); - printf(" pageant -D\n"); + printf(" pageant -l [-E fptype]\n"); printf("Lifetime options, for running Pageant as an agent:\n"); printf(" -X run with the lifetime of the X server\n"); printf(" -T run with the lifetime of the controlling tty\n"); @@ -87,11 +206,16 @@ static void usage(void) printf(" --public-openssh, -L print public keys in OpenSSH format\n"); printf(" -d delete key(s) from the agent\n"); printf(" -D delete all keys from the agent\n"); + printf(" -r re-encrypt keys in the agent (forget cleartext\n"); + printf(" -R re-encrypt all possible keys in the agent\n"); printf("Other options:\n"); printf(" -v verbose mode (in agent mode)\n"); printf(" -s -c force POSIX or C shell syntax (in agent mode)\n"); - printf(" --tty-prompt force tty-based passphrase prompt (in -a mode)\n"); - printf(" --gui-prompt force GUI-based passphrase prompt (in -a mode)\n"); + printf(" --symlink path create symlink to socket (in agent mode)\n"); + printf(" --encrypted when adding keys, don't decrypt\n"); + printf(" -E alg, --fptype alg fingerprint type for -l (sha256, md5)\n"); + printf(" --tty-prompt force tty-based passphrase prompt\n"); + printf(" --gui-prompt force GUI-based passphrase prompt\n"); printf(" --askpass behave like a standalone askpass program\n"); exit(1); } @@ -152,7 +276,7 @@ void chan_no_request_response(Channel *chan, bool success) {} * except that x11_closing has to signal back to the main loop that * it's time to terminate. */ -static void x11_log(Plug *p, int type, SockAddr *addr, int port, +static void x11_log(Plug *p, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) {} static void x11_receive(Plug *plug, int urgent, const char *data, size_t len) {} static void x11_sent(Plug *plug, size_t bufsize) {} @@ -165,7 +289,7 @@ struct X11Connection { Plug plug; }; -char *socketname; +static char *socketname; static enum { SHELL_AUTO, SHELL_SH, SHELL_CSH } shell_type = SHELL_AUTO; void pageant_print_env(int pid) { @@ -248,16 +372,31 @@ void pageant_fork_and_print_env(bool retain_tty) } } -int signalpipe[2]; +static int signalpipe[2] = { -1, -1 }; -void sigchld(int signum) +static void sigchld(int signum) { if (write(signalpipe[1], "x", 1) <= 0) /* not much we can do about it */; } +static void setup_sigchld_handler(void) +{ + if (signalpipe[0] >= 0) + return; + + /* + * Set up the pipe we'll use to tell us about SIGCHLD. + */ + if (pipe(signalpipe) < 0) { + perror("pipe"); + exit(1); + } + putty_signal(SIGCHLD, sigchld); +} + #define TTY_LIFE_POLL_INTERVAL (TICKSPERSEC * 30) -void *dummy_timer_ctx; +static void *dummy_timer_ctx; static void tty_life_timer(void *ctx, unsigned long now) { schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx); @@ -265,12 +404,18 @@ static void tty_life_timer(void *ctx, unsigned long now) typedef enum { KEYACT_AGENT_LOAD, - KEYACT_CLIENT_ADD, + KEYACT_AGENT_LOAD_ENCRYPTED, + KEYACT_CLIENT_BASE, + KEYACT_CLIENT_ADD = KEYACT_CLIENT_BASE, + KEYACT_CLIENT_ADD_ENCRYPTED, KEYACT_CLIENT_DEL, KEYACT_CLIENT_DEL_ALL, KEYACT_CLIENT_LIST, KEYACT_CLIENT_PUBLIC_OPENSSH, - KEYACT_CLIENT_PUBLIC + KEYACT_CLIENT_PUBLIC, + KEYACT_CLIENT_SIGN, + KEYACT_CLIENT_REENCRYPT, + KEYACT_CLIENT_REENCRYPT_ALL, } keyact; struct cmdline_key_action { struct cmdline_key_action *next; @@ -280,10 +425,11 @@ struct cmdline_key_action { bool is_agent_action(keyact action) { - return action == KEYACT_AGENT_LOAD; + return action < KEYACT_CLIENT_BASE; } -struct cmdline_key_action *keyact_head = NULL, *keyact_tail = NULL; +static struct cmdline_key_action *keyact_head = NULL, *keyact_tail = NULL; +static uint32_t sign_flags = 0; void add_keyact(keyact action, const char *filename) { @@ -313,14 +459,15 @@ bool have_controlling_tty(void) } } -char **exec_args = NULL; -enum { +static char **exec_args = NULL; +static enum { LIFE_UNSPEC, LIFE_X11, LIFE_TTY, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC } life = LIFE_UNSPEC; -const char *display = NULL; -enum { +static const char *display = NULL; +static enum { PROMPT_UNSPEC, PROMPT_TTY, PROMPT_GUI } prompt_type = PROMPT_UNSPEC; +static FingerprintType key_list_fptype = SSH_FPTYPE_DEFAULT; static char *askpass_tty(const char *prompt) { @@ -329,7 +476,7 @@ static char *askpass_tty(const char *prompt) p->to_server = false; p->from_server = false; p->name = dupstr("Pageant passphrase prompt"); - add_prompt(p, dupcat(prompt, ": ", (const char *)NULL), false); + add_prompt(p, dupcat(prompt, ": "), false); ret = console_get_userpass_input(p); assert(ret >= 0); @@ -338,7 +485,7 @@ static char *askpass_tty(const char *prompt) free_prompts(p); return NULL; } else { - char *passphrase = dupstr(p->prompts[0]->result); + char *passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); return passphrase; } @@ -391,7 +538,7 @@ static char *askpass(const char *prompt) } } -static bool unix_add_keyfile(const char *filename_str) +static bool unix_add_keyfile(const char *filename_str, bool add_encrypted) { Filename *filename = filename_from_str(filename_str); int status; @@ -403,7 +550,7 @@ static bool unix_add_keyfile(const char *filename_str) /* * Try without a passphrase. */ - status = pageant_add_keyfile(filename, NULL, &err); + status = pageant_add_keyfile(filename, NULL, &err, add_encrypted); if (status == PAGEANT_ACTION_OK) { goto cleanup; } else if (status == PAGEANT_ACTION_FAILURE) { @@ -425,7 +572,8 @@ static bool unix_add_keyfile(const char *filename_str) if (!passphrase) break; - status = pageant_add_keyfile(filename, passphrase, &err); + status = pageant_add_keyfile(filename, passphrase, &err, + add_encrypted); smemclr(passphrase, strlen(passphrase)); sfree(passphrase); @@ -446,49 +594,106 @@ static bool unix_add_keyfile(const char *filename_str) return ret; } -void key_list_callback(void *ctx, const char *fingerprint, - const char *comment, struct pageant_pubkey *key) +void key_list_callback(void *ctx, char **fingerprints, const char *comment, + uint32_t ext_flags, struct pageant_pubkey *key) { - printf("%s %s\n", fingerprint, comment); + const char *mode = ""; + if (ext_flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY) + mode = " (encrypted)"; + else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE) + mode = " (re-encryptable)"; + + FingerprintType this_type = + ssh2_pick_fingerprint(fingerprints, key_list_fptype); + printf("%s %s%s\n", fingerprints[this_type], comment, mode); } struct key_find_ctx { const char *string; bool match_fp, match_comment; + bool match_fptypes[SSH_N_FPTYPES]; struct pageant_pubkey *found; int nfound; }; -bool match_fingerprint_string(const char *string, const char *fingerprint) +static bool match_fingerprint_string( + const char *string_orig, char **fingerprints, + const struct key_find_ctx *ctx) { const char *hash; - /* Find the hash in the fingerprint string. It'll be the word at the end. */ - hash = strrchr(fingerprint, ' '); - assert(hash); - hash++; + for (unsigned fptype = 0; fptype < SSH_N_FPTYPES; fptype++) { + if (!ctx->match_fptypes[fptype]) + continue; - /* Now see if the search string is a prefix of the full hash, - * neglecting colons and case differences. */ - while (1) { - while (*string == ':') string++; - while (*hash == ':') hash++; - if (!*string) - return true; - if (tolower((unsigned char)*string) != tolower((unsigned char)*hash)) - return false; - string++; + const char *fingerprint = fingerprints[fptype]; + if (!fingerprint) + continue; + + /* Find the hash in the fingerprint string. It'll be the word + * at the end. */ + hash = strrchr(fingerprint, ' '); + assert(hash); hash++; + + const char *string = string_orig; + bool case_sensitive; + const char *ignore_chars = ""; + + switch (fptype) { + case SSH_FPTYPE_MD5: + /* MD5 fingerprints are in hex, so disregard case differences. */ + case_sensitive = false; + /* And we don't really need to force the user to type the + * colons in between the digits, which are always the + * same. */ + ignore_chars = ":"; + break; + case SSH_FPTYPE_SHA256: + /* Skip over the "SHA256:" prefix, which we don't really + * want to force the user to type. On the other hand, + * tolerate it on the input string. */ + assert(strstartswith(hash, "SHA256:")); + hash += 7; + if (strstartswith(string, "SHA256:")) + string += 7; + /* SHA256 fingerprints are base64, which is intrinsically + * case sensitive. */ + case_sensitive = true; + break; + } + + /* Now see if the search string is a prefix of the full hash, + * neglecting colons and (where appropriate) case differences. */ + while (1) { + string += strspn(string, ignore_chars); + hash += strspn(hash, ignore_chars); + if (!*string) + return true; + char sc = *string, hc = *hash; + if (!case_sensitive) { + sc = tolower((unsigned char)sc); + hc = tolower((unsigned char)hc); + } + if (sc != hc) + break; + string++; + hash++; + } } + + return false; } -void key_find_callback(void *vctx, const char *fingerprint, - const char *comment, struct pageant_pubkey *key) +void key_find_callback(void *vctx, char **fingerprints, + const char *comment, uint32_t ext_flags, + struct pageant_pubkey *key) { struct key_find_ctx *ctx = (struct key_find_ctx *)vctx; if ((ctx->match_comment && !strcmp(ctx->string, comment)) || - (ctx->match_fp && match_fingerprint_string(ctx->string, fingerprint))) + (ctx->match_fp && match_fingerprint_string(ctx->string, fingerprints, + ctx))) { if (!ctx->found) ctx->found = pageant_pubkey_copy(key); @@ -498,10 +703,12 @@ void key_find_callback(void *vctx, const char *fingerprint, struct pageant_pubkey *find_key(const char *string, char **retstr) { - struct key_find_ctx actx, *ctx = &actx; + struct key_find_ctx ctx[1]; struct pageant_pubkey key_in, *key_ret; bool try_file = true, try_fp = true, try_comment = true; bool file_errors = false; + bool try_all_fptypes = true; + FingerprintType fptype = SSH_FPTYPE_DEFAULT; /* * Trim off disambiguating prefixes telling us how to interpret @@ -524,6 +731,18 @@ struct pageant_pubkey *find_key(const char *string, char **retstr) string += 12; try_file = false; try_comment = false; + } else if (!strnicmp(string, "md5:", 4)) { + string += 4; + try_file = false; + try_comment = false; + try_all_fptypes = false; + fptype = SSH_FPTYPE_MD5; + } else if (!strncmp(string, "sha256:", 7)) { + string += 7; + try_file = false; + try_comment = false; + try_all_fptypes = false; + fptype = SSH_FPTYPE_SHA256; } /* @@ -537,8 +756,8 @@ struct pageant_pubkey *find_key(const char *string, char **retstr) const char *error; key_in.blob = strbuf_new(); - if (!rsa_ssh1_loadpub(fn, BinarySink_UPCAST(key_in.blob), - NULL, &error)) { + if (!rsa1_loadpub_f(fn, BinarySink_UPCAST(key_in.blob), + NULL, &error)) { strbuf_free(key_in.blob); key_in.blob = NULL; if (file_errors) { @@ -567,8 +786,8 @@ struct pageant_pubkey *find_key(const char *string, char **retstr) const char *error; key_in.blob = strbuf_new(); - if (!ssh2_userkey_loadpub(fn, NULL, BinarySink_UPCAST(key_in.blob), - NULL, &error)) { + if (!ppk_loadpub_f(fn, NULL, BinarySink_UPCAST(key_in.blob), + NULL, &error)) { strbuf_free(key_in.blob); key_in.blob = NULL; if (file_errors) { @@ -609,6 +828,8 @@ struct pageant_pubkey *find_key(const char *string, char **retstr) ctx->string = string; ctx->match_fp = try_fp; ctx->match_comment = try_comment; + for (unsigned i = 0; i < SSH_N_FPTYPES; i++) + ctx->match_fptypes[i] = (try_all_fptypes || i == fptype); ctx->found = NULL; ctx->nfound = 0; if (pageant_enum_keys(key_find_callback, ctx, retstr) == @@ -636,6 +857,9 @@ void run_client(void) struct pageant_pubkey *key; bool errors = false; char *retstr; + LoadedFile *message = lf_new(AGENT_MAX_MSGLEN); + bool message_loaded = false, message_ok = false; + strbuf *signature = strbuf_new(); if (!agent_exists()) { fprintf(stderr, "pageant: no agent running to talk to\n"); @@ -645,7 +869,9 @@ void run_client(void) for (act = keyact_head; act; act = act->next) { switch (act->action) { case KEYACT_CLIENT_ADD: - if (!unix_add_keyfile(act->filename)) + case KEYACT_CLIENT_ADD_ENCRYPTED: + if (!unix_add_keyfile(act->filename, + act->action == KEYACT_CLIENT_ADD_ENCRYPTED)) errors = true; break; case KEYACT_CLIENT_LIST: @@ -668,6 +894,18 @@ void run_client(void) if (key) pageant_pubkey_free(key); break; + case KEYACT_CLIENT_REENCRYPT: + key = NULL; + if (!(key = find_key(act->filename, &retstr)) || + pageant_reencrypt_key(key, &retstr) == PAGEANT_ACTION_FAILURE) { + fprintf(stderr, "pageant: re-encrypting key '%s': %s\n", + act->filename, retstr); + sfree(retstr); + errors = true; + } + if (key) + pageant_pubkey_free(key); + break; case KEYACT_CLIENT_PUBLIC_OPENSSH: case KEYACT_CLIENT_PUBLIC: key = NULL; @@ -707,51 +945,202 @@ void run_client(void) errors = true; } break; + case KEYACT_CLIENT_REENCRYPT_ALL: { + int status = pageant_reencrypt_all_keys(&retstr); + if (status == PAGEANT_ACTION_FAILURE) { + fprintf(stderr, "pageant: re-encrypting all keys: " + "%s\n", retstr); + sfree(retstr); + errors = true; + } else if (status == PAGEANT_ACTION_WARNING) { + fprintf(stderr, "pageant: re-encrypting all keys: " + "warning: %s\n", retstr); + sfree(retstr); + } + break; + } + case KEYACT_CLIENT_SIGN: + key = NULL; + if (!message_loaded) { + message_loaded = true; + switch(lf_load_fp(message, stdin)) { + case LF_TOO_BIG: + fprintf(stderr, "pageant: message to sign is too big\n"); + errors = true; + break; + case LF_ERROR: + fprintf(stderr, "pageant: reading message to sign: %s\n", + strerror(errno)); + errors = true; + break; + case LF_OK: + message_ok = true; + break; + } + } + if (!message_ok) + break; + strbuf_clear(signature); + if (!(key = find_key(act->filename, &retstr)) || + pageant_sign(key, ptrlen_from_lf(message), signature, + sign_flags, &retstr) == PAGEANT_ACTION_FAILURE) { + fprintf(stderr, "pageant: signing with key '%s': %s\n", + act->filename, retstr); + sfree(retstr); + errors = true; + } else { + fwrite(signature->s, 1, signature->len, stdout); + } + if (key) + pageant_pubkey_free(key); + break; default: unreachable("Invalid client action found"); } } + lf_free(message); + strbuf_free(signature); + if (errors) exit(1); } static const PlugVtable X11Connection_plugvt = { - x11_log, - x11_closing, - x11_receive, - x11_sent, - NULL + .log = x11_log, + .closing = x11_closing, + .receive = x11_receive, + .sent = x11_sent, }; -void run_agent(void) + +static bool agent_loop_pw_setup(void *vctx, pollwrapper *pw) +{ + struct uxpgnt_client *upc = (struct uxpgnt_client *)vctx; + + if (signalpipe[0] >= 0) { + pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); + } + + if (upc->prompt_active) + pollwrap_add_fd_rwx(pw, upc->passphrase_fd, SELECT_R); + + return true; +} + +static void agent_loop_pw_check(void *vctx, pollwrapper *pw) +{ + struct uxpgnt_client *upc = (struct uxpgnt_client *)vctx; + + if (life == LIFE_TTY) { + /* + * Every time we wake up (whether it was due to tty_timer + * elapsing or for any other reason), poll to see if we still + * have a controlling terminal. If we don't, then our + * containing tty session has ended, so it's time to clean up + * and leave. + */ + if (!have_controlling_tty()) { + time_to_die = true; + return; + } + } + + if (signalpipe[0] >= 0 && + pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { + char c[1]; + if (read(signalpipe[0], c, 1) <= 0) + /* ignore error */; + /* ignore its value; it'll be `x' */ + while (1) { + int status; + pid_t pid; + pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) + break; + if (pid == upc->termination_pid) + time_to_die = true; + } + } + + if (upc->prompt_active && + pollwrap_check_fd_rwx(pw, upc->passphrase_fd, SELECT_R)) { + char c; + int retd = read(upc->passphrase_fd, &c, 1); + + switch (upc->prompt_type) { + case RTPROMPT_GUI: + if (retd <= 0) { + close(upc->passphrase_fd); + upc->passphrase_fd = -1; + bool ok = (retd == 0); + if (!strbuf_chomp(upc->prompt_buf, '\n')) + ok = false; + passphrase_done(upc, ok); + } else { + put_byte(upc->prompt_buf, c); + } + break; + case RTPROMPT_DEBUG: + if (retd <= 0) { + passphrase_done(upc, false); + /* Now never try to read from stdin again */ + upc->prompt_type = RTPROMPT_UNAVAILABLE; + break; + } + + switch (c) { + case '\n': + case '\r': + passphrase_done(upc, true); + break; + case '\004': + passphrase_done(upc, false); + break; + case '\b': + case '\177': + strbuf_shrink_by(upc->prompt_buf, 1); + break; + case '\025': + strbuf_clear(upc->prompt_buf); + break; + default: + put_byte(upc->prompt_buf, c); + break; + } + break; + case RTPROMPT_UNAVAILABLE: + unreachable("Should never have started a prompt at all"); + } + } +} + +static bool agent_loop_continue(void *vctx, bool fd, bool cb) +{ + return !time_to_die; +} + +void run_agent(FILE *logfp, const char *symlink_path) { const char *err; char *errw; struct pageant_listen_state *pl; Plug *pl_plug; Socket *sock; - unsigned long now; - int *fdlist; - int fd; - int i, fdstate; - size_t fdsize; - int termination_pid = -1; bool errors = false; Conf *conf; const struct cmdline_key_action *act; - fdlist = NULL; - fdsize = 0; - pageant_init(); /* * Start by loading any keys provided on the command line. */ for (act = keyact_head; act; act = act->next) { - assert(act->action == KEYACT_AGENT_LOAD); - if (!unix_add_keyfile(act->filename)) + assert(act->action == KEYACT_AGENT_LOAD || + act->action == KEYACT_AGENT_LOAD_ENCRYPTED); + if (!unix_add_keyfile(act->filename, + act->action == KEYACT_AGENT_LOAD_ENCRYPTED)) errors = true; } if (errors) @@ -760,7 +1149,15 @@ void run_agent(void) /* * Set up a listening socket and run Pageant on it. */ - pl = pageant_listener_new(&pl_plug); + struct uxpgnt_client upc[1]; + memset(upc, 0, sizeof(upc)); + upc->plc.vt = &uxpgnt_vtable; + upc->logfp = logfp; + upc->passphrase_fd = -1; + upc->termination_pid = -1; + upc->prompt_buf = strbuf_new_nm(); + upc->prompt_type = display ? RTPROMPT_GUI : RTPROMPT_UNAVAILABLE; + pl = pageant_listener_new(&pl_plug, &upc->plc); sock = platform_make_agent_socket(pl_plug, PAGEANT_DIR_PREFIX, &errw, &socketname); if (!sock) { @@ -770,13 +1167,35 @@ void run_agent(void) } pageant_listener_got_socket(pl, sock); + if (symlink_path) { + /* + * Try to make a symlink to the Unix socket, in a location of + * the user's choosing. + * + * If the link already exists, we want to replace it. There + * are two ways we could do this: either make it under another + * name and then rename it over the top, or remove the old + * link first. The former is what 'ln -sf' does, on the + * grounds that it's more atomic. But I think in this case, + * where the expected use case is that the previous agent has + * long since shut down, atomicity isn't a critical concern + * compared to not accidentally overwriting some non-symlink + * that might have important data in it! + */ + struct stat st; + if (lstat(symlink_path, &st) == 0 && S_ISLNK(st.st_mode)) + unlink(symlink_path); + if (symlink(socketname, symlink_path) < 0) + fprintf(stderr, "pageant: making symlink %s: %s\n", + symlink_path, strerror(errno)); + } + conf = conf_new(); conf_set_int(conf, CONF_proxy_type, PROXY_NONE); /* * Lifetime preparations. */ - signalpipe[0] = signalpipe[1] = -1; if (life == LIFE_X11) { struct X11Display *disp; void *greeting; @@ -823,20 +1242,43 @@ void run_agent(void) pageant_fork_and_print_env(false); } else if (life == LIFE_DEBUG) { pageant_print_env(getpid()); - pageant_logfp = stdout; + upc->logfp = stdout; + + struct termios orig_termios; + upc->passphrase_fd = fileno(stdin); + if (tcgetattr(upc->passphrase_fd, &orig_termios) == 0) { + struct termios new_termios = orig_termios; + new_termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON); + + /* + * Try to set up a watchdog process that will restore + * termios if we crash or are killed. If successful, turn + * off echo, for runtime passphrase prompts. + */ + int pipefd[2]; + if (pipe(pipefd) == 0) { + pid_t pid = fork(); + if (pid == 0) { + tcsetattr(upc->passphrase_fd, TCSADRAIN, &new_termios); + close(pipefd[1]); + char buf[4096]; + while (read(pipefd[0], buf, sizeof(buf)) > 0); + tcsetattr(upc->passphrase_fd, TCSADRAIN, &new_termios); + _exit(0); + } else if (pid > 0) { + upc->prompt_type = RTPROMPT_DEBUG; + } + + close(pipefd[0]); + if (pid < 0) + close(pipefd[1]); + } + } } else if (life == LIFE_EXEC) { pid_t agentpid, pid; agentpid = getpid(); - - /* - * Set up the pipe we'll use to tell us about SIGCHLD. - */ - if (pipe(signalpipe) < 0) { - perror("pipe"); - exit(1); - } - putty_signal(SIGCHLD, sigchld); + setup_sigchld_handler(); pid = fork(); if (pid < 0) { @@ -849,147 +1291,26 @@ void run_agent(void) perror("exec"); _exit(127); } else { - termination_pid = pid; + upc->termination_pid = pid; } } - /* - * Now we've decided on our logging arrangements, pass them on to - * pageant.c. - */ - pageant_listener_set_logfn(pl, NULL, pageant_logfp ? pageant_log : NULL); - - now = GETTICKCOUNT(); - - pollwrapper *pw = pollwrap_new(); + if (!upc->logfp) + upc->plc.suppress_logging = true; - while (!time_to_die) { - int rwx; - int ret; - unsigned long next; - - pollwrap_clear(pw); - - if (signalpipe[0] >= 0) { - pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); - } - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - /* - * Add all currently open fds to pw, and store them in fdlist - * as well. - */ - int fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - } else if (run_timers(now, &next)) { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } else { - ret = pollwrap_poll_endless(pw); - } - - if (ret < 0 && errno == EINTR) - continue; - - if (ret < 0) { - perror("poll"); - exit(1); - } - - if (life == LIFE_TTY) { - /* - * Every time we wake up (whether it was due to tty_timer - * elapsing or for any other reason), poll to see if we - * still have a controlling terminal. If we don't, then - * our containing tty session has ended, so it's time to - * clean up and leave. - */ - if (!have_controlling_tty()) { - time_to_die = true; - break; - } - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - if (signalpipe[0] >= 0 && - pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { - char c[1]; - if (read(signalpipe[0], c, 1) <= 0) - /* ignore error */; - /* ignore its value; it'll be `x' */ - while (1) { - int status; - pid_t pid; - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - if (pid == termination_pid) - time_to_die = true; - } - } - - run_toplevel_callbacks(); - } + cli_main_loop(agent_loop_pw_setup, agent_loop_pw_check, + agent_loop_continue, upc); /* - * When we come here, we're terminating, and should clean up our - * Unix socket file if possible. + * Before terminating, clean up our Unix socket file if possible. */ if (unlink(socketname) < 0) { fprintf(stderr, "pageant: %s: %s\n", socketname, strerror(errno)); exit(1); } + strbuf_free(upc->prompt_buf); conf_free(conf); - pollwrap_free(pw); } int main(int argc, char **argv) @@ -997,6 +1318,10 @@ int main(int argc, char **argv) bool doing_opts = true; keyact curr_keyact = KEYACT_AGENT_LOAD; const char *standalone_askpass_prompt = NULL; + const char *symlink_path = NULL; + FILE *logfp = NULL; + + progname = argv[0]; /* * Process the command line. @@ -1010,17 +1335,21 @@ int main(int argc, char **argv) usage(); exit(0); } else if (!strcmp(p, "-v")) { - pageant_logfp = stderr; + logfp = stderr; } else if (!strcmp(p, "-a")) { curr_keyact = KEYACT_CLIENT_ADD; } else if (!strcmp(p, "-d")) { curr_keyact = KEYACT_CLIENT_DEL; + } else if (!strcmp(p, "-r")) { + curr_keyact = KEYACT_CLIENT_REENCRYPT; } else if (!strcmp(p, "-s")) { shell_type = SHELL_SH; } else if (!strcmp(p, "-c")) { shell_type = SHELL_CSH; } else if (!strcmp(p, "-D")) { add_keyact(KEYACT_CLIENT_DEL_ALL, NULL); + } else if (!strcmp(p, "-R")) { + add_keyact(KEYACT_CLIENT_REENCRYPT_ALL, NULL); } else if (!strcmp(p, "-l")) { add_keyact(KEYACT_CLIENT_LIST, NULL); } else if (!strcmp(p, "--public")) { @@ -1031,8 +1360,31 @@ int main(int argc, char **argv) life = LIFE_X11; } else if (!strcmp(p, "-T")) { life = LIFE_TTY; + } else if (!strcmp(p, "--no-decrypt") || + !strcmp(p, "-no-decrypt") || + !strcmp(p, "--no_decrypt") || + !strcmp(p, "-no_decrypt") || + !strcmp(p, "--nodecrypt") || + !strcmp(p, "-nodecrypt") || + !strcmp(p, "--encrypted") || + !strcmp(p, "-encrypted")) { + if (curr_keyact == KEYACT_AGENT_LOAD) + curr_keyact = KEYACT_AGENT_LOAD_ENCRYPTED; + else if (curr_keyact == KEYACT_CLIENT_ADD) + curr_keyact = KEYACT_CLIENT_ADD_ENCRYPTED; + else { + fprintf(stderr, "pageant: unexpected -E while not adding " + "keys\n"); + exit(1); + } } else if (!strcmp(p, "--debug")) { life = LIFE_DEBUG; + } else if (!strcmp(p, "--test-sign")) { + curr_keyact = KEYACT_CLIENT_SIGN; + sign_flags = 0; + } else if (strstartswith(p, "--test-sign-with-flags=")) { + curr_keyact = KEYACT_CLIENT_SIGN; + sign_flags = atoi(p + strlen("--test-sign-with-flags=")); } else if (!strcmp(p, "--permanent")) { life = LIFE_PERM; } else if (!strcmp(p, "--exec")) { @@ -1058,8 +1410,37 @@ int main(int argc, char **argv) "after --askpass\n"); exit(1); } + } else if (!strcmp(p, "--symlink")) { + if (--argc > 0) { + symlink_path = *++argv; + } else { + fprintf(stderr, "pageant: expected a pathname " + "after --symlink\n"); + exit(1); + } + } else if (!strcmp(p, "-E") || !strcmp(p, "--fptype")) { + const char *keyword; + if (--argc > 0) { + keyword = *++argv; + } else { + fprintf(stderr, "pageant: expected a type string " + "after %s\n", p); + exit(1); + } + if (!strcmp(keyword, "md5")) + key_list_fptype = SSH_FPTYPE_MD5; + else if (!strcmp(keyword, "sha256")) + key_list_fptype = SSH_FPTYPE_SHA256; + else { + fprintf(stderr, "pageant: unknown fingerprint type `%s'\n", + keyword); + exit(1); + } } else if (!strcmp(p, "--")) { doing_opts = false; + } else { + fprintf(stderr, "pageant: unrecognised option '%s'\n", p); + exit(1); } } else { /* @@ -1093,6 +1474,8 @@ int main(int argc, char **argv) return 1; puts(passphrase); + fflush(stdout); + smemclr(passphrase, strlen(passphrase)); sfree(passphrase); return 0; @@ -1131,8 +1514,8 @@ int main(int argc, char **argv) has_lifetime = true; if (has_lifetime && has_client_actions) { - fprintf(stderr, "pageant: client key actions (-a, -d, -D, -l, -L)" - " do not go with an agent lifetime option\n"); + fprintf(stderr, "pageant: client key actions (-a, -d, -D, -r, -R, " + "-l, -L) do not go with an agent lifetime option\n"); exit(1); } if (!has_lifetime && has_agent_actions) { @@ -1147,7 +1530,7 @@ int main(int argc, char **argv) } if (has_lifetime) { - run_agent(); + run_agent(logfp, symlink_path); } else if (has_client_actions) { run_client(); } diff --git a/unix/uxplink.c b/unix/uxplink.c index 0201431..240783f 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -15,8 +15,8 @@ #include #include -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" +#include "ssh.h" #include "storage.h" #include "tree234.h" @@ -38,7 +38,7 @@ void cmdline_error(const char *fmt, ...) static bool local_tty = false; /* do we have a local tty? */ static Backend *backend; -Conf *conf; +static Conf *conf; /* * Default settings that are specific to Unix plink. @@ -315,12 +315,12 @@ void cleanup_termios(void) tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } -bufchain stdout_data, stderr_data; -bufchain_sink stdout_bcs, stderr_bcs; -StripCtrlChars *stdout_scc, *stderr_scc; -BinarySink *stdout_bs, *stderr_bs; +static bufchain stdout_data, stderr_data; +static bufchain_sink stdout_bcs, stderr_bcs; +static StripCtrlChars *stdout_scc, *stderr_scc; +static BinarySink *stdout_bs, *stderr_bs; -enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; +static enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; size_t try_output(bool is_stderr) { @@ -379,25 +379,35 @@ static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) return ret; } +static bool plink_seat_interactive(Seat *seat) +{ + return (!*conf_get_str(conf, CONF_remote_cmd) && + !*conf_get_str(conf, CONF_remote_cmd2) && + !*conf_get_str(conf, CONF_ssh_nc_host)); +} + static const SeatVtable plink_seat_vt = { - plink_output, - plink_eof, - plink_get_userpass_input, - nullseat_notify_remote_exit, - console_connection_fatal, - nullseat_update_specials_menu, - plink_get_ttymode, - nullseat_set_busy_status, - console_verify_ssh_host_key, - console_confirm_weak_crypto_primitive, - console_confirm_weak_cached_hostkey, - nullseat_is_never_utf8, - plink_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - nullseat_get_window_pixel_size, - console_stripctrl_new, - console_set_trust_status, + .output = plink_output, + .eof = plink_eof, + .get_userpass_input = plink_get_userpass_input, + .notify_remote_exit = nullseat_notify_remote_exit, + .connection_fatal = console_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = plink_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .verify_ssh_host_key = console_verify_ssh_host_key, + .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = plink_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = nullseat_get_window_pixel_size, + .stripctrl_new = console_stripctrl_new, + .set_trust_status = console_set_trust_status, + .verbose = cmdline_seat_verbose, + .interactive = plink_seat_interactive, + .get_cursor_position = nullseat_get_cursor_position, }; static Seat plink_seat[1] = {{ &plink_seat_vt }}; @@ -467,7 +477,7 @@ static void from_tty(void *vbuf, unsigned len) } } -int signalpipe[2]; +static int signalpipe[2]; void sigwinch(int signum) { @@ -475,13 +485,6 @@ void sigwinch(int signum) /* not much we can do about it */; } -/* - * In Plink our selects are synchronous, so these functions are - * empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - /* * Short description of parameters. */ @@ -498,6 +501,8 @@ static void usage(void) printf(" -load sessname Load settings from saved session\n"); printf(" -ssh -telnet -rlogin -raw -serial\n"); printf(" force use of a particular protocol\n"); + printf(" -ssh-connection\n"); + printf(" force use of the bare ssh-connection protocol\n"); printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -batch disable all interactive prompts\n"); @@ -516,15 +521,17 @@ static void usage(void) printf(" -X -x enable / disable X11 forwarding\n"); printf(" -A -a enable / disable agent forwarding\n"); printf(" -t -T enable / disable pty allocation\n"); - printf(" -1 -2 force use of particular protocol version\n"); + printf(" -1 -2 force use of particular SSH protocol version\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); + printf(" -no-trivial-auth\n"); + printf(" disconnect if SSH authentication succeeds trivially\n"); printf(" -noshare disable use of connection sharing\n"); printf(" -share enable use of connection sharing\n"); - printf(" -hostkey aa:bb:cc:...\n"); + printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -sanitise-stderr, -sanitise-stdout, " "-no-sanitise-stderr, -no-sanitise-stdout\n"); @@ -540,6 +547,9 @@ static void usage(void) printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); + printf(" -logoverwrite\n"); + printf(" -logappend\n"); + printf(" control what happens when a log file already exists\n"); printf(" -shareexists\n"); printf(" test whether a connection-sharing upstream exists\n"); exit(1); @@ -560,30 +570,107 @@ const bool share_can_be_upstream = true; const bool buildinfo_gtk_relevant = false; +const unsigned cmdline_tooltype = + TOOLTYPE_HOST_ARG | + TOOLTYPE_HOST_ARG_CAN_BE_SESSION | + TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | + TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; + +static bool seen_stdin_eof = false; + +static bool plink_pw_setup(void *vctx, pollwrapper *pw) +{ + pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); + + if (!seen_stdin_eof && + backend_connected(backend) && + backend_sendok(backend) && + backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) { + /* If we're OK to send, then try to read from stdin. */ + pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R); + } + + if (bufchain_size(&stdout_data) > 0) { + /* If we have data for stdout, try to write to stdout. */ + pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W); + } + + if (bufchain_size(&stderr_data) > 0) { + /* If we have data for stderr, try to write to stderr. */ + pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W); + } + + return true; +} + +static void plink_pw_check(void *vctx, pollwrapper *pw) +{ + if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { + char c[1]; + struct winsize size; + if (read(signalpipe[0], c, 1) <= 0) + /* ignore error */; + /* ignore its value; it'll be `x' */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) + backend_size(backend, size.ws_col, size.ws_row); + } + + if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) { + char buf[4096]; + int ret; + + if (backend_connected(backend)) { + ret = read(STDIN_FILENO, buf, sizeof(buf)); + noise_ultralight(NOISE_SOURCE_IOLEN, ret); + if (ret < 0) { + perror("stdin: read"); + exit(1); + } else if (ret == 0) { + backend_special(backend, SS_EOF, 0); + seen_stdin_eof = true; + } else { + if (local_tty) + from_tty(buf, ret); + else + backend_send(backend, buf, ret); + } + } + } + + if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) { + backend_unthrottle(backend, try_output(false)); + } + + if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) { + backend_unthrottle(backend, try_output(true)); + } +} + +static bool plink_continue(void *vctx, bool found_any_fd, + bool ran_any_callback) +{ + if (!backend_connected(backend) && + bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) + return false; /* terminate main loop */ + return true; +} + int main(int argc, char **argv) { - bool sending; - int *fdlist; - int fd; - int i, fdstate; - size_t fdsize; int exitcode; bool errors; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; bool use_subsystem = false; bool just_test_share_exists = false; - unsigned long now; struct winsize size; const struct BackendVtable *backvt; - fdlist = NULL; - fdsize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ - default_protocol = PROT_SSH; - default_port = 22; + settings_set_default_protocol(PROT_SSH); + settings_set_default_port(22); bufchain_init(&stdout_data); bufchain_init(&stderr_data); @@ -593,22 +680,14 @@ int main(int argc, char **argv) stderr_bs = BinarySink_UPCAST(&stderr_bcs); outgoingeof = EOF_NO; - flags = FLAG_STDERR_TTY; - cmdline_tooltype |= - (TOOLTYPE_HOST_ARG | - TOOLTYPE_HOST_ARG_CAN_BE_SESSION | - TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | - TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD); - stderr_tty_init(); /* * Process the command line. */ conf = conf_new(); do_defaults(NULL, conf); - loaded_session = false; - default_protocol = conf_get_int(conf, CONF_protocol); - default_port = conf_get_int(conf, CONF_port); + settings_set_default_protocol(conf_get_int(conf, CONF_protocol)); + settings_set_default_port(conf_get_int(conf, CONF_port)); errors = false; { /* @@ -618,10 +697,10 @@ int main(int argc, char **argv) if (p) { const struct BackendVtable *vt = backend_vt_from_name(p); if (vt) { - default_protocol = vt->protocol; - default_port = vt->default_port; - conf_set_int(conf, CONF_protocol, default_protocol); - conf_set_int(conf, CONF_port, default_port); + settings_set_default_protocol(vt->protocol); + settings_set_default_port(vt->default_port); + conf_set_int(conf, CONF_protocol, vt->protocol); + conf_set_int(conf, CONF_port, vt->default_port); } } } @@ -737,11 +816,6 @@ int main(int argc, char **argv) if (use_subsystem) conf_set_bool(conf, CONF_ssh_subsys, true); - if (!*conf_get_str(conf, CONF_remote_cmd) && - !*conf_get_str(conf, CONF_remote_cmd2) && - !*conf_get_str(conf, CONF_ssh_nc_host)) - flags |= FLAG_INTERACTIVE; - /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. @@ -753,6 +827,13 @@ int main(int argc, char **argv) return 1; } + if (backvt->flags & BACKEND_NEEDS_TERMINAL) { + fprintf(stderr, + "Plink doesn't support %s, which needs terminal emulation\n", + backvt->displayname); + return 1; + } + /* * Block SIGPIPE, so that we'll get EPIPE individually on * particular network connections that go wrong. @@ -825,8 +906,8 @@ int main(int argc, char **argv) if (just_test_share_exists) { if (!backvt->test_for_upstream) { - fprintf(stderr, "Connection sharing not supported for connection " - "type '%s'\n", backvt->name); + fprintf(stderr, "Connection sharing not supported for this " + "connection type (%s)'\n", backvt->displayname); return 1; } if (backvt->test_for_upstream(conf_get_str(conf, CONF_host), @@ -839,10 +920,9 @@ int main(int argc, char **argv) /* * Start up the connection. */ - logctx = log_init(default_logpolicy, conf); + logctx = log_init(console_cli_logpolicy, conf); { - const char *error; - char *realhost; + char *error, *realhost; /* nodelay is only useful if stdin is a terminal device */ bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && isatty(0); @@ -858,6 +938,7 @@ int main(int argc, char **argv) conf_get_bool(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s\n", error); + sfree(error); return 1; } ldisc_create(conf, NULL, backend, plink_seat); @@ -872,158 +953,9 @@ int main(int argc, char **argv) local_tty = (tcgetattr(STDIN_FILENO, &orig_termios) == 0); atexit(cleanup_termios); seat_echoedit_update(plink_seat, 1, 1); - sending = false; - now = GETTICKCOUNT(); - - pollwrapper *pw = pollwrap_new(); - while (1) { - int rwx; - int ret; - unsigned long next; - - pollwrap_clear(pw); - - pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); - - if (!sending && - backend_connected(backend) && - backend_sendok(backend) && - backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) { - /* If we're OK to send, then try to read from stdin. */ - pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R); - } - - if (bufchain_size(&stdout_data) > 0) { - /* If we have data for stdout, try to write to stdout. */ - pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W); - } - - if (bufchain_size(&stderr_data) > 0) { - /* If we have data for stderr, try to write to stderr. */ - pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W); - } - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); + cli_main_loop(plink_pw_setup, plink_pw_check, plink_continue, NULL); - /* - * Add all currently open fds to pw, and store them in fdlist - * as well. - */ - int fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - } else if (run_timers(now, &next)) { - do { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } while (ret < 0 && errno == EINTR); - } else { - ret = pollwrap_poll_endless(pw); - } - - if (ret < 0 && errno == EINTR) - continue; - - if (ret < 0) { - perror("poll"); - exit(1); - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { - char c[1]; - struct winsize size; - if (read(signalpipe[0], c, 1) <= 0) - /* ignore error */; - /* ignore its value; it'll be `x' */ - if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) - backend_size(backend, size.ws_col, size.ws_row); - } - - if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) { - char buf[4096]; - int ret; - - if (backend_connected(backend)) { - ret = read(STDIN_FILENO, buf, sizeof(buf)); - noise_ultralight(NOISE_SOURCE_IOLEN, ret); - if (ret < 0) { - perror("stdin: read"); - exit(1); - } else if (ret == 0) { - backend_special(backend, SS_EOF, 0); - sending = false; /* send nothing further after this */ - } else { - if (local_tty) - from_tty(buf, ret); - else - backend_send(backend, buf, ret); - } - } - } - - if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) { - backend_unthrottle(backend, try_output(false)); - } - - if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) { - backend_unthrottle(backend, try_output(true)); - } - - run_toplevel_callbacks(); - - if (!backend_connected(backend) && - bufchain_size(&stdout_data) == 0 && - bufchain_size(&stderr_data) == 0) - break; /* we closed the connection */ - } exitcode = backend_exitcode(backend); if (exitcode < 0) { fprintf(stderr, "Remote process exit code unavailable\n"); diff --git a/unix/uxpoll.c b/unix/uxpoll.c index da74ebf..474926b 100644 --- a/unix/uxpoll.c +++ b/unix/uxpoll.c @@ -126,29 +126,44 @@ int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds) return poll(pw->fds, pw->nfd, milliseconds); } -int pollwrap_get_fd_events(pollwrapper *pw, int fd) +static void pollwrap_get_fd_events_revents(pollwrapper *pw, int fd, + int *events_p, int *revents_p) { pollwrap_fdtopos *f2p, f2p_find; + int events = 0, revents = 0; assert(fd >= 0); f2p_find.fd = fd; f2p = find234(pw->fdtopos, &f2p_find, NULL); - if (!f2p) - return 0; + if (f2p) { + events = pw->fds[f2p->pos].events; + revents = pw->fds[f2p->pos].revents; + } + + if (events_p) + *events_p = events; + if (revents_p) + *revents_p = revents; +} - return pw->fds[f2p->pos].revents; +int pollwrap_get_fd_events(pollwrapper *pw, int fd) +{ + int revents; + pollwrap_get_fd_events_revents(pw, fd, NULL, &revents); + return revents; } int pollwrap_get_fd_rwx(pollwrapper *pw, int fd) { - int revents = pollwrap_get_fd_events(pw, fd); + int events, revents; + pollwrap_get_fd_events_revents(pw, fd, &events, &revents); int rwx = 0; - if (revents & SELECT_R_OUT) + if ((events & POLLIN) && (revents & SELECT_R_OUT)) rwx |= SELECT_R; - if (revents & SELECT_W_OUT) + if ((events & POLLOUT) && (revents & SELECT_W_OUT)) rwx |= SELECT_W; - if (revents & SELECT_X_OUT) + if ((events & POLLPRI) && (revents & SELECT_X_OUT)) rwx |= SELECT_X; return rwx; } diff --git a/unix/uxproxy.c b/unix/uxproxy.c index 7fb46ef..0a637bd 100644 --- a/unix/uxproxy.c +++ b/unix/uxproxy.c @@ -33,7 +33,7 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname, { char *logmsg = dupprintf("Starting local proxy command: %s", cmd); - plug_log(plug, 2, NULL, 0, logmsg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } diff --git a/unix/uxpsusan.c b/unix/uxpsusan.c new file mode 100644 index 0000000..c60728c --- /dev/null +++ b/unix/uxpsusan.c @@ -0,0 +1,422 @@ +/* + * 'psusan': Pseudo Ssh for Untappable, Separately Authenticated Networks + * + * This is a standalone application that speaks on its standard I/O + * (or a listening Unix-domain socket) the server end of the bare + * ssh-connection protocol used by PuTTY's connection sharing. + * + * The idea of this tool is that you can use it to communicate across + * any 8-bit-clean data channel between two inconveniently separated + * domains, provided the channel is already (as the name suggests) + * adequately secured against eavesdropping and modification and + * already authenticated as the right user. + * + * If you're sitting at one end of such a channel and want to type + * commands into the other end, the most obvious thing to do is to run + * a terminal session directly over it. But if you run psusan at one + * end, and a PuTTY (or compatible) client at the other end, then you + * not only get a single terminal session: you get all the other SSH + * amenities, like the ability to spawn extra terminal sessions, + * forward ports or X11 connections, even forward an SSH agent. + * + * There are a surprising number of channels of that kind; see the man + * page for some examples. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "putty.h" +#include "mpint.h" +#include "ssh.h" +#include "sshserver.h" + +const char *const appname = "psusan"; + +void modalfatalbox(const char *p, ...) +{ + va_list ap; + fprintf(stderr, "FATAL ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} +void nonfatal(const char *p, ...) +{ + va_list ap; + fprintf(stderr, "ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); +} + +char *platform_default_s(const char *name) +{ + return NULL; +} + +bool platform_default_b(const char *name, bool def) +{ + return def; +} + +int platform_default_i(const char *name, int def) +{ + return def; +} + +FontSpec *platform_default_fontspec(const char *name) +{ + return fontspec_new(""); +} + +Filename *platform_default_filename(const char *name) +{ + return filename_from_str(""); +} + +char *x_get_default(const char *key) +{ + return NULL; /* this is a stub */ +} + +void old_keyfile_warning(void) { } + +void timer_change_notify(unsigned long next) +{ +} + +char *platform_get_x_display(void) { return NULL; } + +void make_unix_sftp_filehandle_key(void *vdata, size_t size) +{ + /* psusan runs without a random number generator, so we can't make + * this up by random_read. Fortunately, psusan is also + * non-adversarial, so it's safe to generate this trivially. */ + unsigned char *data = (unsigned char *)vdata; + for (size_t i = 0; i < size; i++) + data[i] = (unsigned)rand() / ((unsigned)RAND_MAX / 256); +} + +static bool verbose; + +struct server_instance { + unsigned id; + LogPolicy logpolicy; +}; + +static void log_to_stderr(unsigned id, const char *msg) +{ + if (!verbose) + return; + if (id != (unsigned)-1) + fprintf(stderr, "#%u: ", id); + fputs(msg, stderr); + fputc('\n', stderr); + fflush(stderr); +} + +static void server_eventlog(LogPolicy *lp, const char *event) +{ + struct server_instance *inst = container_of( + lp, struct server_instance, logpolicy); + if (verbose) + log_to_stderr(inst->id, event); +} + +static void server_logging_error(LogPolicy *lp, const char *event) +{ + struct server_instance *inst = container_of( + lp, struct server_instance, logpolicy); + log_to_stderr(inst->id, event); /* unconditional */ +} + +static int server_askappend( + LogPolicy *lp, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx) +{ + return 2; /* always overwrite (FIXME: could make this a cmdline option) */ +} + +static const LogPolicyVtable server_logpolicy_vt = { + .eventlog = server_eventlog, + .askappend = server_askappend, + .logging_error = server_logging_error, + .verbose = null_lp_verbose_no, +}; + +static void show_help(FILE *fp) +{ + fputs("usage: psusan [options]\n" + "options: --listen SOCKETPATH listen for connections on a Unix-domain socket\n" + " --listen-once (with --listen) stop after one connection\n" + " --verbose print log messages to standard error\n" + " --sessiondir DIR cwd for session subprocess (default $HOME)\n" + " --sshlog FILE write ssh-connection packet log to FILE\n" + " --sshrawlog FILE write packets and raw data log to FILE\n" + "also: psusan --help show this text\n" + " psusan --version show version information\n", fp); +} + +static void show_version_and_exit(void) +{ + char *buildinfo_text = buildinfo("\n"); + printf("%s: %s\n%s\n", appname, ver, buildinfo_text); + sfree(buildinfo_text); + exit(0); +} + +const bool buildinfo_gtk_relevant = false; + +static bool listening = false, listen_once = false; +static bool finished = false; +void server_instance_terminated(LogPolicy *lp) +{ + struct server_instance *inst = container_of( + lp, struct server_instance, logpolicy); + + if (listening && !listen_once) { + log_to_stderr(inst->id, "connection terminated"); + } else { + finished = true; + } + + sfree(inst); +} + +bool psusan_continue(void *ctx, bool fd, bool cb) +{ + return !finished; +} + +static bool longoptarg(const char *arg, const char *expected, + const char **val, int *argcp, char ***argvp) +{ + int len = strlen(expected); + if (memcmp(arg, expected, len)) + return false; + if (arg[len] == '=') { + *val = arg + len + 1; + return true; + } else if (arg[len] == '\0') { + if (--*argcp > 0) { + *val = *++*argvp; + return true; + } else { + fprintf(stderr, "%s: option %s expects an argument\n", + appname, expected); + exit(1); + } + } + return false; +} + +static bool longoptnoarg(const char *arg, const char *expected) +{ + int len = strlen(expected); + if (memcmp(arg, expected, len)) + return false; + if (arg[len] == '=') { + fprintf(stderr, "%s: option %s expects no argument\n", + appname, expected); + exit(1); + } else if (arg[len] == '\0') { + return true; + } + return false; +} + +struct server_config { + Conf *conf; + const SshServerConfig *ssc; + + unsigned next_id; + + Socket *listening_socket; + Plug listening_plug; +}; + +static Plug *server_conn_plug( + struct server_config *cfg, struct server_instance **inst_out) +{ + struct server_instance *inst = snew(struct server_instance); + + memset(inst, 0, sizeof(*inst)); + + inst->id = cfg->next_id++; + inst->logpolicy.vt = &server_logpolicy_vt; + + if (inst_out) + *inst_out = inst; + + return ssh_server_plug( + cfg->conf, cfg->ssc, NULL, 0, NULL, NULL, + &inst->logpolicy, &unix_live_sftpserver_vt); +} + +static void server_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, + const char *error_msg, int error_code) +{ + log_to_stderr(-1, error_msg); +} + +static void server_closing(Plug *plug, const char *error_msg, int error_code, + bool calling_back) +{ + log_to_stderr(-1, error_msg); +} + +static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) +{ + struct server_config *cfg = container_of( + p, struct server_config, listening_plug); + Socket *s; + const char *err; + + struct server_instance *inst; + + if (listen_once) { + if (!cfg->listening_socket) /* in case of rapid double-accept */ + return 1; + sk_close(cfg->listening_socket); + cfg->listening_socket = NULL; + } + + Plug *plug = server_conn_plug(cfg, &inst); + s = constructor(ctx, plug); + if ((err = sk_socket_error(s)) != NULL) + return 1; + + SocketPeerInfo *pi = sk_peer_info(s); + + char *msg = dupprintf("new connection from %s", pi->log_text); + log_to_stderr(inst->id, msg); + sfree(msg); + sk_free_peer_info(pi); + + sk_set_frozen(s, false); + ssh_server_start(plug, s); + return 0; +} + +static const PlugVtable server_plugvt = { + .log = server_log, + .closing = server_closing, + .accepting = server_accepting, +}; + +unsigned auth_methods(AuthPolicy *ap) +{ return 0; } +bool auth_none(AuthPolicy *ap, ptrlen username) +{ return false; } +int auth_password(AuthPolicy *ap, ptrlen username, ptrlen password, + ptrlen *new_password_opt) +{ return 0; } +bool auth_publickey(AuthPolicy *ap, ptrlen username, ptrlen public_blob) +{ return false; } +RSAKey *auth_publickey_ssh1( + AuthPolicy *ap, ptrlen username, mp_int *rsa_modulus) +{ return NULL; } +AuthKbdInt *auth_kbdint_prompts(AuthPolicy *ap, ptrlen username) +{ return NULL; } +int auth_kbdint_responses(AuthPolicy *ap, const ptrlen *responses) +{ return -1; } +char *auth_ssh1int_challenge(AuthPolicy *ap, unsigned method, ptrlen username) +{ return NULL; } +bool auth_ssh1int_response(AuthPolicy *ap, ptrlen response) +{ return false; } +bool auth_successful(AuthPolicy *ap, ptrlen username, unsigned method) +{ return false; } + +int main(int argc, char **argv) +{ + const char *listen_socket = NULL; + + SshServerConfig ssc; + + Conf *conf = make_ssh_server_conf(); + + memset(&ssc, 0, sizeof(ssc)); + + ssc.application_name = "PSUSAN"; + ssc.session_starting_dir = getenv("HOME"); + ssc.bare_connection = true; + + while (--argc > 0) { + const char *arg = *++argv; + const char *val; + + if (longoptnoarg(arg, "--help")) { + show_help(stdout); + exit(0); + } else if (longoptnoarg(arg, "--version")) { + show_version_and_exit(); + } else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) { + verbose = true; + } else if (longoptarg(arg, "--sessiondir", &val, &argc, &argv)) { + ssc.session_starting_dir = val; + } else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) || + longoptarg(arg, "-sshlog", &val, &argc, &argv)) { + Filename *logfile = filename_from_str(val); + conf_set_filename(conf, CONF_logfilename, logfile); + filename_free(logfile); + conf_set_int(conf, CONF_logtype, LGTYP_PACKETS); + conf_set_int(conf, CONF_logxfovr, LGXF_OVR); + } else if (longoptarg(arg, "--sshrawlog", &val, &argc, &argv) || + longoptarg(arg, "-sshrawlog", &val, &argc, &argv)) { + Filename *logfile = filename_from_str(val); + conf_set_filename(conf, CONF_logfilename, logfile); + filename_free(logfile); + conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW); + conf_set_int(conf, CONF_logxfovr, LGXF_OVR); + } else if (longoptarg(arg, "--listen", &val, &argc, &argv)) { + listen_socket = val; + } else if (!strcmp(arg, "--listen-once")) { + listen_once = true; + } else { + fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg); + exit(1); + } + } + + sk_init(); + uxsel_init(); + + struct server_config scfg; + scfg.conf = conf; + scfg.ssc = &ssc; + scfg.next_id = 0; + + if (listen_socket) { + listening = true; + scfg.listening_plug.vt = &server_plugvt; + SockAddr *addr = unix_sock_addr(listen_socket); + scfg.listening_socket = new_unix_listener(addr, &scfg.listening_plug); + char *msg = dupprintf("listening on Unix socket %s", listen_socket); + log_to_stderr(-1, msg); + sfree(msg); + } else { + struct server_instance *inst; + Plug *plug = server_conn_plug(&scfg, &inst); + ssh_server_start(plug, make_fd_socket(0, 1, -1, plug)); + log_to_stderr(inst->id, "running directly on stdio"); + } + + cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check, + psusan_continue, NULL); + + return 0; +} diff --git a/unix/uxpterm.c b/unix/uxpterm.c index 054d56e..b18e344 100644 --- a/unix/uxpterm.c +++ b/unix/uxpterm.c @@ -14,6 +14,8 @@ const bool dup_check_launchable = false; /* no need to check host name * in conf */ const bool use_pty_argv = true; +const unsigned cmdline_tooltype = TOOLTYPE_NONNETWORK; + /* gtkwin.c will call this, and in pterm it's not needed */ void noise_ultralight(NoiseSourceId id, unsigned long data) { } @@ -39,15 +41,9 @@ void cleanup_exit(int code) exit(code); } -char *make_default_wintitle(char *hostname) -{ - return dupstr("pterm"); -} - void setup(bool single) { - cmdline_tooltype = TOOLTYPE_NONNETWORK; - default_protocol = -1; + settings_set_default_protocol(-1); if (single) pty_pre_init(); diff --git a/unix/uxpty.c b/unix/uxpty.c index e57770f..bfd0de5 100644 --- a/unix/uxpty.c +++ b/unix/uxpty.c @@ -25,6 +25,7 @@ #include "putty.h" #include "ssh.h" +#include "sshserver.h" /* to check the prototypes of server-needed things */ #include "tree234.h" #ifndef OMIT_UTMP @@ -205,6 +206,8 @@ static void setup_utmp(char *ttyname, char *location) struct timeval tv; pw = getpwuid(getuid()); + if (!pw) + return; /* can't stamp utmp if we don't have a username */ memset(&utmp_entry, 0, sizeof(utmp_entry)); utmp_entry.ut_type = USER_PROCESS; utmp_entry.ut_pid = getpid(); @@ -889,6 +892,15 @@ Backend *pty_backend_create( pty->fds[i].pty = pty; } + if (pty_signal_pipe[0] < 0) { + if (pipe(pty_signal_pipe) < 0) { + perror("pipe"); + exit(1); + } + cloexec(pty_signal_pipe[0]); + cloexec(pty_signal_pipe[1]); + } + pty->seat = seat; pty->backend.vt = &pty_backend; @@ -1134,7 +1146,7 @@ Backend *pty_backend_create( for (val = conf_get_str_strs(conf, CONF_environmt, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_environmt, key, &key)) { - char *varval = dupcat(key, "=", val, NULL); + char *varval = dupcat(key, "=", val); putenv(varval); /* * We must not free varval, since putenv links it @@ -1207,16 +1219,18 @@ Backend *pty_backend_create( execl(shell, shell, "-c", cmd, (void *)NULL); } } else { - char *shell = getenv("SHELL"); + const char *shell = getenv("SHELL"); + if (!shell) + shell = "/bin/sh"; char *shellname; if (conf_get_bool(conf, CONF_login_shell)) { - char *p = strrchr(shell, '/'); + const char *p = strrchr(shell, '/'); shellname = snewn(2+strlen(shell), char); p = p ? p+1 : shell; sprintf(shellname, "-%s", p); } else - shellname = shell; - execl(getenv("SHELL"), shellname, (void *)NULL); + shellname = (char *)shell; + execl(shell, shellname, (void *)NULL); } /* @@ -1247,14 +1261,6 @@ Backend *pty_backend_create( add234(ptys_by_pid, pty); } - if (pty_signal_pipe[0] < 0) { - if (pipe(pty_signal_pipe) < 0) { - perror("pipe"); - exit(1); - } - cloexec(pty_signal_pipe[0]); - cloexec(pty_signal_pipe[1]); - } pty_uxsel_setup(pty); return &pty->backend; @@ -1267,10 +1273,10 @@ Backend *pty_backend_create( * it gets the argv array from the global variable pty_argv, expecting * that it will have been invoked by pterm. */ -static const char *pty_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, - char **realhost, bool nodelay, bool keepalive) +static char *pty_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { const char *cmd = NULL; struct ssh_ttymodes modes; @@ -1280,7 +1286,8 @@ static const char *pty_init(Seat *seat, Backend **backend_handle, if (pty_argv && pty_argv[0] && !pty_argv[1]) cmd = pty_argv[0]; - *backend_handle= pty_backend_create( + assert(vt == &pty_backend); + *backend_handle = pty_backend_create( seat, logctx, conf, pty_argv, cmd, modes, false, NULL, NULL); *realhost = dupstr(""); return NULL; @@ -1561,52 +1568,16 @@ ptrlen pty_backend_exit_signame(Backend *be, char **aux_msg) if (sig < 0) return PTRLEN_LITERAL(""); -#define TRANSLATE_SIGNAL(s) do \ - { \ + #define SIGNAL_SUB(s) { \ if (sig == SIG ## s) \ return PTRLEN_LITERAL(#s); \ - } while (0) - -#ifdef SIGABRT - TRANSLATE_SIGNAL(ABRT); -#endif -#ifdef SIGALRM - TRANSLATE_SIGNAL(ALRM); -#endif -#ifdef SIGFPE - TRANSLATE_SIGNAL(FPE); -#endif -#ifdef SIGHUP - TRANSLATE_SIGNAL(HUP); -#endif -#ifdef SIGILL - TRANSLATE_SIGNAL(ILL); -#endif -#ifdef SIGINT - TRANSLATE_SIGNAL(INT); -#endif -#ifdef SIGKILL - TRANSLATE_SIGNAL(KILL); -#endif -#ifdef SIGPIPE - TRANSLATE_SIGNAL(PIPE); -#endif -#ifdef SIGQUIT - TRANSLATE_SIGNAL(QUIT); -#endif -#ifdef SIGSEGV - TRANSLATE_SIGNAL(SEGV); -#endif -#ifdef SIGTERM - TRANSLATE_SIGNAL(TERM); -#endif -#ifdef SIGUSR1 - TRANSLATE_SIGNAL(USR1); -#endif -#ifdef SIGUSR2 - TRANSLATE_SIGNAL(USR2); -#endif -#undef TRANSLATE_SIGNAL + } + #define SIGNAL_MAIN(s, desc) SIGNAL_SUB(s) + #define SIGNALS_LOCAL_ONLY + #include "sshsignals.h" + #undef SIGNAL_MAIN + #undef SIGNAL_SUB + #undef SIGNALS_LOCAL_ONLY *aux_msg = dupprintf("untranslatable signal number %d: %s", sig, strsignal(sig)); @@ -1619,24 +1590,23 @@ static int pty_cfg_info(Backend *be) return 0; } -const struct BackendVtable pty_backend = { - pty_init, - pty_free, - pty_reconfig, - pty_send, - pty_sendbuffer, - pty_size, - pty_special, - pty_get_specials, - pty_connected, - pty_exitcode, - pty_sendok, - pty_ldisc, - pty_provide_ldisc, - pty_unthrottle, - pty_cfg_info, - NULL /* test_for_upstream */, - "pty", - -1, - 0 +const BackendVtable pty_backend = { + .init = pty_init, + .free = pty_free, + .reconfig = pty_reconfig, + .send = pty_send, + .sendbuffer = pty_sendbuffer, + .size = pty_size, + .special = pty_special, + .get_specials = pty_get_specials, + .connected = pty_connected, + .exitcode = pty_exitcode, + .sendok = pty_sendok, + .ldisc_option_state = pty_ldisc, + .provide_ldisc = pty_provide_ldisc, + .unthrottle = pty_unthrottle, + .cfg_info = pty_cfg_info, + .id = "pty", + .displayname = "pty", + .protocol = -1, }; diff --git a/unix/uxputty.c b/unix/uxputty.c index 8142bdd..7a80808 100644 --- a/unix/uxputty.c +++ b/unix/uxputty.c @@ -13,6 +13,7 @@ #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" +#include "ssh.h" #include "storage.h" #include "gtkcompat.h" @@ -47,7 +48,7 @@ const struct BackendVtable *select_backend(Conf *conf) void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx) { - char *title = dupcat(appname, " Configuration", NULL); + char *title = dupcat(appname, " Configuration"); create_config_box(title, conf, false, 0, after, afterctx); sfree(title); } @@ -55,11 +56,6 @@ void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx) const bool use_event_log = true, new_session = true, saved_sessions = true; const bool dup_check_launchable = true; -char *make_default_wintitle(char *hostname) -{ - return dupcat(hostname, " - ", appname, NULL); -} - /* * X11-forwarding-related things suitable for Gtk app. */ @@ -76,18 +72,21 @@ char *platform_get_x_display(void) { const bool share_can_be_downstream = true; const bool share_can_be_upstream = true; +const unsigned cmdline_tooltype = + TOOLTYPE_HOST_ARG | + TOOLTYPE_PORT_ARG | + TOOLTYPE_NO_VERBOSE_OPTION; + void setup(bool single) { sk_init(); - flags = FLAG_VERBOSE | FLAG_INTERACTIVE; - cmdline_tooltype |= TOOLTYPE_HOST_ARG | TOOLTYPE_PORT_ARG; - default_protocol = be_default_protocol; + settings_set_default_protocol(be_default_protocol); /* Find the appropriate default port. */ { const struct BackendVtable *vt = - backend_vt_from_proto(default_protocol); - default_port = 0; /* illegal */ + backend_vt_from_proto(be_default_protocol); + settings_set_default_port(0); /* illegal */ if (vt) - default_port = vt->default_port; + settings_set_default_port(vt->default_port); } } diff --git a/unix/uxser.c b/unix/uxser.c index f2b1cad..d4a1e0b 100644 --- a/unix/uxser.c +++ b/unix/uxser.c @@ -63,14 +63,15 @@ static void serial_select_result(int fd, int event); static void serial_uxsel_setup(Serial *serial); static void serial_try_write(Serial *serial); -static const char *serial_configure(Serial *serial, Conf *conf) +static char *serial_configure(Serial *serial, Conf *conf) { struct termios options; int bflag, bval, speed, flow, parity; const char *str; if (serial->fd < 0) - return "Unable to reconfigure already-closed serial connection"; + return dupstr("Unable to reconfigure already-closed " + "serial connection"); tcgetattr(serial->fd, &options); @@ -189,7 +190,8 @@ static const char *serial_configure(Serial *serial, Conf *conf) case 6: options.c_cflag |= CS6; break; case 7: options.c_cflag |= CS7; break; case 8: options.c_cflag |= CS8; break; - default: return "Invalid number of data bits (need 5, 6, 7 or 8)"; + default: return dupstr("Invalid number of data bits " + "(need 5, 6, 7 or 8)"); } logeventf(serial->logctx, "Configuring %d data bits", conf_get_int(conf, CONF_serdatabits)); @@ -199,8 +201,8 @@ static const char *serial_configure(Serial *serial, Conf *conf) } else { options.c_cflag &= ~CSTOPB; } - logeventf(serial->logctx, "Configuring %d stop bits", - (options.c_cflag & CSTOPB ? 2 : 1)); + logeventf(serial->logctx, "Configuring %s", + (options.c_cflag & CSTOPB ? "2 stop bits" : "1 stop bit")); options.c_iflag &= ~(IXON|IXOFF); #ifdef CRTSCTS @@ -266,7 +268,7 @@ static const char *serial_configure(Serial *serial, Conf *conf) options.c_cc[VTIME] = 0; if (tcsetattr(serial->fd, TCSANOW, &options) < 0) - return "Unable to configure serial port"; + return dupprintf("Configuring serial port: %s", strerror(errno)); return NULL; } @@ -279,20 +281,20 @@ static const char *serial_configure(Serial *serial, Conf *conf) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *serial_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, char **realhost, - bool nodelay, bool keepalive) +static char *serial_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { Serial *serial; - const char *err; + char *err; char *line; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); serial = snew(Serial); - serial->backend.vt = &serial_backend; + serial->backend.vt = vt; *backend_handle = &serial->backend; serial->seat = seat; @@ -306,7 +308,8 @@ static const char *serial_init(Seat *seat, Backend **backend_handle, serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (serial->fd < 0) - return "Unable to open serial port"; + return dupprintf("Opening serial port '%s': %s", + line, strerror(errno)); cloexec(serial->fd); @@ -333,6 +336,7 @@ static const char *serial_init(Seat *seat, Backend **backend_handle, static void serial_close(Serial *serial) { if (serial->fd >= 0) { + uxsel_del(serial->fd); close(serial->fd); serial->fd = -1; } @@ -353,10 +357,14 @@ static void serial_reconfig(Backend *be, Conf *conf) { Serial *serial = container_of(be, Serial, backend); - /* - * FIXME: what should we do if this returns an error? - */ - serial_configure(serial, conf); + char *err = serial_configure(serial, conf); + if (err) { + /* + * FIXME: apart from freeing the dynamically allocated + * message, what should we do if this returns an error? + */ + sfree(err); + } } static void serial_select_result(int fd, int event) @@ -559,24 +567,29 @@ static int serial_cfg_info(Backend *be) return 0; } -const struct BackendVtable serial_backend = { - serial_init, - serial_free, - serial_reconfig, - serial_send, - serial_sendbuffer, - serial_size, - serial_special, - serial_get_specials, - serial_connected, - serial_exitcode, - serial_sendok, - serial_ldisc, - serial_provide_ldisc, - serial_unthrottle, - serial_cfg_info, - NULL /* test_for_upstream */, - "serial", - PROT_SERIAL, - 0 +const BackendVtable serial_backend = { + .init = serial_init, + .free = serial_free, + .reconfig = serial_reconfig, + .send = serial_send, + .sendbuffer = serial_sendbuffer, + .size = serial_size, + .special = serial_special, + .get_specials = serial_get_specials, + .connected = serial_connected, + .exitcode = serial_exitcode, + .sendok = serial_sendok, + .ldisc_option_state = serial_ldisc, + .provide_ldisc = serial_provide_ldisc, + .unthrottle = serial_unthrottle, + .cfg_info = serial_cfg_info, + .id = "serial", + .displayname = "Serial", + .protocol = PROT_SERIAL, + .serial_parity_mask = ((1 << SER_PAR_NONE) | + (1 << SER_PAR_ODD) | + (1 << SER_PAR_EVEN)), + .serial_flow_mask = ((1 << SER_FLOW_NONE) | + (1 << SER_FLOW_XONXOFF) | + (1 << SER_FLOW_RTSCTS)), }; diff --git a/unix/uxserver.c b/unix/uxserver.c index b063e50..448c651 100644 --- a/unix/uxserver.c +++ b/unix/uxserver.c @@ -38,7 +38,6 @@ #include #include -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "mpint.h" #include "ssh.h" @@ -96,12 +95,6 @@ char *x_get_default(const char *key) return NULL; /* this is a stub */ } -/* - * Our selects are synchronous, so these functions are empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - void old_keyfile_warning(void) { } void timer_change_notify(unsigned long next) @@ -110,6 +103,11 @@ void timer_change_notify(unsigned long next) char *platform_get_x_display(void) { return NULL; } +void make_unix_sftp_filehandle_key(void *data, size_t size) +{ + random_read(data, size); +} + static bool verbose; struct AuthPolicyShared { @@ -160,9 +158,10 @@ static int server_askappend( } static const LogPolicyVtable server_logpolicy_vt = { - server_eventlog, - server_askappend, - server_logging_error, + .eventlog = server_eventlog, + .askappend = server_askappend, + .logging_error = server_logging_error, + .verbose = null_lp_verbose_no, }; struct AuthPolicy_ssh1_pubkey { @@ -312,7 +311,7 @@ static void show_help(FILE *fp) safety_warning(fp); fputs("\n" "usage: uppity [options]\n" - "options: --listen PORT listen to a port on localhost\n" + "options: --listen [PORT|PATH] listen to a port on localhost, or Unix socket\n" " --listen-once (with --listen) stop after one " "connection\n" " --hostkey KEY SSH host key (need at least one)\n" @@ -344,7 +343,7 @@ static void show_help(FILE *fp) " --exitsignum send buggy numeric \"exit-signal\" " "message\n" " --verbose print event log messages to standard " - "output\n" + "error\n" " --sshlog FILE write SSH packet log to FILE\n" " --sshrawlog FILE write SSH packets + raw data log" " to FILE\n" @@ -453,7 +452,7 @@ static Plug *server_conn_plug( &inst->ap, &inst->logpolicy, &unix_live_sftpserver_vt); } -static void server_log(Plug *plug, int type, SockAddr *addr, int port, +static void server_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { log_to_stderr((unsigned)-1, error_msg); @@ -490,7 +489,7 @@ static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) SocketPeerInfo *pi = sk_peer_info(s); - if (!sk_peer_trusted(s)) { + if (pi->addressfamily != ADDRTYPE_LOCAL && !sk_peer_trusted(s)) { fprintf(stderr, "rejected connection from %s (untrustworthy peer)\n", pi->log_text); sk_free_peer_info(pi); @@ -510,21 +509,15 @@ static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) } static const PlugVtable server_plugvt = { - server_log, - server_closing, - NULL, /* recv */ - NULL, /* send */ - server_accepting + .log = server_log, + .closing = server_closing, + .accepting = server_accepting, }; int main(int argc, char **argv) { - int *fdlist; - int fd; - int i, fdstate; - size_t fdsize; - unsigned long now; int listen_port = -1; + const char *listen_socket = NULL; ssh_key **hostkeys = NULL; size_t nhostkeys = 0, hostkeysize = 0; @@ -540,6 +533,7 @@ int main(int argc, char **argv) memset(&ssc, 0, sizeof(ssc)); + ssc.application_name = "Uppity"; ssc.session_starting_dir = getenv("HOME"); ssc.ssh1_cipher_mask = SSH1_SUPPORTED_CIPHER_MASK; ssc.ssh1_allow_compression = true; @@ -567,7 +561,13 @@ int main(int argc, char **argv) } else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) { verbose = true; } else if (longoptarg(arg, "--listen", &val, &argc, &argv)) { - listen_port = atoi(val); + if (val[0] == '/') { + listen_port = -1; + listen_socket = val; + } else { + listen_port = atoi(val); + listen_socket = NULL; + } } else if (!strcmp(arg, "--listen-once")) { listen_once = true; } else if (longoptarg(arg, "--hostkey", &val, &argc, &argv)) { @@ -581,7 +581,7 @@ int main(int argc, char **argv) if (keytype == SSH_KEYTYPE_SSH2) { ssh2_userkey *uk; ssh_key *key; - uk = ssh2_load_userkey(keyfile, NULL, &error); + uk = ppk_load_f(keyfile, NULL, &error); filename_free(keyfile); if (!uk || !uk->key) { fprintf(stderr, "%s: unable to load host key '%s': " @@ -598,7 +598,7 @@ int main(int argc, char **argv) sfree(uk->comment); sfree(uk); - for (i = 0; i < nhostkeys; i++) + for (int i = 0; i < nhostkeys; i++) if (ssh_key_alg(hostkeys[i]) == ssh_key_alg(key)) { fprintf(stderr, "%s: host key '%s' duplicates key " "type %s\n", appname, val, @@ -615,7 +615,7 @@ int main(int argc, char **argv) exit(1); } hostkey1 = snew(RSAKey); - if (!rsa_ssh1_loadkey(keyfile, hostkey1, NULL, &error)) { + if (!rsa1_load_f(keyfile, hostkey1, NULL, &error)) { fprintf(stderr, "%s: unable to load host key '%s': " "%s\n", appname, val, error); exit(1); @@ -647,8 +647,7 @@ int main(int argc, char **argv) ssc.rsa_kex_key = snew(RSAKey); } - if (!rsa_ssh1_loadkey(keyfile, ssc.rsa_kex_key, - NULL, &error)) { + if (!rsa1_load_f(keyfile, ssc.rsa_kex_key, NULL, &error)) { fprintf(stderr, "%s: unable to load RSA kex key '%s': " "%s\n", appname, val, error); exit(1); @@ -669,8 +668,8 @@ int main(int argc, char **argv) struct AuthPolicy_ssh2_pubkey *node; void *blob; - if (!ssh2_userkey_loadpub(keyfile, NULL, BinarySink_UPCAST(sb), - NULL, &error)) { + if (!ppk_loadpub_f(keyfile, NULL, BinarySink_UPCAST(sb), + NULL, &error)) { fprintf(stderr, "%s: unable to load user key '%s': " "%s\n", appname, val, error); exit(1); @@ -690,8 +689,8 @@ int main(int argc, char **argv) BinarySource src[1]; struct AuthPolicy_ssh1_pubkey *node; - if (!rsa_ssh1_loadpub(keyfile, BinarySink_UPCAST(sb), - NULL, &error)) { + if (!rsa1_loadpub_f(keyfile, BinarySink_UPCAST(sb), + NULL, &error)) { fprintf(stderr, "%s: unable to load user key '%s': " "%s\n", appname, val, error); exit(1); @@ -782,6 +781,10 @@ int main(int argc, char **argv) filename_free(logfile); conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW); conf_set_int(conf, CONF_logxfovr, LGXF_OVR); + } else if (!strcmp(arg, "--pretend-to-accept-any-pubkey")) { + ssc.stunt_pretend_to_accept_any_pubkey = true; + } else if (!strcmp(arg, "--open-unconditional-agent-socket")) { + ssc.stunt_open_unconditional_agent_socket = true; } else { fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg); exit(1); @@ -793,9 +796,6 @@ int main(int argc, char **argv) exit(1); } - fdlist = NULL; - fdsize = 0; - random_ref(); /* @@ -816,14 +816,24 @@ int main(int argc, char **argv) scfg.ap_shared = &aps; scfg.next_id = 0; - if (listen_port >= 0) { + if (listen_port >= 0 || listen_socket) { listening = true; scfg.listening_plug.vt = &server_plugvt; - scfg.listening_socket = sk_newlistener( - NULL, listen_port, &scfg.listening_plug, true, ADDRTYPE_UNSPEC); + char *msg; + if (listen_port >= 0) { + scfg.listening_socket = sk_newlistener( + NULL, listen_port, &scfg.listening_plug, true, + ADDRTYPE_UNSPEC); + msg = dupprintf("%s: listening on port %d", + appname, listen_port); + } else { + SockAddr *addr = unix_sock_addr(listen_socket); + scfg.listening_socket = new_unix_listener( + addr, &scfg.listening_plug); + msg = dupprintf("%s: listening on Unix socket %s", + appname, listen_socket); + } - char *msg = dupprintf("%s: listening on port %d", - appname, listen_port); log_to_stderr(-1, msg); sfree(msg); } else { @@ -833,90 +843,8 @@ int main(int argc, char **argv) log_to_stderr(inst->id, "speaking SSH on stdio"); } - now = GETTICKCOUNT(); - - pollwrapper *pw = pollwrap_new(); - while (!finished) { - int rwx; - int ret; - unsigned long next; - - pollwrap_clear(pw); + cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check, + cliloop_always_continue, NULL); - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - /* - * Add all currently open fds to the select sets, and store - * them in fdlist as well. - */ - int fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - } else if (run_timers(now, &next)) { - do { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } while (ret < 0 && errno == EINTR); - } else { - ret = pollwrap_poll_endless(pw); - } - - if (ret < 0 && errno == EINTR) - continue; - - if (ret < 0) { - perror("poll"); - exit(1); - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - run_toplevel_callbacks(); - } - exit(0); + return 0; } diff --git a/unix/uxsftp.c b/unix/uxsftp.c index de838f8..89a81c9 100644 --- a/unix/uxsftp.c +++ b/unix/uxsftp.c @@ -21,13 +21,6 @@ #include #endif -/* - * In PSFTP our selects are synchronous, so these functions are - * empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - char *x_get_default(const char *key) { return NULL; /* this is a stub */ @@ -460,121 +453,60 @@ char *dir_file_cat(const char *dir, const char *file) ptrlen dir_pl = ptrlen_from_asciz(dir); return dupcat( dir, ptrlen_endswith(dir_pl, PTRLEN_LITERAL("/"), NULL) ? "" : "/", - file, NULL); + file); } /* * Do a select() between all currently active network fds and - * optionally stdin. + * optionally stdin, using cli_main_loop. */ -static int ssh_sftp_do_select(bool include_stdin, bool no_fds_ok) -{ - int i, *fdlist; - size_t fdsize; - int fd, fdcount, fdstate, rwx, ret; - unsigned long now = GETTICKCOUNT(); - unsigned long next; - bool done_something = false; - - fdlist = NULL; - fdsize = 0; - - pollwrapper *pw = pollwrap_new(); - - do { - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - if (i < 1 && !no_fds_ok && !toplevel_callback_pending()) { - pollwrap_free(pw); - return -1; /* doom */ - } - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - pollwrap_clear(pw); - - /* - * Add all currently open fds to the select sets, and store - * them in fdlist as well. - */ - fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - if (include_stdin) - pollwrap_add_fd_rwx(pw, 0, SELECT_R); - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - if (ret == 0) - done_something |= run_toplevel_callbacks(); - } else if (run_timers(now, &next)) { - do { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } while (ret < 0 && errno == EINTR); - } else { - do { - ret = pollwrap_poll_endless(pw); - } while (ret < 0 && errno == EINTR); - } - } while (ret == 0 && !done_something); +struct ssh_sftp_mainloop_ctx { + bool include_stdin, no_fds_ok; + int toret; +}; +static bool ssh_sftp_pw_setup(void *vctx, pollwrapper *pw) +{ + struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; + int fdstate, rwx; - if (ret < 0) { - perror("poll"); - exit(1); + if (!ctx->no_fds_ok && !toplevel_callback_pending() && + first_fd(&fdstate, &rwx) < 0) { + ctx->toret = -1; + return false; /* terminate cli_main_loop */ } - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } + if (ctx->include_stdin) + pollwrap_add_fd_rwx(pw, 0, SELECT_R); - sfree(fdlist); + return true; +} +static void ssh_sftp_pw_check(void *vctx, pollwrapper *pw) +{ + struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; + + if (ctx->include_stdin && pollwrap_check_fd_rwx(pw, 0, SELECT_R)) + ctx->toret = 1; +} +static bool ssh_sftp_mainloop_continue(void *vctx, bool found_any_fd, + bool ran_any_callback) +{ + struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; + if (ctx->toret != 0 || found_any_fd || ran_any_callback) + return false; /* finish the loop */ + return true; +} +static int ssh_sftp_do_select(bool include_stdin, bool no_fds_ok) +{ + struct ssh_sftp_mainloop_ctx ctx[1]; + ctx->include_stdin = include_stdin; + ctx->no_fds_ok = no_fds_ok; + ctx->toret = 0; - run_toplevel_callbacks(); + cli_main_loop(ssh_sftp_pw_setup, ssh_sftp_pw_check, + ssh_sftp_mainloop_continue, ctx); - int toret = pollwrap_check_fd_rwx(pw, 0, SELECT_R) ? 1 : 0; - pollwrap_free(pw); - return toret; + return ctx->toret; } /* @@ -631,7 +563,7 @@ char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) void frontend_net_error_pending(void) {} -void platform_psftp_pre_conn_setup(void) {} +void platform_psftp_pre_conn_setup(LogPolicy *lp) {} const bool buildinfo_gtk_relevant = false; diff --git a/unix/uxsftpserver.c b/unix/uxsftpserver.c index a90344e..acefe9b 100644 --- a/unix/uxsftpserver.c +++ b/unix/uxsftpserver.c @@ -20,6 +20,7 @@ #include "putty.h" #include "ssh.h" +#include "sshserver.h" #include "sftp.h" #include "tree234.h" @@ -65,7 +66,7 @@ static SftpServer *uss_new(const SftpServerVtable *vt) uss->dirhandles = newtree234(uss_dirhandle_cmp); uss->srv.vt = vt; - random_read(uss->handlekey, sizeof(uss->handlekey)); + make_unix_sftp_filehandle_key(uss->handlekey, sizeof(uss->handlekey)); return &uss->srv; } @@ -681,22 +682,22 @@ static void uss_readdir(SftpServer *srv, SftpReplyBuilder *reply, } } -const struct SftpServerVtable unix_live_sftpserver_vt = { - uss_new, - uss_free, - uss_realpath, - uss_open, - uss_opendir, - uss_close, - uss_mkdir, - uss_rmdir, - uss_remove, - uss_rename, - uss_stat, - uss_fstat, - uss_setstat, - uss_fsetstat, - uss_read, - uss_write, - uss_readdir, +const SftpServerVtable unix_live_sftpserver_vt = { + .new = uss_new, + .free = uss_free, + .realpath = uss_realpath, + .open = uss_open, + .opendir = uss_opendir, + .close = uss_close, + .mkdir = uss_mkdir, + .rmdir = uss_rmdir, + .remove = uss_remove, + .rename = uss_rename, + .stat = uss_stat, + .fstat = uss_fstat, + .setstat = uss_setstat, + .fsetstat = uss_fsetstat, + .read = uss_read, + .write = uss_write, + .readdir = uss_readdir, }; diff --git a/unix/uxshare.c b/unix/uxshare.c index 0c5265b..f1ef201 100644 --- a/unix/uxshare.c +++ b/unix/uxshare.c @@ -273,7 +273,7 @@ int platform_ssh_share(const char *pi_name, Conf *conf, /* * Acquire a lock on a file in that directory. */ - lockname = dupcat(dirname, "/lock", (char *)NULL); + lockname = dupcat(dirname, "/lock"); lockfd = open(lockname, O_CREAT | O_RDWR | O_TRUNC, 0600); if (lockfd < 0) { *logtext = dupprintf("%s: open: %s", lockname, strerror(errno)); @@ -348,11 +348,11 @@ void platform_ssh_share_cleanup(const char *name) return; } - filename = dupcat(dirname, "/socket", (char *)NULL); + filename = dupcat(dirname, "/socket"); remove(filename); sfree(filename); - filename = dupcat(dirname, "/lock", (char *)NULL); + filename = dupcat(dirname, "/lock"); remove(filename); sfree(filename); diff --git a/unix/uxsocks.c b/unix/uxsocks.c new file mode 100644 index 0000000..91613af --- /dev/null +++ b/unix/uxsocks.c @@ -0,0 +1,178 @@ +/* + * Main program for Unix psocks. + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "putty.h" +#include "ssh.h" +#include "psocks.h" + +const bool buildinfo_gtk_relevant = false; + +typedef struct PsocksDataSinkPopen { + stdio_sink sink[2]; + PsocksDataSink pds; +} PsocksDataSinkPopen; + +static void popen_free(PsocksDataSink *pds) +{ + PsocksDataSinkPopen *pdsp = container_of(pds, PsocksDataSinkPopen, pds); + for (size_t i = 0; i < 2; i++) + pclose(pdsp->sink[i].fp); + sfree(pdsp); +} + +static PsocksDataSink *open_pipes( + const char *cmd, const char *const *direction_args, + const char *index_arg, char **err) +{ + FILE *fp[2]; + char *errmsg = NULL; + + for (size_t i = 0; i < 2; i++) { + /* No escaping needed: the provided command is already + * shell-quoted, and our extra arguments are simple */ + char *command = dupprintf("%s %s %s", cmd, + direction_args[i], index_arg); + + fp[i] = popen(command, "w"); + sfree(command); + + if (!fp[i]) { + if (!errmsg) + errmsg = dupprintf("%s", strerror(errno)); + } + } + + if (errmsg) { + for (size_t i = 0; i < 2; i++) + if (fp[i]) + pclose(fp[i]); + *err = errmsg; + return NULL; + } + + PsocksDataSinkPopen *pdsp = snew(PsocksDataSinkPopen); + + for (size_t i = 0; i < 2; i++) { + setvbuf(fp[i], NULL, _IONBF, 0); + stdio_sink_init(&pdsp->sink[i], fp[i]); + pdsp->pds.s[i] = BinarySink_UPCAST(&pdsp->sink[i]); + } + + pdsp->pds.free = popen_free; + + return &pdsp->pds; +} + +static int signalpipe[2] = { -1, -1 }; +static void sigchld(int signum) +{ + if (write(signalpipe[1], "x", 1) <= 0) + /* not much we can do about it */; +} + +static pid_t subcommand_pid = -1; + +static bool still_running = true; + +static void start_subcommand(strbuf *args) +{ + pid_t pid; + + /* + * Set up the pipe we'll use to tell us about SIGCHLD. + */ + if (pipe(signalpipe) < 0) { + perror("pipe"); + exit(1); + } + putty_signal(SIGCHLD, sigchld); + + /* + * Make an array of argument pointers that execvp will like. + */ + size_t nargs = 0; + for (size_t i = 0; i < args->len; i++) + if (args->s[i] == '\0') + nargs++; + + char **exec_args = snewn(nargs + 1, char *); + char *p = args->s; + for (size_t a = 0; a < nargs; a++) { + exec_args[a] = p; + size_t len = strlen(p); + assert(len < args->len - (p - args->s)); + p += 1 + len; + } + exec_args[nargs] = NULL; + + pid = fork(); + if (pid < 0) { + perror("fork"); + exit(1); + } else if (pid == 0) { + execvp(exec_args[0], exec_args); + perror("exec"); + _exit(127); + } else { + subcommand_pid = pid; + sfree(exec_args); + } +} + +static const PsocksPlatform platform = { + open_pipes, + start_subcommand, +}; + +static bool psocks_pw_setup(void *ctx, pollwrapper *pw) +{ + if (signalpipe[0] >= 0) + pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); + return true; +} + +static void psocks_pw_check(void *ctx, pollwrapper *pw) +{ + if (signalpipe[0] >= 0 && + pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { + while (true) { + int status; + pid_t pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) + break; + if (pid == subcommand_pid) + still_running = false; + } + } +} + +static bool psocks_continue(void *ctx, bool found_any_fd, + bool ran_any_callback) +{ + return still_running; +} + +typedef bool (*cliloop_continue_t)(void *ctx, bool found_any_fd, + bool ran_any_callback); + +int main(int argc, char **argv) +{ + psocks_state *ps = psocks_new(&platform); + psocks_cmdline(ps, argc, argv); + + sk_init(); + uxsel_init(); + psocks_start(ps); + + cli_main_loop(psocks_pw_setup, psocks_pw_check, psocks_continue, NULL); +} diff --git a/unix/uxstore.c b/unix/uxstore.c index 250db9a..9db713d 100644 --- a/unix/uxstore.c +++ b/unix/uxstore.c @@ -449,7 +449,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name) * provided name string (e.g. "Font") to a suffixed one * ("FontName"). */ - char *suffname = dupcat(name, "Name", NULL); + char *suffname = dupcat(name, "Name"); char *tmp; if ((tmp = read_setting_s(handle, suffname)) != NULL) { @@ -463,7 +463,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name) /* Fall back to old-style name. */ tmp = read_setting_s(handle, name); if (tmp && *tmp) { - char *tmp2 = dupcat("server:", tmp, NULL); + char *tmp2 = dupcat("server:", tmp); FontSpec *fs = fontspec_new(tmp2); sfree(tmp2); sfree(tmp); @@ -491,7 +491,7 @@ void write_setting_fontspec(settings_w *handle, const char *name, FontSpec *fs) * writing our settings back out we simply always generate the * new-style name. */ - char *suffname = dupcat(name, "Name", NULL); + char *suffname = dupcat(name, "Name"); write_setting_s(handle, suffname, fs->name); sfree(suffname); } @@ -564,7 +564,7 @@ bool enum_settings_next(settings_e *handle, strbuf *out) size_t baselen = fullpath->len; while ( (de = readdir(handle->dp)) != NULL ) { - fullpath->len = baselen; + strbuf_shrink_to(fullpath, baselen); put_datapl(fullpath, ptrlen_from_asciz(de->d_name)); if (stat(fullpath->s, &st) < 0 || !S_ISREG(st.st_mode)) diff --git a/unix/uxucs.c b/unix/uxucs.c index a3d6f5b..c1d76a4 100644 --- a/unix/uxucs.c +++ b/unix/uxucs.c @@ -68,7 +68,7 @@ int wc_to_mb(int codepage, int flags, const wchar_t *wcstr, int wclen, memset(&state, 0, sizeof state); while (wclen > 0) { - int i = wcrtomb(output, wcstr[0], &state); + size_t i = wcrtomb(output, wcstr[0], &state); if (i == (size_t)-1 || i > n - mblen) break; memcpy(mbstr+n, output, i); diff --git a/unix/uxutils.c b/unix/uxutils.c index 7b63842..3a04c1b 100644 --- a/unix/uxutils.c +++ b/unix/uxutils.c @@ -1,11 +1,9 @@ #include "putty.h" #include "ssh.h" -#if defined __linux__ && (defined __arm__ || defined __aarch64__) && \ - HAVE_SYS_AUXV_H && HAVE_ASM_HWCAP_H +#include "uxutils.h" -#include -#include +#if defined __arm__ || defined __aarch64__ bool platform_aes_hw_available(void) { @@ -13,6 +11,11 @@ bool platform_aes_hw_available(void) return getauxval(AT_HWCAP) & HWCAP_AES; #elif defined HWCAP2_AES return getauxval(AT_HWCAP2) & HWCAP2_AES; +#elif defined __APPLE__ + /* M1 macOS defines no optional sysctl flag indicating presence of + * the AES extension, which I assume to be because it's always + * present */ + return true; #else return false; #endif @@ -24,6 +27,9 @@ bool platform_sha256_hw_available(void) return getauxval(AT_HWCAP) & HWCAP_SHA2; #elif defined HWCAP2_SHA2 return getauxval(AT_HWCAP2) & HWCAP2_SHA2; +#elif defined __APPLE__ + /* Assume always present on M1 macOS, similarly to AES */ + return true; #else return false; #endif @@ -35,26 +41,25 @@ bool platform_sha1_hw_available(void) return getauxval(AT_HWCAP) & HWCAP_SHA1; #elif defined HWCAP2_SHA1 return getauxval(AT_HWCAP2) & HWCAP2_SHA1; +#elif defined __APPLE__ + /* Assume always present on M1 macOS, similarly to AES */ + return true; #else return false; #endif } -#else - -bool platform_aes_hw_available(void) -{ - return false; -} - -bool platform_sha256_hw_available(void) -{ - return false; -} - -bool platform_sha1_hw_available(void) +bool platform_sha512_hw_available(void) { +#if defined HWCAP_SHA512 + return getauxval(AT_HWCAP) & HWCAP_SHA512; +#elif defined HWCAP2_SHA512 + return getauxval(AT_HWCAP2) & HWCAP2_SHA512; +#elif defined __APPLE__ + return test_sysctl_flag("hw.optional.armv8_2_sha512"); +#else return false; +#endif } -#endif +#endif /* defined __arm__ || defined __aarch64__ */ diff --git a/unix/uxutils.h b/unix/uxutils.h new file mode 100644 index 0000000..c9acff5 --- /dev/null +++ b/unix/uxutils.h @@ -0,0 +1,65 @@ +/* + * uxutils.h: header included only by uxutils.c. + * + * The only reason this is a header file instead of a source file is + * so that I can define 'static inline' functions which may or may not + * be used, without provoking a compiler warning when I turn out not + * to use them in the subsequent source file. + */ + +#ifndef PUTTY_UXUTILS_H +#define PUTTY_UXUTILS_H + +#if defined __APPLE__ +#ifdef HAVE_SYS_SYSCTL_H +#include +#endif +#endif /* defined __APPLE__ */ + +#if defined __arm__ || defined __aarch64__ + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_AUXV_H +#include +#endif + +#ifdef HAVE_ASM_HWCAP_H +#include +#endif + +#if defined HAVE_GETAUXVAL +/* No code needed: getauxval has just the API we want already */ +#elif defined HAVE_ELF_AUX_INFO +/* Implement the simple getauxval API in terms of FreeBSD elf_aux_info */ +static inline u_long getauxval(int which) +{ + u_long toret; + if (elf_aux_info(which, &toret, sizeof(toret)) != 0) + return 0; /* elf_aux_info didn't work */ + return toret; +} +#else +/* Implement a stub getauxval which returns no capabilities */ +static inline u_long getauxval(int which) { return 0; } +#endif + +#endif /* defined __arm__ || defined __aarch64__ */ + +#if defined __APPLE__ +static inline bool test_sysctl_flag(const char *flagname) +{ +#ifdef HAVE_SYSCTLBYNAME + int value; + size_t size = sizeof(value); + return (sysctlbyname(flagname, &value, &size, NULL, 0) == 0 && + size == sizeof(value) && value != 0); +#else /* HAVE_SYSCTLBYNAME */ + return false; +#endif /* HAVE_SYSCTLBYNAME */ +} +#endif /* defined __APPLE__ */ + +#endif /* PUTTY_UXUTILS_H */ diff --git a/unix/x11misc.c b/unix/x11misc.c index 813773c..e1fd190 100644 --- a/unix/x11misc.c +++ b/unix/x11misc.c @@ -32,9 +32,8 @@ struct x11_err_to_ignore { unsigned long serial; }; -struct x11_err_to_ignore *errs; - -size_t nerrs, errsize; +static struct x11_err_to_ignore *errs; +static size_t nerrs, errsize; static int x11_error_handler(Display *thisdisp, XErrorEvent *err) { diff --git a/utils.c b/utils.c index 1819396..30b326e 100644 --- a/utils.c +++ b/utils.c @@ -17,6 +17,7 @@ #include "defs.h" #include "misc.h" +#include "ssh.h" /* * Parse a string block size specification. This is approximately a @@ -182,10 +183,21 @@ int main(void) printf("passed %d failed %d total %d\n", passes, fails, passes+fails); return fails != 0 ? 1 : 0; } + /* Stubs to stop the rest of this module causing compile failures. */ -void modalfatalbox(const char *fmt, ...) {} -int conf_get_int(Conf *conf, int primary) { return 0; } -char *conf_get_str(Conf *conf, int primary) { return NULL; } +static NORETURN void fatal_error(const char *p, ...) +{ + va_list ap; + fprintf(stderr, "host_string_test: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +void out_of_memory(void) { fatal_error("out of memory"); } + #endif /* TEST_HOST_STRFOO */ /* @@ -249,7 +261,7 @@ char *dupstr(const char *s) } /* Allocate the concatenation of N strings. Terminate arg list with NULL. */ -char *dupcat(const char *s1, ...) +char *dupcat_fn(const char *s1, ...) { int len; char *p, *q, *sn; @@ -418,6 +430,29 @@ void *strbuf_append(strbuf *buf_o, size_t len) return toret; } +void strbuf_shrink_to(strbuf *buf, size_t new_len) +{ + assert(new_len <= buf->len); + buf->len = new_len; + buf->s[buf->len] = '\0'; +} + +void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove) +{ + assert(amount_to_remove <= buf->len); + buf->len -= amount_to_remove; + buf->s[buf->len] = '\0'; +} + +bool strbuf_chomp(strbuf *buf, char char_to_remove) +{ + if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) { + strbuf_shrink_by(buf, 1); + return true; + } + return false; +} + static void strbuf_BinarySink_write( BinarySink *bs, const void *data, size_t len) { @@ -824,7 +859,7 @@ void debug_memdump(const void *buf, int len, bool L) dputs(" "); /* 3 spaces */ foo[i] = ' '; } else { - debug_printf("%c%02.2x", + debug_printf("%c%2.2x", &p[i] != (unsigned char *) buf && i % 4 ? '.' : ' ', p[i] ); @@ -985,7 +1020,7 @@ char *mkstr(ptrlen pl) bool strstartswith(const char *s, const char *t) { - return !memcmp(s, t, strlen(t)); + return !strncmp(s, t, strlen(t)); } bool strendswith(const char *s, const char *t) @@ -1015,3 +1050,73 @@ size_t encode_utf8(void *output, unsigned long ch) } return p - start; } + +void write_c_string_literal(FILE *fp, ptrlen str) +{ + for (const char *p = str.ptr; p < (const char *)str.ptr + str.len; p++) { + char c = *p; + + if (c == '\n') + fputs("\\n", fp); + else if (c == '\r') + fputs("\\r", fp); + else if (c == '\t') + fputs("\\t", fp); + else if (c == '\b') + fputs("\\b", fp); + else if (c == '\\') + fputs("\\\\", fp); + else if (c == '"') + fputs("\\\"", fp); + else if (c >= 32 && c <= 126) + fputc(c, fp); + else + fprintf(fp, "\\%03o", (unsigned char)c); + } +} + +void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size) +{ + switch (size & 15) { + case 0: + while (size >= 16) { + size -= 16; + *out++ = *in1++ ^ *in2++; + case 15: *out++ = *in1++ ^ *in2++; + case 14: *out++ = *in1++ ^ *in2++; + case 13: *out++ = *in1++ ^ *in2++; + case 12: *out++ = *in1++ ^ *in2++; + case 11: *out++ = *in1++ ^ *in2++; + case 10: *out++ = *in1++ ^ *in2++; + case 9: *out++ = *in1++ ^ *in2++; + case 8: *out++ = *in1++ ^ *in2++; + case 7: *out++ = *in1++ ^ *in2++; + case 6: *out++ = *in1++ ^ *in2++; + case 5: *out++ = *in1++ ^ *in2++; + case 4: *out++ = *in1++ ^ *in2++; + case 3: *out++ = *in1++ ^ *in2++; + case 2: *out++ = *in1++ ^ *in2++; + case 1: *out++ = *in1++ ^ *in2++; + } + } +} + +FingerprintType ssh2_pick_fingerprint( + char **fingerprints, FingerprintType preferred_type) +{ + /* + * Keys are either SSH-2, in which case we have all fingerprint + * types, or SSH-1, in which case we have only MD5. So we return + * the default type if we can, or MD5 if that's all we have; no + * need for a fully general preference-list system. + */ + FingerprintType fptype = fingerprints[preferred_type] ? + preferred_type : SSH_FPTYPE_MD5; + assert(fingerprints[fptype]); + return fptype; +} + +FingerprintType ssh2_pick_default_fingerprint(char **fingerprints) +{ + return ssh2_pick_fingerprint(fingerprints, SSH_FPTYPE_DEFAULT); +} diff --git a/uxconfig.in b/uxconfig.in index c1d7985..4c2da60 100644 --- a/uxconfig.in +++ b/uxconfig.in @@ -13,6 +13,9 @@ /* Define to 1 if you have the `dirfd' function. */ #undef HAVE_DIRFD +/* Define to 1 if you have the `elf_aux_info' function. */ +#undef HAVE_ELF_AUX_INFO + /* Define to 1 if you have the `endpwent' function. */ #undef HAVE_ENDPWENT @@ -25,6 +28,9 @@ /* Define to 1 if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO +/* Define to 1 if you have the `getauxval' function. */ +#undef HAVE_GETAUXVAL + /* Define to 1 if you have the header file. */ #undef HAVE_GLOB_H @@ -76,12 +82,18 @@ /* Define to 1 if you have the `strsignal' function. */ #undef HAVE_STRSIGNAL +/* Define to 1 if you have the `sysctlbyname' function. */ +#undef HAVE_SYSCTLBYNAME + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_AUXV_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SYSCTL_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H diff --git a/version.c b/version.c index 59e9ca7..620879c 100644 --- a/version.c +++ b/version.c @@ -8,6 +8,9 @@ * to do here is to drop it into variables of the right names. */ +#include "putty.h" +#include "ssh.h" + #ifdef SOURCE_COMMIT #include "empty.h" #endif diff --git a/version.h b/version.h index 8df9024..d213a45 100644 --- a/version.h +++ b/version.h @@ -1,8 +1,6 @@ /* Generated by automated build script */ -#define RELEASE 0.73 -#define TEXTVER "Release 0.73" -#define SSHVER "-Release-0.73" -#define BINARY_VERSION 0,73,0,0 -#ifndef SOURCE_COMMIT -#define SOURCE_COMMIT "745ed3ad3beaf52fc623827e770b3a068b238dd5" -#endif +#define RELEASE 0.76 +#define TEXTVER "Release 0.76" +#define SSHVER "-Release-0.76" +#define BINARY_VERSION 0,76,0,0 +#define SOURCE_COMMIT "1fd7baa7344bb38d62a024e5dba3a720c67d05cf" diff --git a/wcwidth.c b/wcwidth.c index 6de676a..6468fed 100644 --- a/wcwidth.c +++ b/wcwidth.c @@ -179,7 +179,7 @@ int mk_wcwidth(unsigned int ucs) /* A sorted list of intervals of double-width characters generated by: * https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl * from the Unicode 9.0.0 data files available at: - * http://www.unicode.org/Public/9.0.0/ucd/ + * https://www.unicode.org/Public/13.0.0/ucd/ */ static const struct interval wide[] = { {0x1100, 0x115F}, @@ -224,14 +224,12 @@ int mk_wcwidth(unsigned int ucs) {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, - {0x3105, 0x312D}, + {0x3105, 0x312F}, {0x3131, 0x318E}, - {0x3190, 0x31BA}, - {0x31C0, 0x31E3}, + {0x3190, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, - {0x3250, 0x32FE}, - {0x3300, 0x4DBF}, + {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, @@ -243,10 +241,15 @@ int mk_wcwidth(unsigned int ucs) {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, - {0x16FE0, 0x16FE0}, - {0x17000, 0x187EC}, - {0x18800, 0x18AF2}, - {0x1B000, 0x1B001}, + {0x16FE0, 0x16FE4}, + {0x16FF0, 0x16FF1}, + {0x17000, 0x187F7}, + {0x18800, 0x18CD5}, + {0x18D00, 0x18D08}, + {0x1B000, 0x1B11E}, + {0x1B150, 0x1B152}, + {0x1B164, 0x1B167}, + {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, @@ -255,6 +258,7 @@ int mk_wcwidth(unsigned int ucs) {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, + {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, @@ -276,16 +280,22 @@ int mk_wcwidth(unsigned int ucs) {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, + {0x1F6D5, 0x1F6D7}, {0x1F6EB, 0x1F6EC}, - {0x1F6F4, 0x1F6F6}, - {0x1F910, 0x1F91E}, - {0x1F920, 0x1F927}, - {0x1F930, 0x1F930}, - {0x1F933, 0x1F93E}, - {0x1F940, 0x1F94B}, - {0x1F950, 0x1F95E}, - {0x1F980, 0x1F991}, - {0x1F9C0, 0x1F9C0}, + {0x1F6F4, 0x1F6FC}, + {0x1F7E0, 0x1F7EB}, + {0x1F90C, 0x1F93A}, + {0x1F93C, 0x1F945}, + {0x1F947, 0x1F978}, + {0x1F97A, 0x1F9CB}, + {0x1F9CD, 0x1F9FF}, + {0x1FA70, 0x1FA74}, + {0x1FA78, 0x1FA7A}, + {0x1FA80, 0x1FA86}, + {0x1FA90, 0x1FAA8}, + {0x1FAB0, 0x1FAB6}, + {0x1FAC0, 0x1FAC2}, + {0x1FAD0, 0x1FAD6}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, }; diff --git a/windows/DEVCPP/pageant/pageant.dev b/windows/DEVCPP/pageant/pageant.dev index 6e1bef8..c17dd9e 100644 --- a/windows/DEVCPP/pageant/pageant.dev +++ b/windows/DEVCPP/pageant/pageant.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=62 +UnitCount=78 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pageant_private.rc @@ -43,7 +43,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit2] -FileName=..\..\..\conf.c +FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -53,7 +53,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit3] -FileName=..\..\..\ecc.c +FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\marshal.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\memory.c +FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\mpint.c +FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\pageant.c +FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshaes.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshauxcrypt.c +FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshdes.c +FileName=..\..\..\pageant.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\stripctrl.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\utils.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\version.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\windows\winpgnt.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,6 +353,136 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] +FileName=..\..\..\windows\winhandl.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit34] +FileName=..\..\..\windows\winhelp.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit35] +FileName=..\..\..\windows\winhsock.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit36] +FileName=..\..\..\windows\winmisc.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit37] +FileName=..\..\..\windows\winmiscs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit38] +FileName=..\..\..\windows\winnet.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit39] +FileName=..\..\..\windows\winnpc.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit40] +FileName=..\..\..\windows\winnps.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit41] +FileName=..\..\..\windows\winpgnt.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit42] +FileName=..\..\..\windows\winpgntc.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit43] +FileName=..\..\..\windows\winsecur.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit44] +FileName=..\..\..\windows\winselgui.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit45] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit46] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -362,7 +492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit34] +[Unit47] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -372,7 +502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit35] +[Unit48] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -382,7 +512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit36] +[Unit49] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -392,7 +522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit37] +[Unit50] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -402,7 +532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit38] +[Unit51] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -412,7 +542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit39] +[Unit52] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -422,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit40] +[Unit53] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -432,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit41] +[Unit54] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -442,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit42] +[Unit55] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -452,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit43] +[Unit56] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 @@ -462,7 +592,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit44] +[Unit57] +FileName=..\..\..\proxy.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit58] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -472,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit45] +[Unit59] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -482,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit60] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -492,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit47] +[Unit61] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -502,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit48] +[Unit62] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 @@ -512,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit49] +[Unit63] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -522,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit50] +[Unit64] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -532,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit51] +[Unit65] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -542,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit66] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -552,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit67] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -562,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit68] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -572,7 +712,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit69] +FileName=..\..\..\windows\pageant-rc.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit70] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -582,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit71] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -592,7 +742,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit72] +FileName=..\..\..\windows\wincapi.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit73] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -602,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit74] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -612,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit75] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -622,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit76] FileName=..\..\..\windows\pageant.ico Folder=Resource Files Compile=0 @@ -632,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit77] FileName=..\..\..\windows\pageant.rc Folder=Resource Files Compile=1 @@ -642,7 +802,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit78] FileName=..\..\..\windows\pageants.ico Folder=Resource Files Compile=0 diff --git a/windows/DEVCPP/plink/plink.dev b/windows/DEVCPP/plink/plink.dev index d8d2a6f..4edffba 100644 --- a/windows/DEVCPP/plink/plink.dev +++ b/windows/DEVCPP/plink/plink.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=146 +UnitCount=156 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=plink_private.rc @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\cmdline.c +FileName=..\..\..\clicons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\conf.c +FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\ecc.c +FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\errsock.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\ldisc.c +FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\logging.c +FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\mainchan.c +FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\marshal.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\memory.c +FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\misc.c +FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\miscucs.c +FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\mpint.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\noshare.c +FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\noterm.c +FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\nullplug.c +FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\noterm.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\pinger.c +FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\proxy.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\raw.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\rlogin.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sessprep.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\settings.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\ssh.c +FileName=..\..\..\sessprep.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\ssh1bpp.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\ssh1censor.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\ssh1connection-client.c +FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\ssh1connection.c +FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\ssh1login.c +FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\ssh2bpp-bare.c +FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\ssh2bpp.c +FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\ssh2censor.c +FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\ssh2connection-client.c +FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\ssh2connection.c +FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\ssh2kex-client.c +FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\ssh2transhk.c +FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\ssh2transport.c +FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\ssh2userauth.c +FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\sshauxcrypt.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\sshccp.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\sshcommon.c +FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,7 +563,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -573,7 +573,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit55] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -583,7 +583,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -593,7 +593,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -603,7 +603,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -613,7 +613,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\..\..\sshmac.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -623,7 +623,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -633,7 +633,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\..\..\sshprng.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -643,7 +643,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -653,7 +653,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -663,7 +663,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -673,7 +673,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 @@ -683,7 +683,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -693,7 +693,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -703,7 +703,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] -FileName=..\..\..\sshshare.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -713,7 +713,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit69] -FileName=..\..\..\sshverstring.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -723,7 +723,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit70] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -733,7 +733,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit71] -FileName=..\..\..\stripctrl.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -743,7 +743,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit72] -FileName=..\..\..\telnet.c +FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 @@ -753,7 +753,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit73] -FileName=..\..\..\timing.c +FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -763,7 +763,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit74] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -773,7 +773,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit75] -FileName=..\..\..\utils.c +FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 @@ -783,7 +783,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit76] -FileName=..\..\..\version.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -793,7 +793,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit77] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -803,7 +803,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit78] -FileName=..\..\..\wildcard.c +FileName=..\..\..\supdup.c Folder=Source Files Compile=1 CompileCpp=0 @@ -813,7 +813,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit79] -FileName=..\..\..\windows\wincapi.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -823,7 +823,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit80] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -833,7 +833,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit81] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -843,7 +843,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit82] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -853,7 +853,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit83] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -863,7 +863,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit84] -FileName=..\..\..\windows\winhsock.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -873,7 +873,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit85] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -883,7 +883,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit86] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -893,7 +893,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit87] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 @@ -903,7 +903,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit88] -FileName=..\..\..\windows\winnohlp.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -913,7 +913,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit89] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -923,7 +923,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit90] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -933,7 +933,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit91] -FileName=..\..\..\windows\winnpc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -943,7 +943,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit92] -FileName=..\..\..\windows\winnps.c +FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -953,7 +953,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit93] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -963,7 +963,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit94] -FileName=..\..\..\windows\winplink.c +FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -973,7 +973,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit95] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -983,7 +983,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit96] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -993,7 +993,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit97] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1003,7 +1003,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit98] -FileName=..\..\..\windows\winshare.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1013,7 +1013,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit99] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1023,7 +1023,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit100] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1033,7 +1033,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit101] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1043,7 +1043,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit102] -FileName=..\..\..\windows\winx11.c +FileName=..\..\..\windows\winplink.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1053,7 +1053,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit103] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1063,6 +1063,96 @@ OverrideBuildCmd=0 BuildCmd= [Unit104] +FileName=..\..\..\windows\winsecur.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit105] +FileName=..\..\..\windows\winselcli.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit106] +FileName=..\..\..\windows\winser.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit107] +FileName=..\..\..\windows\winshare.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit108] +FileName=..\..\..\windows\winstore.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit109] +FileName=..\..\..\windows\wintime.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit110] +FileName=..\..\..\windows\winucs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit111] +FileName=..\..\..\windows\winx11.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit112] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit113] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -1072,7 +1162,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit105] +[Unit114] +FileName=..\..\..\console.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit115] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -1082,7 +1182,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit106] +[Unit116] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -1092,7 +1192,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit107] +[Unit117] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -1102,7 +1202,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit108] +[Unit118] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -1112,7 +1212,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit109] +[Unit119] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -1122,7 +1222,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit110] +[Unit120] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -1132,7 +1232,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit111] +[Unit121] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -1142,7 +1242,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit112] +[Unit122] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -1152,7 +1252,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit113] +[Unit123] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -1162,7 +1262,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit114] +[Unit124] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -1172,7 +1272,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit115] +[Unit125] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 @@ -1182,7 +1282,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit116] +[Unit126] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -1192,7 +1292,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit117] +[Unit127] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -1202,7 +1302,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit118] +[Unit128] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -1212,7 +1312,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit119] +[Unit129] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -1222,7 +1322,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit120] +[Unit130] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -1232,7 +1332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit121] +[Unit131] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -1242,7 +1342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit122] +[Unit132] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 @@ -1252,7 +1352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit123] +[Unit133] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 @@ -1262,7 +1362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit124] +[Unit134] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 @@ -1272,7 +1372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit125] +[Unit135] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 @@ -1282,7 +1382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit126] +[Unit136] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 @@ -1292,7 +1392,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit127] +[Unit137] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 @@ -1302,7 +1402,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit128] +[Unit138] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 @@ -1312,7 +1412,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit129] +[Unit139] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -1322,7 +1422,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit130] +[Unit140] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -1332,7 +1432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit131] +[Unit141] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 @@ -1342,7 +1442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit132] +[Unit142] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 @@ -1352,7 +1452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit133] +[Unit143] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -1362,7 +1462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit134] +[Unit144] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -1372,7 +1472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit135] +[Unit145] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -1382,7 +1482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit136] +[Unit146] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -1392,7 +1492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit137] +[Unit147] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -1402,7 +1502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit138] +[Unit148] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -1412,7 +1512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit139] +[Unit149] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -1422,7 +1522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit140] +[Unit150] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -1432,7 +1532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit141] +[Unit151] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 @@ -1442,7 +1542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit142] +[Unit152] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -1452,7 +1552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit143] +[Unit153] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -1462,7 +1562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit144] +[Unit154] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -1472,7 +1572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit145] +[Unit155] FileName=..\..\..\windows\plink.rc Folder=Resource Files Compile=1 @@ -1482,7 +1582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit146] +[Unit156] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 diff --git a/windows/DEVCPP/pscp/pscp.dev b/windows/DEVCPP/pscp/pscp.dev index b1a0ece..6508fa0 100644 --- a/windows/DEVCPP/pscp/pscp.dev +++ b/windows/DEVCPP/pscp/pscp.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=143 +UnitCount=152 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pscp_private.rc @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\cmdline.c +FileName=..\..\..\clicons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\conf.c +FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\ecc.c +FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\errsock.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\logging.c +FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\mainchan.c +FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\marshal.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\memory.c +FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\misc.c +FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\miscucs.c +FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\mpint.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\noshare.c +FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\nullplug.c +FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\pinger.c +FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\proxy.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\pscp.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\psftpcommon.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\settings.c +FileName=..\..\..\pscp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sftp.c +FileName=..\..\..\psftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sftpcommon.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\ssh.c +FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\ssh1bpp.c +FileName=..\..\..\sftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\ssh1censor.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\ssh1connection-client.c +FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\ssh1connection.c +FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\ssh1login.c +FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\ssh2bpp-bare.c +FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\ssh2bpp.c +FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\ssh2censor.c +FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\ssh2connection-client.c +FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\ssh2connection.c +FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\ssh2kex-client.c +FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\ssh2transhk.c +FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\ssh2transport.c +FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\ssh2userauth.c +FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\sshauxcrypt.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\sshccp.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\sshcommon.c +FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,7 +563,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -573,7 +573,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit55] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -583,7 +583,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -593,7 +593,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -603,7 +603,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\..\..\sshmac.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -613,7 +613,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -623,7 +623,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\..\..\sshprng.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -633,7 +633,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -643,7 +643,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -653,7 +653,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -663,7 +663,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 @@ -673,7 +673,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -683,7 +683,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -693,7 +693,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\..\..\sshshare.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -703,7 +703,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] -FileName=..\..\..\sshverstring.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -713,7 +713,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit69] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -723,7 +723,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit70] -FileName=..\..\..\stripctrl.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -733,7 +733,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit71] -FileName=..\..\..\timing.c +FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 @@ -743,7 +743,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit72] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -753,7 +753,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit73] -FileName=..\..\..\utils.c +FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -763,7 +763,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit74] -FileName=..\..\..\version.c +FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 @@ -773,7 +773,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit75] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -783,7 +783,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit76] -FileName=..\..\..\wildcard.c +FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -793,7 +793,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit77] -FileName=..\..\..\windows\wincapi.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -803,7 +803,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit78] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -813,7 +813,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit79] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -823,7 +823,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit80] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -833,7 +833,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit81] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -843,7 +843,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit82] -FileName=..\..\..\windows\winhsock.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -853,7 +853,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit83] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -863,7 +863,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit84] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 @@ -873,7 +873,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit85] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -883,7 +883,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit86] -FileName=..\..\..\windows\winnohlp.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -893,7 +893,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit87] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -903,7 +903,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit88] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -913,7 +913,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit89] -FileName=..\..\..\windows\winnpc.c +FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -923,7 +923,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit90] -FileName=..\..\..\windows\winnps.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -933,7 +933,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit91] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -943,7 +943,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit92] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -953,7 +953,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit93] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -963,7 +963,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit94] -FileName=..\..\..\windows\winsftp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -973,7 +973,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit95] -FileName=..\..\..\windows\winshare.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -983,7 +983,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit96] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -993,7 +993,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit97] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1003,7 +1003,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit98] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1013,7 +1013,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit99] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1023,6 +1023,86 @@ OverrideBuildCmd=0 BuildCmd= [Unit100] +FileName=..\..\..\windows\winsecur.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit101] +FileName=..\..\..\windows\winselcli.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit102] +FileName=..\..\..\windows\winsftp.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit103] +FileName=..\..\..\windows\winshare.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit104] +FileName=..\..\..\windows\winstore.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit105] +FileName=..\..\..\windows\wintime.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit106] +FileName=..\..\..\windows\winucs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit107] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit108] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -1032,7 +1112,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit101] +[Unit109] +FileName=..\..\..\console.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit110] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -1042,7 +1132,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit102] +[Unit111] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -1052,7 +1142,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit103] +[Unit112] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -1062,7 +1152,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit104] +[Unit113] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -1072,7 +1162,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit105] +[Unit114] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -1082,7 +1172,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit106] +[Unit115] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -1092,7 +1182,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit107] +[Unit116] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -1102,7 +1192,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit108] +[Unit117] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -1112,7 +1202,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit109] +[Unit118] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -1122,7 +1212,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit110] +[Unit119] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 @@ -1132,7 +1222,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit111] +[Unit120] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -1142,7 +1232,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit112] +[Unit121] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -1152,7 +1242,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit113] +[Unit122] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 @@ -1162,7 +1252,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit114] +[Unit123] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -1172,7 +1262,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit115] +[Unit124] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -1182,7 +1272,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit116] +[Unit125] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -1192,7 +1282,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit117] +[Unit126] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 @@ -1202,7 +1292,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit118] +[Unit127] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -1212,7 +1302,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit119] +[Unit128] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 @@ -1222,7 +1312,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit120] +[Unit129] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 @@ -1232,7 +1322,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit121] +[Unit130] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 @@ -1242,7 +1332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit122] +[Unit131] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 @@ -1252,7 +1342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit123] +[Unit132] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 @@ -1262,7 +1352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit124] +[Unit133] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 @@ -1272,7 +1362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit125] +[Unit134] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 @@ -1282,7 +1372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit126] +[Unit135] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -1292,7 +1382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit127] +[Unit136] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -1302,7 +1392,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit128] +[Unit137] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 @@ -1312,7 +1402,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit129] +[Unit138] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 @@ -1322,7 +1412,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit130] +[Unit139] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -1332,7 +1422,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit131] +[Unit140] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -1342,7 +1432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit132] +[Unit141] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -1352,7 +1442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit133] +[Unit142] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -1362,7 +1452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit134] +[Unit143] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -1372,7 +1462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit135] +[Unit144] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -1382,7 +1472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit136] +[Unit145] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -1392,7 +1482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit137] +[Unit146] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -1402,7 +1492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit138] +[Unit147] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 @@ -1412,7 +1502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit139] +[Unit148] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -1422,7 +1512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit140] +[Unit149] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -1432,7 +1522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit141] +[Unit150] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -1442,7 +1532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit142] +[Unit151] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 @@ -1452,7 +1542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit143] +[Unit152] FileName=..\..\..\windows\pscp.rc Folder=Resource Files Compile=1 diff --git a/windows/DEVCPP/psftp/psftp.dev b/windows/DEVCPP/psftp/psftp.dev index 2adb9a1..5d953e9 100644 --- a/windows/DEVCPP/psftp/psftp.dev +++ b/windows/DEVCPP/psftp/psftp.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=143 +UnitCount=152 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=psftp_private.rc @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\cmdline.c +FileName=..\..\..\clicons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\conf.c +FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\cproxy.c +FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\ecc.c +FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\errsock.c +FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\logging.c +FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\mainchan.c +FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\marshal.c +FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\memory.c +FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\misc.c +FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\miscucs.c +FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\mpint.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\noshare.c +FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\nullplug.c +FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\pgssapi.c +FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\pinger.c +FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\portfwd.c +FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\proxy.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\psftp.c +FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\psftpcommon.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\settings.c +FileName=..\..\..\psftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sftp.c +FileName=..\..\..\psftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sftpcommon.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\ssh.c +FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\ssh1bpp.c +FileName=..\..\..\sftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\ssh1censor.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\ssh1connection-client.c +FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\ssh1connection.c +FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\ssh1login.c +FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\ssh2bpp-bare.c +FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\ssh2bpp.c +FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\ssh2censor.c +FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\ssh2connection-client.c +FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\ssh2connection.c +FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\ssh2kex-client.c +FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\ssh2transhk.c +FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\ssh2transport.c +FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\ssh2userauth.c +FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\sshauxcrypt.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\sshccp.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\sshcommon.c +FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,7 +563,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -573,7 +573,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit55] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -583,7 +583,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -593,7 +593,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -603,7 +603,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\..\..\sshmac.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -613,7 +613,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -623,7 +623,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\..\..\sshprng.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -633,7 +633,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -643,7 +643,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -653,7 +653,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -663,7 +663,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 @@ -673,7 +673,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -683,7 +683,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -693,7 +693,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\..\..\sshshare.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -703,7 +703,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] -FileName=..\..\..\sshverstring.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -713,7 +713,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit69] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -723,7 +723,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit70] -FileName=..\..\..\stripctrl.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -733,7 +733,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit71] -FileName=..\..\..\timing.c +FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 @@ -743,7 +743,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit72] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -753,7 +753,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit73] -FileName=..\..\..\utils.c +FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -763,7 +763,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit74] -FileName=..\..\..\version.c +FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 @@ -773,7 +773,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit75] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -783,7 +783,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit76] -FileName=..\..\..\wildcard.c +FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -793,7 +793,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit77] -FileName=..\..\..\windows\wincapi.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -803,7 +803,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit78] -FileName=..\..\..\windows\wincons.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -813,7 +813,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit79] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -823,7 +823,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit80] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -833,7 +833,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit81] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -843,7 +843,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit82] -FileName=..\..\..\windows\winhsock.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -853,7 +853,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit83] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -863,7 +863,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit84] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 @@ -873,7 +873,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit85] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 @@ -883,7 +883,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit86] -FileName=..\..\..\windows\winnohlp.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -893,7 +893,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit87] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -903,7 +903,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit88] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -913,7 +913,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit89] -FileName=..\..\..\windows\winnpc.c +FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -923,7 +923,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit90] -FileName=..\..\..\windows\winnps.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -933,7 +933,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit91] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -943,7 +943,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit92] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -953,7 +953,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit93] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -963,7 +963,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit94] -FileName=..\..\..\windows\winsftp.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -973,7 +973,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit95] -FileName=..\..\..\windows\winshare.c +FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -983,7 +983,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit96] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -993,7 +993,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit97] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1003,7 +1003,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit98] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1013,7 +1013,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit99] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1023,6 +1023,86 @@ OverrideBuildCmd=0 BuildCmd= [Unit100] +FileName=..\..\..\windows\winsecur.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit101] +FileName=..\..\..\windows\winselcli.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit102] +FileName=..\..\..\windows\winsftp.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit103] +FileName=..\..\..\windows\winshare.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit104] +FileName=..\..\..\windows\winstore.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit105] +FileName=..\..\..\windows\wintime.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit106] +FileName=..\..\..\windows\winucs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit107] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit108] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -1032,7 +1112,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit101] +[Unit109] +FileName=..\..\..\console.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit110] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -1042,7 +1132,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit102] +[Unit111] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -1052,7 +1142,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit103] +[Unit112] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -1062,7 +1152,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit104] +[Unit113] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -1072,7 +1162,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit105] +[Unit114] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -1082,7 +1172,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit106] +[Unit115] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -1092,7 +1182,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit107] +[Unit116] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -1102,7 +1192,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit108] +[Unit117] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -1112,7 +1202,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit109] +[Unit118] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -1122,7 +1212,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit110] +[Unit119] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 @@ -1132,7 +1222,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit111] +[Unit120] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -1142,7 +1232,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit112] +[Unit121] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -1152,7 +1242,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit113] +[Unit122] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 @@ -1162,7 +1252,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit114] +[Unit123] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -1172,7 +1262,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit115] +[Unit124] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -1182,7 +1272,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit116] +[Unit125] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -1192,7 +1282,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit117] +[Unit126] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 @@ -1202,7 +1292,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit118] +[Unit127] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -1212,7 +1302,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit119] +[Unit128] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 @@ -1222,7 +1312,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit120] +[Unit129] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 @@ -1232,7 +1322,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit121] +[Unit130] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 @@ -1242,7 +1332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit122] +[Unit131] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 @@ -1252,7 +1342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit123] +[Unit132] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 @@ -1262,7 +1352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit124] +[Unit133] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 @@ -1272,7 +1362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit125] +[Unit134] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 @@ -1282,7 +1372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit126] +[Unit135] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -1292,7 +1382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit127] +[Unit136] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -1302,7 +1392,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit128] +[Unit137] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 @@ -1312,7 +1402,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit129] +[Unit138] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 @@ -1322,7 +1412,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit130] +[Unit139] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -1332,7 +1422,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit131] +[Unit140] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -1342,7 +1432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit132] +[Unit141] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -1352,7 +1442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit133] +[Unit142] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -1362,7 +1452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit134] +[Unit143] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -1372,7 +1462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit135] +[Unit144] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -1382,7 +1472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit136] +[Unit145] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -1392,7 +1482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit137] +[Unit146] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -1402,7 +1492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit138] +[Unit147] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 @@ -1412,7 +1502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit139] +[Unit148] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -1422,7 +1512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit140] +[Unit149] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -1432,7 +1522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit141] +[Unit150] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -1442,7 +1532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit142] +[Unit151] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 @@ -1452,7 +1542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit143] +[Unit152] FileName=..\..\..\windows\psftp.rc Folder=Resource Files Compile=1 diff --git a/windows/DEVCPP/putty/putty.dev b/windows/DEVCPP/putty/putty.dev index 103493a..f8cf0e8 100644 --- a/windows/DEVCPP/putty/putty.dev +++ b/windows/DEVCPP/putty/putty.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=158 +UnitCount=164 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=putty_private.rc @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\sercfg.c +FileName=..\..\..\sessprep.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\sessprep.c +FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\settings.c +FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\ssh.c +FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\ssh1bpp.c +FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\ssh1censor.c +FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\ssh1connection-client.c +FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\ssh1connection.c +FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\ssh1login.c +FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\ssh2bpp-bare.c +FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\ssh2bpp.c +FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\ssh2censor.c +FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\ssh2connection-client.c +FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\ssh2connection.c +FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\ssh2kex-client.c +FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\ssh2transhk.c +FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\ssh2transport.c +FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\ssh2userauth.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\sshaes.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\sshccp.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\sshcommon.c +FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,7 +563,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 @@ -573,7 +573,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit55] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -583,7 +583,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit56] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -593,7 +593,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit57] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -603,7 +603,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit58] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -613,7 +613,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit59] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -623,7 +623,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit60] -FileName=..\..\..\sshgssc.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -633,7 +633,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit61] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -643,7 +643,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit62] -FileName=..\..\..\sshmac.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -653,7 +653,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit63] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -663,7 +663,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit64] -FileName=..\..\..\sshprng.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -673,7 +673,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit65] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 @@ -683,7 +683,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit66] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -693,7 +693,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit67] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -703,7 +703,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit68] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -713,7 +713,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit69] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -723,7 +723,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit70] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -733,7 +733,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit71] -FileName=..\..\..\sshshare.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -743,7 +743,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit72] -FileName=..\..\..\sshverstring.c +FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 @@ -753,7 +753,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit73] -FileName=..\..\..\sshzlib.c +FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -763,7 +763,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit74] -FileName=..\..\..\stripctrl.c +FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -773,7 +773,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit75] -FileName=..\..\..\telnet.c +FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 @@ -783,7 +783,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit76] -FileName=..\..\..\terminal.c +FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 @@ -793,7 +793,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit77] -FileName=..\..\..\timing.c +FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -803,7 +803,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit78] -FileName=..\..\..\tree234.c +FileName=..\..\..\supdup.c Folder=Source Files Compile=1 CompileCpp=0 @@ -813,7 +813,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit79] -FileName=..\..\..\utils.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -823,7 +823,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit80] -FileName=..\..\..\version.c +FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -833,7 +833,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit81] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -843,7 +843,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit82] -FileName=..\..\..\wildcard.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -853,7 +853,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit83] -FileName=..\..\..\windows\sizetip.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -863,7 +863,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit84] -FileName=..\..\..\windows\wincapi.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -873,7 +873,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit85] -FileName=..\..\..\windows\wincfg.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -883,7 +883,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit86] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 @@ -893,7 +893,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit87] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 @@ -903,7 +903,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit88] -FileName=..\..\..\windows\windlg.c +FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 @@ -913,7 +913,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit89] -FileName=..\..\..\windows\window.c +FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -923,7 +923,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit90] -FileName=..\..\..\windows\wingss.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -933,7 +933,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit91] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -943,7 +943,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit92] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -953,7 +953,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit93] -FileName=..\..\..\windows\winhsock.c +FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 @@ -963,7 +963,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit94] -FileName=..\..\..\windows\winjump.c +FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -973,7 +973,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit95] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -983,7 +983,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit96] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -993,7 +993,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit97] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1003,7 +1003,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit98] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1013,7 +1013,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit99] -FileName=..\..\..\windows\winnpc.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1023,7 +1023,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit100] -FileName=..\..\..\windows\winnps.c +FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1033,7 +1033,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit101] -FileName=..\..\..\windows\winpgntc.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1043,7 +1043,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit102] -FileName=..\..\..\windows\winprint.c +FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1053,7 +1053,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit103] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1063,7 +1063,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit104] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1073,7 +1073,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit105] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1083,7 +1083,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit106] -FileName=..\..\..\windows\winshare.c +FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1093,7 +1093,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit107] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1103,7 +1103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit108] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1113,7 +1113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit109] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\winselgui.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1123,7 +1123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit110] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1133,7 +1133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit111] -FileName=..\..\..\windows\winx11.c +FileName=..\..\..\windows\winshare.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1143,7 +1143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit112] -FileName=..\..\..\x11fwd.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -1153,6 +1153,56 @@ OverrideBuildCmd=0 BuildCmd= [Unit113] +FileName=..\..\..\windows\wintime.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit114] +FileName=..\..\..\windows\winucs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit115] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit116] +FileName=..\..\..\windows\winx11.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit117] +FileName=..\..\..\x11fwd.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit118] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -1162,7 +1212,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit114] +[Unit119] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -1172,7 +1222,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit115] +[Unit120] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -1182,7 +1232,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit116] +[Unit121] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -1192,7 +1242,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit117] +[Unit122] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -1202,7 +1252,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit118] +[Unit123] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -1212,7 +1262,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit119] +[Unit124] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -1222,7 +1272,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit120] +[Unit125] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -1232,7 +1282,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit121] +[Unit126] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -1242,7 +1292,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit122] +[Unit127] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -1252,7 +1302,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit123] +[Unit128] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -1262,7 +1312,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit124] +[Unit129] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -1272,7 +1322,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit125] +[Unit130] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 @@ -1282,7 +1332,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit126] +[Unit131] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -1292,7 +1342,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit127] +[Unit132] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -1302,7 +1352,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit128] +[Unit133] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -1312,7 +1362,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit129] +[Unit134] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -1322,7 +1372,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit130] +[Unit135] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -1332,7 +1382,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit131] +[Unit136] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -1342,7 +1392,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit132] +[Unit137] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 @@ -1352,7 +1402,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit133] +[Unit138] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 @@ -1362,7 +1412,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit134] +[Unit139] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 @@ -1372,7 +1422,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit135] +[Unit140] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 @@ -1382,7 +1432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit136] +[Unit141] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 @@ -1392,7 +1442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit137] +[Unit142] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 @@ -1402,7 +1452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit138] +[Unit143] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 @@ -1412,7 +1462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit139] +[Unit144] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -1422,7 +1472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit140] +[Unit145] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -1432,7 +1482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit141] +[Unit146] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 @@ -1442,7 +1492,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit142] +[Unit147] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 @@ -1452,7 +1502,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit143] +[Unit148] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -1462,7 +1512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit144] +[Unit149] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -1472,7 +1522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit145] +[Unit150] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -1482,7 +1532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit146] +[Unit151] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -1492,7 +1542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit147] +[Unit152] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -1502,7 +1552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit148] +[Unit153] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -1512,7 +1562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit149] +[Unit154] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -1522,7 +1572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit150] +[Unit155] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -1532,7 +1582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit151] +[Unit156] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -1542,7 +1592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit152] +[Unit157] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 @@ -1552,7 +1602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit153] +[Unit158] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -1562,7 +1612,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit154] +[Unit159] +FileName=..\..\..\windows\winseat.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit160] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -1572,7 +1632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit155] +[Unit161] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -1582,7 +1642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit156] +[Unit162] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 @@ -1592,7 +1652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit157] +[Unit163] FileName=..\..\..\windows\putty.rc Folder=Resource Files Compile=1 @@ -1602,7 +1662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit158] +[Unit164] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 diff --git a/windows/DEVCPP/puttygen/puttygen.dev b/windows/DEVCPP/puttygen/puttygen.dev index 9202c92..a3d3df3 100644 --- a/windows/DEVCPP/puttygen/puttygen.dev +++ b/windows/DEVCPP/puttygen/puttygen.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=74 +UnitCount=85 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttygen_private.rc @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\misc.c +FileName=..\..\..\millerrabin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\mpint.c +FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\notiming.c +FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshaes.c +FileName=..\..\..\mpunsafe.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshauxcrypt.c +FileName=..\..\..\notiming.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshbcrypt.c +FileName=..\..\..\pockle.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\primecandidate.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sshdes.c +FileName=..\..\..\smallprimes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshdssg.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshecdsag.c +FileName=..\..\..\sshbcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshprime.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshprng.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshpubk.c +FileName=..\..\..\sshdssg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshrand.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshecdsag.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\sshrsag.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshprime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\stripctrl.c +FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\utils.c +FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\version.c +FileName=..\..\..\sshrsag.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\winnoise.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\winnojmp.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winpgen.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,6 +473,86 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] +FileName=..\..\..\windows\winmiscs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit46] +FileName=..\..\..\windows\winnoise.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit47] +FileName=..\..\..\windows\winnojmp.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit48] +FileName=..\..\..\windows\winpgen.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit49] +FileName=..\..\..\windows\winsecur.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit50] +FileName=..\..\..\windows\winstore.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit51] +FileName=..\..\..\windows\wintime.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit52] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit53] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -482,7 +562,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit54] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -492,7 +572,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit47] +[Unit55] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -502,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit48] +[Unit56] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -512,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit49] +[Unit57] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -522,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit50] +[Unit58] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -532,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit51] +[Unit59] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -542,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit52] +[Unit60] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -552,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit53] +[Unit61] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -562,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit54] +[Unit62] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -572,7 +652,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit63] +FileName=..\..\..\mpunsafe.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit64] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -582,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit65] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -592,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit66] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -602,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit67] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -612,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit68] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -622,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit69] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 @@ -632,7 +722,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit70] +FileName=..\..\..\sshkeygen.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit71] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -642,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit72] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -652,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit73] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -662,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit74] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -672,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit75] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -682,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit76] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -692,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit77] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -702,7 +802,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit78] +FileName=..\..\..\windows\puttygen-rc.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit79] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -712,7 +822,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit80] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -722,7 +832,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit81] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -732,7 +842,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit82] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -742,7 +852,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit83] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -752,7 +862,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit84] FileName=..\..\..\windows\puttygen.ico Folder=Resource Files Compile=0 @@ -762,7 +872,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit74] +[Unit85] FileName=..\..\..\windows\puttygen.rc Folder=Resource Files Compile=1 diff --git a/windows/DEVCPP/puttytel/puttytel.dev b/windows/DEVCPP/puttytel/puttytel.dev index f28e57e..35fc851 100644 --- a/windows/DEVCPP/puttytel/puttytel.dev +++ b/windows/DEVCPP/puttytel/puttytel.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=85 +UnitCount=88 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttytel_private.rc @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\pinger.c +FileName=..\..\..\norand.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\proxy.c +FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\raw.c +FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\rlogin.c +FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sercfg.c +FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\telnet.c +FileName=..\..\..\supdup.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\terminal.c +FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,7 +303,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] -FileName=..\..\..\timing.c +FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 @@ -313,7 +313,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit29] -FileName=..\..\..\tree234.c +FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 @@ -323,7 +323,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit30] -FileName=..\..\..\utils.c +FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 @@ -333,7 +333,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit31] -FileName=..\..\..\version.c +FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 @@ -343,7 +343,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit32] -FileName=..\..\..\wcwidth.c +FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 @@ -353,7 +353,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit33] -FileName=..\..\..\windows\sizetip.c +FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 @@ -363,7 +363,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit34] -FileName=..\..\..\windows\wincfg.c +FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 @@ -373,7 +373,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit35] -FileName=..\..\..\windows\winctrls.c +FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -383,7 +383,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit36] -FileName=..\..\..\windows\windefs.c +FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 @@ -393,7 +393,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit37] -FileName=..\..\..\windows\windlg.c +FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -403,7 +403,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit38] -FileName=..\..\..\windows\window.c +FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -413,7 +413,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit39] -FileName=..\..\..\windows\winhandl.c +FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 @@ -423,7 +423,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit40] -FileName=..\..\..\windows\winhelp.c +FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 @@ -433,7 +433,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit41] -FileName=..\..\..\windows\winhsock.c +FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -443,7 +443,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit42] -FileName=..\..\..\windows\winjump.c +FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 @@ -453,7 +453,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit43] -FileName=..\..\..\windows\winmisc.c +FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 @@ -463,7 +463,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit44] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -473,7 +473,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit45] -FileName=..\..\..\windows\winnet.c +FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 @@ -483,7 +483,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit46] -FileName=..\..\..\windows\winprint.c +FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 @@ -493,7 +493,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit47] -FileName=..\..\..\windows\winproxy.c +FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -503,7 +503,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit48] -FileName=..\..\..\windows\winsecur.c +FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 @@ -513,7 +513,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit49] -FileName=..\..\..\windows\winser.c +FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 @@ -523,7 +523,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit50] -FileName=..\..\..\windows\winstore.c +FileName=..\..\..\windows\winselgui.c Folder=Source Files Compile=1 CompileCpp=0 @@ -533,7 +533,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit51] -FileName=..\..\..\windows\wintime.c +FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 @@ -543,7 +543,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit52] -FileName=..\..\..\windows\winucs.c +FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 @@ -553,7 +553,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit53] -FileName=..\..\..\windows\winutils.c +FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -563,6 +563,26 @@ OverrideBuildCmd=0 BuildCmd= [Unit54] +FileName=..\..\..\windows\winucs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] +FileName=..\..\..\windows\winutils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit56] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -572,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit55] +[Unit57] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -582,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit56] +[Unit58] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 @@ -592,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit57] +[Unit59] FileName=..\..\..\empty.h Folder=Header Files Compile=1 @@ -602,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit58] +[Unit60] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 @@ -612,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit59] +[Unit61] FileName=..\..\..\licence.h Folder=Header Files Compile=1 @@ -622,7 +642,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit60] +[Unit62] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -632,7 +652,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit61] +[Unit63] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -642,7 +662,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit62] +[Unit64] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -652,7 +672,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit63] +[Unit65] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 @@ -662,7 +682,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit64] +[Unit66] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 @@ -672,7 +692,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit65] +[Unit67] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -682,7 +702,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit66] +[Unit68] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -692,7 +712,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit67] +[Unit69] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -702,7 +722,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit68] +[Unit70] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -712,7 +732,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit69] +[Unit71] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 @@ -722,7 +742,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit70] +[Unit72] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 @@ -732,7 +752,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit71] +[Unit73] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -742,7 +762,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit72] +[Unit74] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -752,7 +772,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit73] +[Unit75] FileName=..\..\..\storage.h Folder=Header Files Compile=1 @@ -762,7 +782,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit74] +[Unit76] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 @@ -772,7 +792,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit75] +[Unit77] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -782,7 +802,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit76] +[Unit78] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -792,7 +812,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit77] +[Unit79] FileName=..\..\..\version.h Folder=Header Files Compile=1 @@ -802,7 +822,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit78] +[Unit80] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 @@ -812,7 +832,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit79] +[Unit81] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 @@ -822,7 +842,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit80] +[Unit82] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -832,7 +852,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit81] +[Unit83] +FileName=..\..\..\windows\winseat.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit84] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 @@ -842,7 +872,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit82] +[Unit85] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 @@ -852,7 +882,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit83] +[Unit86] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 @@ -862,7 +892,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit84] +[Unit87] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 @@ -872,7 +902,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit85] +[Unit88] FileName=..\..\..\windows\puttytel.rc Folder=Resource Files Compile=1 diff --git a/windows/DEVCPP/testcrypt/testcrypt.dev b/windows/DEVCPP/testcrypt/testcrypt.dev index 50e90d4..a52bbdc 100644 --- a/windows/DEVCPP/testcrypt/testcrypt.dev +++ b/windows/DEVCPP/testcrypt/testcrypt.dev @@ -12,7 +12,7 @@ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= -UnitCount=47 +UnitCount=61 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=testcrypt_private.rc @@ -63,7 +63,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit4] -FileName=..\..\..\mpint.c +FileName=..\..\..\millerrabin.c Folder=Source Files Compile=1 CompileCpp=0 @@ -73,7 +73,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit5] -FileName=..\..\..\sshaes.c +FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 @@ -83,7 +83,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit6] -FileName=..\..\..\ssharcf.c +FileName=..\..\..\mpunsafe.c Folder=Source Files Compile=1 CompileCpp=0 @@ -93,7 +93,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit7] -FileName=..\..\..\sshauxcrypt.c +FileName=..\..\..\pockle.c Folder=Source Files Compile=1 CompileCpp=0 @@ -103,7 +103,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit8] -FileName=..\..\..\sshblowf.c +FileName=..\..\..\primecandidate.c Folder=Source Files Compile=1 CompileCpp=0 @@ -113,7 +113,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit9] -FileName=..\..\..\sshccp.c +FileName=..\..\..\smallprimes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -123,7 +123,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit10] -FileName=..\..\..\sshcrc.c +FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -133,7 +133,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit11] -FileName=..\..\..\sshcrcda.c +FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -143,7 +143,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit12] -FileName=..\..\..\sshdes.c +FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -153,7 +153,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit13] -FileName=..\..\..\sshdh.c +FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 @@ -163,7 +163,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit14] -FileName=..\..\..\sshdss.c +FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 @@ -173,7 +173,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit15] -FileName=..\..\..\sshecc.c +FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 @@ -183,7 +183,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit16] -FileName=..\..\..\sshhmac.c +FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 @@ -193,7 +193,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit17] -FileName=..\..\..\sshmd5.c +FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -203,7 +203,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit18] -FileName=..\..\..\sshprime.c +FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 @@ -213,7 +213,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit19] -FileName=..\..\..\sshprng.c +FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 @@ -223,7 +223,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit20] -FileName=..\..\..\sshrsa.c +FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 @@ -233,7 +233,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit21] -FileName=..\..\..\sshsh256.c +FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 @@ -243,7 +243,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit22] -FileName=..\..\..\sshsh512.c +FileName=..\..\..\sshdssg.c Folder=Source Files Compile=1 CompileCpp=0 @@ -253,7 +253,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit23] -FileName=..\..\..\sshsha.c +FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 @@ -263,7 +263,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit24] -FileName=..\..\..\testcrypt.c +FileName=..\..\..\sshecdsag.c Folder=Source Files Compile=1 CompileCpp=0 @@ -273,7 +273,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit25] -FileName=..\..\..\tree234.c +FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 @@ -283,7 +283,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit26] -FileName=..\..\..\utils.c +FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 @@ -293,7 +293,7 @@ OverrideBuildCmd=0 BuildCmd= [Unit27] -FileName=..\..\..\windows\winmiscs.c +FileName=..\..\..\sshprime.c Folder=Source Files Compile=1 CompileCpp=0 @@ -303,6 +303,126 @@ OverrideBuildCmd=0 BuildCmd= [Unit28] +FileName=..\..\..\sshprng.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit29] +FileName=..\..\..\sshpubk.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit30] +FileName=..\..\..\sshrsa.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit31] +FileName=..\..\..\sshrsag.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit32] +FileName=..\..\..\sshsh256.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit33] +FileName=..\..\..\sshsh512.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit34] +FileName=..\..\..\sshsha.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit35] +FileName=..\..\..\sshsha3.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit36] +FileName=..\..\..\testcrypt.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit37] +FileName=..\..\..\tree234.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit38] +FileName=..\..\..\utils.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit39] +FileName=..\..\..\windows\winmiscs.c +Folder=Source Files +Compile=1 +CompileCpp=0 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit40] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 @@ -312,7 +432,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit29] +[Unit41] FileName=..\..\..\defs.h Folder=Header Files Compile=1 @@ -322,7 +442,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit30] +[Unit42] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 @@ -332,7 +452,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit31] +[Unit43] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 @@ -342,7 +462,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit32] +[Unit44] FileName=..\..\..\misc.h Folder=Header Files Compile=1 @@ -352,7 +472,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit33] +[Unit45] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 @@ -362,7 +482,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit34] +[Unit46] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 @@ -372,7 +492,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit35] +[Unit47] +FileName=..\..\..\mpunsafe.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit48] FileName=..\..\..\network.h Folder=Header Files Compile=1 @@ -382,7 +512,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit36] +[Unit49] FileName=..\..\..\putty.h Folder=Header Files Compile=1 @@ -392,7 +522,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit37] +[Unit50] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 @@ -402,7 +532,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit38] +[Unit51] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 @@ -412,7 +542,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit39] +[Unit52] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 @@ -422,7 +552,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit40] +[Unit53] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 @@ -432,7 +562,17 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit41] +[Unit54] +FileName=..\..\..\sshkeygen.h +Folder=Header Files +Compile=1 +CompileCpp=1 +Link=1 +Priority=1000 +OverrideBuildCmd=0 +BuildCmd= + +[Unit55] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 @@ -442,7 +582,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit42] +[Unit56] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 @@ -452,7 +592,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit43] +[Unit57] FileName=..\..\..\testcrypt.h Folder=Header Files Compile=1 @@ -462,7 +602,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit44] +[Unit58] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 @@ -472,7 +612,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit45] +[Unit59] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 @@ -482,7 +622,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit46] +[Unit60] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 @@ -492,7 +632,7 @@ Priority=1000 OverrideBuildCmd=0 BuildCmd= -[Unit47] +[Unit61] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 diff --git a/windows/MSVC/pageant/pageant.dsp b/windows/MSVC/pageant/pageant.dsp index 0225ec8..337ec66 100644 --- a/windows/MSVC/pageant/pageant.dsp +++ b/windows/MSVC/pageant/pageant.dsp @@ -98,6 +98,14 @@ SOURCE=..\..\..\aqsync.c # End Source File # Begin Source File +SOURCE=..\..\..\be_misc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\callback.c +# End Source File +# Begin Source File + SOURCE=..\..\..\conf.c # End Source File # Begin Source File @@ -106,6 +114,10 @@ SOURCE=..\..\..\ecc.c # End Source File # Begin Source File +SOURCE=..\..\..\errsock.c +# End Source File +# Begin Source File + SOURCE=..\..\..\marshal.c # End Source File # Begin Source File @@ -130,10 +142,18 @@ SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File @@ -174,6 +194,10 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File @@ -194,10 +218,22 @@ SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\wincapi.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\windows\winhandl.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winhelp.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winhsock.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File @@ -206,6 +242,18 @@ SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winnet.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\windows\winnpc.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\windows\winnps.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winpgnt.c # End Source File # Begin Source File @@ -218,6 +266,10 @@ SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winselgui.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winutils.c # End Source File # End Group @@ -270,6 +322,10 @@ SOURCE=..\..\..\pageant.h # End Source File # Begin Source File +SOURCE=..\..\..\proxy.h +# End Source File +# Begin Source File + SOURCE=..\..\..\putty.h # End Source File # Begin Source File @@ -314,6 +370,10 @@ SOURCE=..\..\..\version.h # End Source File # Begin Source File +SOURCE=..\..\..\windows\pageant-rc.h +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File @@ -322,6 +382,10 @@ SOURCE=..\..\..\windows\win_res.h # End Source File # Begin Source File +SOURCE=..\..\..\windows\wincapi.h +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File diff --git a/windows/MSVC/plink/plink.dsp b/windows/MSVC/plink/plink.dsp index 08e1765..71402de 100644 --- a/windows/MSVC/plink/plink.dsp +++ b/windows/MSVC/plink/plink.dsp @@ -114,6 +114,10 @@ SOURCE=..\..\..\callback.c # End Source File # Begin Source File +SOURCE=..\..\..\clicons.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File @@ -122,6 +126,10 @@ SOURCE=..\..\..\conf.c # End Source File # Begin Source File +SOURCE=..\..\..\console.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File @@ -287,10 +295,18 @@ SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File @@ -371,10 +387,18 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File +SOURCE=..\..\..\sshutils.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File @@ -387,6 +411,10 @@ SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File +SOURCE=..\..\..\supdup.c +# End Source File +# Begin Source File + SOURCE=..\..\..\telnet.c # End Source File # Begin Source File @@ -419,6 +447,10 @@ SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\wincliloop.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File @@ -487,6 +519,10 @@ SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winselcli.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winser.c # End Source File # Begin Source File @@ -523,6 +559,10 @@ SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File +SOURCE=..\..\..\console.h +# End Source File +# Begin Source File + SOURCE=..\..\..\defs.h # End Source File # Begin Source File diff --git a/windows/MSVC/pscp/pscp.dsp b/windows/MSVC/pscp/pscp.dsp index d8c15bd..584a513 100644 --- a/windows/MSVC/pscp/pscp.dsp +++ b/windows/MSVC/pscp/pscp.dsp @@ -123,6 +123,10 @@ SOURCE=..\..\..\callback.c # End Source File # Begin Source File +SOURCE=..\..\..\clicons.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File @@ -131,6 +135,10 @@ SOURCE=..\..\..\conf.c # End Source File # Begin Source File +SOURCE=..\..\..\console.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File @@ -292,10 +300,18 @@ SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File @@ -376,10 +392,18 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File +SOURCE=..\..\..\sshutils.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File @@ -420,6 +444,10 @@ SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\wincliloop.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File @@ -484,6 +512,10 @@ SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winselcli.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winsftp.c # End Source File # Begin Source File @@ -516,6 +548,10 @@ SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File +SOURCE=..\..\..\console.h +# End Source File +# Begin Source File + SOURCE=..\..\..\defs.h # End Source File # Begin Source File diff --git a/windows/MSVC/psftp/psftp.dsp b/windows/MSVC/psftp/psftp.dsp index 2797af3..8edbc14 100644 --- a/windows/MSVC/psftp/psftp.dsp +++ b/windows/MSVC/psftp/psftp.dsp @@ -123,6 +123,10 @@ SOURCE=..\..\..\callback.c # End Source File # Begin Source File +SOURCE=..\..\..\clicons.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File @@ -131,6 +135,10 @@ SOURCE=..\..\..\conf.c # End Source File # Begin Source File +SOURCE=..\..\..\console.c +# End Source File +# Begin Source File + SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File @@ -292,10 +300,18 @@ SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File @@ -376,10 +392,18 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File +SOURCE=..\..\..\sshutils.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File @@ -420,6 +444,10 @@ SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\wincliloop.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File @@ -484,6 +512,10 @@ SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winselcli.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winsftp.c # End Source File # Begin Source File @@ -516,6 +548,10 @@ SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File +SOURCE=..\..\..\console.h +# End Source File +# Begin Source File + SOURCE=..\..\..\defs.h # End Source File # Begin Source File diff --git a/windows/MSVC/putty.dsw b/windows/MSVC/putty.dsw index 03e98ad..0c494d2 100644 --- a/windows/MSVC/putty.dsw +++ b/windows/MSVC/putty.dsw @@ -7,6 +7,7 @@ Project: "pageant"=".\pageant\pageant.dsp" - Package Owner=<4> Project: "plink"=".\plink\plink.dsp" - Package Owner=<4> Project: "pscp"=".\pscp\pscp.dsp" - Package Owner=<4> Project: "psftp"=".\psftp\psftp.dsp" - Package Owner=<4> +Project: "psocks"=".\psocks\psocks.dsp" - Package Owner=<4> Project: "putty"=".\putty\putty.dsp" - Package Owner=<4> Project: "puttygen"=".\puttygen\puttygen.dsp" - Package Owner=<4> Project: "puttytel"=".\puttytel\puttytel.dsp" - Package Owner=<4> diff --git a/windows/MSVC/putty/putty.dsp b/windows/MSVC/putty/putty.dsp index c17314d..a678aaf 100644 --- a/windows/MSVC/putty/putty.dsp +++ b/windows/MSVC/putty/putty.dsp @@ -210,10 +210,6 @@ SOURCE=..\..\..\rlogin.c # End Source File # Begin Source File -SOURCE=..\..\..\sercfg.c -# End Source File -# Begin Source File - SOURCE=..\..\..\sessprep.c # End Source File # Begin Source File @@ -299,10 +295,18 @@ SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File @@ -383,10 +387,18 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File +SOURCE=..\..\..\sshutils.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File @@ -399,6 +411,10 @@ SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File +SOURCE=..\..\..\supdup.c +# End Source File +# Begin Source File + SOURCE=..\..\..\telnet.c # End Source File # Begin Source File @@ -519,6 +535,10 @@ SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winselgui.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winser.c # End Source File # Begin Source File @@ -719,6 +739,10 @@ SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File +SOURCE=..\..\..\windows\winseat.h +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File diff --git a/windows/MSVC/puttygen/puttygen.dsp b/windows/MSVC/puttygen/puttygen.dsp index 20345ac..3f44baa 100644 --- a/windows/MSVC/puttygen/puttygen.dsp +++ b/windows/MSVC/puttygen/puttygen.dsp @@ -114,6 +114,10 @@ SOURCE=..\..\..\memory.c # End Source File # Begin Source File +SOURCE=..\..\..\millerrabin.c +# End Source File +# Begin Source File + SOURCE=..\..\..\misc.c # End Source File # Begin Source File @@ -122,14 +126,34 @@ SOURCE=..\..\..\mpint.c # End Source File # Begin Source File +SOURCE=..\..\..\mpunsafe.c +# End Source File +# Begin Source File + SOURCE=..\..\..\notiming.c # End Source File # Begin Source File +SOURCE=..\..\..\pockle.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\primecandidate.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\smallprimes.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File @@ -138,6 +162,10 @@ SOURCE=..\..\..\sshbcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File @@ -206,6 +234,10 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File @@ -314,6 +346,10 @@ SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File +SOURCE=..\..\..\mpunsafe.h +# End Source File +# Begin Source File + SOURCE=..\..\..\network.h # End Source File # Begin Source File @@ -338,6 +374,10 @@ SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File +SOURCE=..\..\..\sshkeygen.h +# End Source File +# Begin Source File + SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File @@ -366,6 +406,10 @@ SOURCE=..\..\..\version.h # End Source File # Begin Source File +SOURCE=..\..\..\windows\puttygen-rc.h +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File diff --git a/windows/MSVC/puttytel/puttytel.dsp b/windows/MSVC/puttytel/puttytel.dsp index 16da18e..ce45ed7 100644 --- a/windows/MSVC/puttytel/puttytel.dsp +++ b/windows/MSVC/puttytel/puttytel.dsp @@ -162,6 +162,10 @@ SOURCE=..\..\..\nogss.c # End Source File # Begin Source File +SOURCE=..\..\..\norand.c +# End Source File +# Begin Source File + SOURCE=..\..\..\pinger.c # End Source File # Begin Source File @@ -178,10 +182,6 @@ SOURCE=..\..\..\rlogin.c # End Source File # Begin Source File -SOURCE=..\..\..\sercfg.c -# End Source File -# Begin Source File - SOURCE=..\..\..\sessprep.c # End Source File # Begin Source File @@ -194,6 +194,10 @@ SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File +SOURCE=..\..\..\supdup.c +# End Source File +# Begin Source File + SOURCE=..\..\..\telnet.c # End Source File # Begin Source File @@ -286,6 +290,10 @@ SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File +SOURCE=..\..\..\windows\winselgui.c +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winser.c # End Source File # Begin Source File @@ -418,6 +426,10 @@ SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File +SOURCE=..\..\..\windows\winseat.h +# End Source File +# Begin Source File + SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File diff --git a/windows/MSVC/testcrypt/testcrypt.dsp b/windows/MSVC/testcrypt/testcrypt.dsp index 7145583..5cbafbe 100644 --- a/windows/MSVC/testcrypt/testcrypt.dsp +++ b/windows/MSVC/testcrypt/testcrypt.dsp @@ -106,10 +106,30 @@ SOURCE=..\..\..\memory.c # End Source File # Begin Source File +SOURCE=..\..\..\millerrabin.c +# End Source File +# Begin Source File + SOURCE=..\..\..\mpint.c # End Source File # Begin Source File +SOURCE=..\..\..\mpunsafe.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\pockle.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\primecandidate.c +# End Source File +# Begin Source File + +SOURCE=..\..\..\smallprimes.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File @@ -118,10 +138,18 @@ SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File +SOURCE=..\..\..\sshargon2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File +SOURCE=..\..\..\sshblake2.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File @@ -150,10 +178,18 @@ SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File +SOURCE=..\..\..\sshdssg.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File +SOURCE=..\..\..\sshecdsag.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File @@ -170,10 +206,18 @@ SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File +SOURCE=..\..\..\sshpubk.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File +SOURCE=..\..\..\sshrsag.c +# End Source File +# Begin Source File + SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File @@ -186,6 +230,10 @@ SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File +SOURCE=..\..\..\sshsha3.c +# End Source File +# Begin Source File + SOURCE=..\..\..\testcrypt.c # End Source File # Begin Source File @@ -234,6 +282,10 @@ SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File +SOURCE=..\..\..\mpunsafe.h +# End Source File +# Begin Source File + SOURCE=..\..\..\network.h # End Source File # Begin Source File @@ -258,6 +310,10 @@ SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File +SOURCE=..\..\..\sshkeygen.h +# End Source File +# Begin Source File + SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File diff --git a/windows/Makefile.clangcl b/windows/Makefile.clangcl index 1b256a6..c97f373 100644 --- a/windows/Makefile.clangcl +++ b/windows/Makefile.clangcl @@ -68,11 +68,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=/DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # @@ -127,65 +122,79 @@ CFLAGS += /DHAS_GSSAPI all: $(BUILDDIR)pageant.exe $(BUILDDIR)plink.exe $(BUILDDIR)pscp.exe \ - $(BUILDDIR)psftp.exe $(BUILDDIR)putty.exe \ - $(BUILDDIR)puttygen.exe $(BUILDDIR)puttytel.exe \ - $(BUILDDIR)testcrypt.exe + $(BUILDDIR)psftp.exe $(BUILDDIR)psocks.exe \ + $(BUILDDIR)putty.exe $(BUILDDIR)puttygen.exe \ + $(BUILDDIR)puttytel.exe $(BUILDDIR)testcrypt.exe -$(BUILDDIR)pageant.exe: $(BUILDDIR)aqsync.obj $(BUILDDIR)conf.obj \ - $(BUILDDIR)ecc.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ - $(BUILDDIR)mpint.obj $(BUILDDIR)pageant.obj \ - $(BUILDDIR)pageant.res $(BUILDDIR)sshaes.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshdes.obj \ - $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ - $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ - $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrsa.obj \ - $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)stripctrl.obj \ +$(BUILDDIR)pageant.exe: $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ + $(BUILDDIR)callback.obj $(BUILDDIR)conf.obj \ + $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ + $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ + $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ + $(BUILDDIR)pageant.obj $(BUILDDIR)pageant.res \ + $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ + $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ + $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ + $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ + $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshpubk.obj \ + $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshsh256.obj \ + $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ + $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ - $(BUILDDIR)winhelp.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winpgnt.obj \ + $(BUILDDIR)wincapi.obj $(BUILDDIR)winhandl.obj \ + $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnpc.obj \ + $(BUILDDIR)winnps.obj $(BUILDDIR)winpgnt.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winsecur.obj \ - $(BUILDDIR)winutils.obj + $(BUILDDIR)winselgui.obj $(BUILDDIR)winutils.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)pageant.exe \ /lldmap:$(BUILDDIR)pageant.map \ /subsystem:windows$(SUBSYSVER) $(EXTRA_windows) advapi32.lib \ - $(BUILDDIR)aqsync.obj comdlg32.lib $(BUILDDIR)conf.obj \ - $(BUILDDIR)ecc.obj gdi32.lib imm32.lib \ - $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ + $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ + $(BUILDDIR)callback.obj comdlg32.lib $(BUILDDIR)conf.obj \ + $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj gdi32.lib \ + imm32.lib $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj ole32.lib \ $(BUILDDIR)pageant.obj $(BUILDDIR)pageant.res shell32.lib \ - $(BUILDDIR)sshaes.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ + $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)tree234.obj user32.lib \ - $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ - $(BUILDDIR)wcwidth.obj $(BUILDDIR)winhelp.obj \ + $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ + $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ + $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ + $(BUILDDIR)wincapi.obj $(BUILDDIR)winhandl.obj \ + $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ - $(BUILDDIR)winpgnt.obj $(BUILDDIR)winpgntc.obj \ - $(BUILDDIR)winsecur.obj $(BUILDDIR)winutils.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnpc.obj \ + $(BUILDDIR)winnps.obj $(BUILDDIR)winpgnt.obj \ + $(BUILDDIR)winpgntc.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)winselgui.obj $(BUILDDIR)winutils.obj \ $(EXTRA_libs) $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_all_s.obj $(BUILDDIR)be_misc.obj \ - $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj \ - $(BUILDDIR)conf.obj $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ - $(BUILDDIR)errsock.obj $(BUILDDIR)ldisc.obj \ - $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ - $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ - $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ - $(BUILDDIR)mpint.obj $(BUILDDIR)noterm.obj \ - $(BUILDDIR)nullplug.obj $(BUILDDIR)pgssapi.obj \ - $(BUILDDIR)pinger.obj $(BUILDDIR)plink.res \ - $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ - $(BUILDDIR)raw.obj $(BUILDDIR)rlogin.obj \ - $(BUILDDIR)sessprep.obj $(BUILDDIR)settings.obj \ - $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ - $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ + $(BUILDDIR)callback.obj $(BUILDDIR)clicons.obj \ + $(BUILDDIR)cmdline.obj $(BUILDDIR)conf.obj \ + $(BUILDDIR)console.obj $(BUILDDIR)cproxy.obj \ + $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ + $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ + $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ + $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ + $(BUILDDIR)miscucs.obj $(BUILDDIR)mpint.obj \ + $(BUILDDIR)noterm.obj $(BUILDDIR)nullplug.obj \ + $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ + $(BUILDDIR)plink.res $(BUILDDIR)portfwd.obj \ + $(BUILDDIR)proxy.obj $(BUILDDIR)raw.obj \ + $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ + $(BUILDDIR)settings.obj $(BUILDDIR)ssh.obj \ + $(BUILDDIR)ssh1bpp.obj $(BUILDDIR)ssh1censor.obj \ + $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ @@ -194,7 +203,8 @@ $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -204,13 +214,15 @@ $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)telnet.obj \ - $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj \ - $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ - $(BUILDDIR)wcwidth.obj $(BUILDDIR)wildcard.obj \ - $(BUILDDIR)wincapi.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ + $(BUILDDIR)telnet.obj $(BUILDDIR)timing.obj \ + $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ + $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ + $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ @@ -219,16 +231,17 @@ $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winplink.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ - $(BUILDDIR)winser.obj $(BUILDDIR)winshare.obj \ - $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ - $(BUILDDIR)winucs.obj $(BUILDDIR)winx11.obj \ - $(BUILDDIR)x11fwd.obj + $(BUILDDIR)winselcli.obj $(BUILDDIR)winser.obj \ + $(BUILDDIR)winshare.obj $(BUILDDIR)winstore.obj \ + $(BUILDDIR)wintime.obj $(BUILDDIR)winucs.obj \ + $(BUILDDIR)winx11.obj $(BUILDDIR)x11fwd.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)plink.exe \ /lldmap:$(BUILDDIR)plink.map /subsystem:console$(SUBSYSVER) \ $(EXTRA_console) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_all_s.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)callback.obj \ - $(BUILDDIR)cmdline.obj comdlg32.lib $(BUILDDIR)conf.obj \ + $(BUILDDIR)clicons.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ + $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ @@ -251,7 +264,8 @@ $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -261,13 +275,15 @@ $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)telnet.obj \ - $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ - $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ - $(BUILDDIR)wcwidth.obj $(BUILDDIR)wildcard.obj \ - $(BUILDDIR)wincapi.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ + $(BUILDDIR)telnet.obj $(BUILDDIR)timing.obj \ + $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ + $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ + $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ @@ -276,22 +292,24 @@ $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winplink.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ - $(BUILDDIR)winser.obj $(BUILDDIR)winshare.obj \ - $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ - $(BUILDDIR)winucs.obj $(BUILDDIR)winx11.obj \ - $(BUILDDIR)x11fwd.obj $(EXTRA_libs) + $(BUILDDIR)winselcli.obj $(BUILDDIR)winser.obj \ + $(BUILDDIR)winshare.obj $(BUILDDIR)winstore.obj \ + $(BUILDDIR)wintime.obj $(BUILDDIR)winucs.obj \ + $(BUILDDIR)winx11.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)be_ssh.obj \ - $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj \ - $(BUILDDIR)conf.obj $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ - $(BUILDDIR)errsock.obj $(BUILDDIR)logging.obj \ - $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ - $(BUILDDIR)miscucs.obj $(BUILDDIR)mpint.obj \ - $(BUILDDIR)nullplug.obj $(BUILDDIR)pgssapi.obj \ - $(BUILDDIR)pinger.obj $(BUILDDIR)portfwd.obj \ - $(BUILDDIR)proxy.obj $(BUILDDIR)pscp.obj $(BUILDDIR)pscp.res \ + $(BUILDDIR)callback.obj $(BUILDDIR)clicons.obj \ + $(BUILDDIR)cmdline.obj $(BUILDDIR)conf.obj \ + $(BUILDDIR)console.obj $(BUILDDIR)cproxy.obj \ + $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ + $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ + $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ + $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ + $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj \ + $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ + $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ + $(BUILDDIR)pscp.obj $(BUILDDIR)pscp.res \ $(BUILDDIR)psftpcommon.obj $(BUILDDIR)settings.obj \ $(BUILDDIR)sftp.obj $(BUILDDIR)sftpcommon.obj \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ @@ -304,7 +322,8 @@ $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -314,20 +333,22 @@ $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ - $(BUILDDIR)wincons.obj $(BUILDDIR)windefs.obj \ - $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ - $(BUILDDIR)winhsock.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ - $(BUILDDIR)winnohlp.obj $(BUILDDIR)winnoise.obj \ - $(BUILDDIR)winnojmp.obj $(BUILDDIR)winnpc.obj \ - $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ - $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ + $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ + $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ + $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj @@ -336,7 +357,8 @@ $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(EXTRA_console) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)be_ssh.obj $(BUILDDIR)callback.obj \ - $(BUILDDIR)cmdline.obj comdlg32.lib $(BUILDDIR)conf.obj \ + $(BUILDDIR)clicons.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ + $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ @@ -358,7 +380,8 @@ $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -368,40 +391,43 @@ $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ - $(BUILDDIR)wincons.obj $(BUILDDIR)windefs.obj \ - $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ - $(BUILDDIR)winhsock.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ - $(BUILDDIR)winnohlp.obj $(BUILDDIR)winnoise.obj \ - $(BUILDDIR)winnojmp.obj $(BUILDDIR)winnpc.obj \ - $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ - $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ + $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ + $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ + $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)be_ssh.obj \ - $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj \ - $(BUILDDIR)conf.obj $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ - $(BUILDDIR)errsock.obj $(BUILDDIR)logging.obj \ - $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ - $(BUILDDIR)miscucs.obj $(BUILDDIR)mpint.obj \ - $(BUILDDIR)nullplug.obj $(BUILDDIR)pgssapi.obj \ - $(BUILDDIR)pinger.obj $(BUILDDIR)portfwd.obj \ - $(BUILDDIR)proxy.obj $(BUILDDIR)psftp.obj \ - $(BUILDDIR)psftp.res $(BUILDDIR)psftpcommon.obj \ - $(BUILDDIR)settings.obj $(BUILDDIR)sftp.obj \ - $(BUILDDIR)sftpcommon.obj $(BUILDDIR)ssh.obj \ - $(BUILDDIR)ssh1bpp.obj $(BUILDDIR)ssh1censor.obj \ - $(BUILDDIR)ssh1connection.obj \ + $(BUILDDIR)callback.obj $(BUILDDIR)clicons.obj \ + $(BUILDDIR)cmdline.obj $(BUILDDIR)conf.obj \ + $(BUILDDIR)console.obj $(BUILDDIR)cproxy.obj \ + $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ + $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ + $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ + $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ + $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj \ + $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ + $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ + $(BUILDDIR)psftp.obj $(BUILDDIR)psftp.res \ + $(BUILDDIR)psftpcommon.obj $(BUILDDIR)settings.obj \ + $(BUILDDIR)sftp.obj $(BUILDDIR)sftpcommon.obj \ + $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ + $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ @@ -410,7 +436,8 @@ $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -420,20 +447,22 @@ $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ - $(BUILDDIR)wincons.obj $(BUILDDIR)windefs.obj \ - $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ - $(BUILDDIR)winhsock.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ - $(BUILDDIR)winnohlp.obj $(BUILDDIR)winnoise.obj \ - $(BUILDDIR)winnojmp.obj $(BUILDDIR)winnpc.obj \ - $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ - $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ + $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ + $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ + $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj @@ -442,7 +471,8 @@ $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(EXTRA_console) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)be_ssh.obj $(BUILDDIR)callback.obj \ - $(BUILDDIR)cmdline.obj comdlg32.lib $(BUILDDIR)conf.obj \ + $(BUILDDIR)clicons.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ + $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ @@ -464,7 +494,8 @@ $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -474,24 +505,64 @@ $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ - $(BUILDDIR)wincons.obj $(BUILDDIR)windefs.obj \ - $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ - $(BUILDDIR)winhsock.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ - $(BUILDDIR)winnohlp.obj $(BUILDDIR)winnoise.obj \ - $(BUILDDIR)winnojmp.obj $(BUILDDIR)winnpc.obj \ - $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ - $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ + $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ + $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ + $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) +$(BUILDDIR)psocks.exe: $(BUILDDIR)be_misc.obj $(BUILDDIR)callback.obj \ + $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ + $(BUILDDIR)errsock.obj $(BUILDDIR)logging.obj \ + $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ + $(BUILDDIR)misc.obj $(BUILDDIR)nocproxy.obj \ + $(BUILDDIR)norand.obj $(BUILDDIR)portfwd.obj \ + $(BUILDDIR)proxy.obj $(BUILDDIR)psocks.obj \ + $(BUILDDIR)sshutils.obj $(BUILDDIR)stripctrl.obj \ + $(BUILDDIR)time.obj $(BUILDDIR)timing.obj \ + $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ + $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ + $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ + $(BUILDDIR)winproxy.obj $(BUILDDIR)winselcli.obj \ + $(BUILDDIR)winsocks.obj + $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)psocks.exe \ + /lldmap:$(BUILDDIR)psocks.map /subsystem:console$(SUBSYSVER) \ + $(EXTRA_console) advapi32.lib $(BUILDDIR)be_misc.obj \ + $(BUILDDIR)callback.obj comdlg32.lib $(BUILDDIR)conf.obj \ + $(BUILDDIR)console.obj $(BUILDDIR)errsock.obj gdi32.lib \ + imm32.lib $(BUILDDIR)logging.obj $(BUILDDIR)marshal.obj \ + $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ + $(BUILDDIR)nocproxy.obj $(BUILDDIR)norand.obj ole32.lib \ + $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ + $(BUILDDIR)psocks.obj shell32.lib $(BUILDDIR)sshutils.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)time.obj \ + $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ + $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ + $(BUILDDIR)wcwidth.obj $(BUILDDIR)wincliloop.obj \ + $(BUILDDIR)wincons.obj $(BUILDDIR)winhandl.obj \ + $(BUILDDIR)winhsock.obj $(BUILDDIR)winmisc.obj \ + $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ + $(BUILDDIR)winnohlp.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winselcli.obj $(BUILDDIR)winsocks.obj \ + $(EXTRA_libs) + $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_all_s.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj \ @@ -506,11 +577,10 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)putty.res $(BUILDDIR)raw.obj \ - $(BUILDDIR)rlogin.obj $(BUILDDIR)sercfg.obj \ - $(BUILDDIR)sessprep.obj $(BUILDDIR)settings.obj \ - $(BUILDDIR)sizetip.obj $(BUILDDIR)ssh.obj \ - $(BUILDDIR)ssh1bpp.obj $(BUILDDIR)ssh1censor.obj \ - $(BUILDDIR)ssh1connection.obj \ + $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ + $(BUILDDIR)settings.obj $(BUILDDIR)sizetip.obj \ + $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ + $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ @@ -519,7 +589,8 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -529,23 +600,25 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)telnet.obj \ - $(BUILDDIR)terminal.obj $(BUILDDIR)timing.obj \ - $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ - $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ - $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ - $(BUILDDIR)wincfg.obj $(BUILDDIR)winctrls.obj \ - $(BUILDDIR)windefs.obj $(BUILDDIR)windlg.obj \ - $(BUILDDIR)window.obj $(BUILDDIR)wingss.obj \ - $(BUILDDIR)winhandl.obj $(BUILDDIR)winhelp.obj \ - $(BUILDDIR)winhsock.obj $(BUILDDIR)winjump.obj \ - $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ - $(BUILDDIR)winnet.obj $(BUILDDIR)winnoise.obj \ - $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ - $(BUILDDIR)winpgntc.obj $(BUILDDIR)winprint.obj \ - $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ + $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ + $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj \ + $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ + $(BUILDDIR)wcwidth.obj $(BUILDDIR)wildcard.obj \ + $(BUILDDIR)wincapi.obj $(BUILDDIR)wincfg.obj \ + $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ + $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ + $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ + $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winjump.obj $(BUILDDIR)winmisc.obj \ + $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ + $(BUILDDIR)winnoise.obj $(BUILDDIR)winnpc.obj \ + $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ + $(BUILDDIR)winprint.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winsecur.obj $(BUILDDIR)winselgui.obj \ $(BUILDDIR)winser.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj \ @@ -567,11 +640,10 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)putty.res $(BUILDDIR)raw.obj \ - $(BUILDDIR)rlogin.obj $(BUILDDIR)sercfg.obj \ - $(BUILDDIR)sessprep.obj $(BUILDDIR)settings.obj shell32.lib \ - $(BUILDDIR)sizetip.obj $(BUILDDIR)ssh.obj \ - $(BUILDDIR)ssh1bpp.obj $(BUILDDIR)ssh1censor.obj \ - $(BUILDDIR)ssh1connection.obj \ + $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ + $(BUILDDIR)settings.obj shell32.lib $(BUILDDIR)sizetip.obj \ + $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ + $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ @@ -580,7 +652,8 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ @@ -590,23 +663,25 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)sshshare.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ + $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)telnet.obj \ - $(BUILDDIR)terminal.obj $(BUILDDIR)timing.obj \ - $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ - $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ - $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ - $(BUILDDIR)wincfg.obj $(BUILDDIR)winctrls.obj \ - $(BUILDDIR)windefs.obj $(BUILDDIR)windlg.obj \ - $(BUILDDIR)window.obj $(BUILDDIR)wingss.obj \ - $(BUILDDIR)winhandl.obj $(BUILDDIR)winhelp.obj \ - $(BUILDDIR)winhsock.obj $(BUILDDIR)winjump.obj \ - $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ - $(BUILDDIR)winnet.obj $(BUILDDIR)winnoise.obj \ - $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ - $(BUILDDIR)winpgntc.obj $(BUILDDIR)winprint.obj \ - $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ + $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ + $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ + $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ + $(BUILDDIR)wcwidth.obj $(BUILDDIR)wildcard.obj \ + $(BUILDDIR)wincapi.obj $(BUILDDIR)wincfg.obj \ + $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ + $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ + $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ + $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winjump.obj $(BUILDDIR)winmisc.obj \ + $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ + $(BUILDDIR)winnoise.obj $(BUILDDIR)winnpc.obj \ + $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ + $(BUILDDIR)winprint.obj $(BUILDDIR)winproxy.obj \ + $(BUILDDIR)winsecur.obj $(BUILDDIR)winselgui.obj \ $(BUILDDIR)winser.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj \ @@ -614,19 +689,23 @@ $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)puttygen.exe: $(BUILDDIR)conf.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)import.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ - $(BUILDDIR)mpint.obj $(BUILDDIR)notiming.obj \ - $(BUILDDIR)puttygen.res $(BUILDDIR)sshaes.obj \ + $(BUILDDIR)memory.obj $(BUILDDIR)millerrabin.obj \ + $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ + $(BUILDDIR)mpunsafe.obj $(BUILDDIR)notiming.obj \ + $(BUILDDIR)pockle.obj $(BUILDDIR)primecandidate.obj \ + $(BUILDDIR)puttygen.res $(BUILDDIR)smallprimes.obj \ + $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshbcrypt.obj \ - $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshdes.obj \ - $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ - $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ - $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ - $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ - $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrand.obj \ - $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshrsag.obj \ - $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)stripctrl.obj \ + $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ + $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ + $(BUILDDIR)sshdssg.obj $(BUILDDIR)sshecc.obj \ + $(BUILDDIR)sshecdsag.obj $(BUILDDIR)sshhmac.obj \ + $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshprime.obj \ + $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ + $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ + $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ + $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ + $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)winctrls.obj $(BUILDDIR)winhelp.obj \ @@ -641,18 +720,22 @@ $(BUILDDIR)puttygen.exe: $(BUILDDIR)conf.obj $(BUILDDIR)ecc.obj \ comdlg32.lib $(BUILDDIR)conf.obj $(BUILDDIR)ecc.obj \ gdi32.lib imm32.lib $(BUILDDIR)import.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ - $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ - $(BUILDDIR)notiming.obj ole32.lib $(BUILDDIR)puttygen.res \ - shell32.lib $(BUILDDIR)sshaes.obj $(BUILDDIR)sshauxcrypt.obj \ - $(BUILDDIR)sshbcrypt.obj $(BUILDDIR)sshblowf.obj \ - $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ - $(BUILDDIR)sshdssg.obj $(BUILDDIR)sshecc.obj \ - $(BUILDDIR)sshecdsag.obj $(BUILDDIR)sshhmac.obj \ - $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshprime.obj \ - $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ - $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ - $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ - $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ + $(BUILDDIR)millerrabin.obj $(BUILDDIR)misc.obj \ + $(BUILDDIR)mpint.obj $(BUILDDIR)mpunsafe.obj \ + $(BUILDDIR)notiming.obj ole32.lib $(BUILDDIR)pockle.obj \ + $(BUILDDIR)primecandidate.obj $(BUILDDIR)puttygen.res \ + shell32.lib $(BUILDDIR)smallprimes.obj $(BUILDDIR)sshaes.obj \ + $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ + $(BUILDDIR)sshbcrypt.obj $(BUILDDIR)sshblake2.obj \ + $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshdes.obj \ + $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ + $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ + $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ + $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ + $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrand.obj \ + $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshrsag.obj \ + $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ + $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)tree234.obj user32.lib \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)winctrls.obj \ @@ -670,23 +753,24 @@ $(BUILDDIR)puttytel.exe: $(BUILDDIR)be_misc.obj $(BUILDDIR)be_nos_s.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)minibidi.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)miscucs.obj $(BUILDDIR)nocproxy.obj \ - $(BUILDDIR)nogss.obj $(BUILDDIR)pinger.obj \ - $(BUILDDIR)proxy.obj $(BUILDDIR)puttytel.res \ - $(BUILDDIR)raw.obj $(BUILDDIR)rlogin.obj \ - $(BUILDDIR)sercfg.obj $(BUILDDIR)sessprep.obj \ + $(BUILDDIR)nogss.obj $(BUILDDIR)norand.obj \ + $(BUILDDIR)pinger.obj $(BUILDDIR)proxy.obj \ + $(BUILDDIR)puttytel.res $(BUILDDIR)raw.obj \ + $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj $(BUILDDIR)sizetip.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)telnet.obj \ - $(BUILDDIR)terminal.obj $(BUILDDIR)timing.obj \ - $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ - $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ - $(BUILDDIR)wincfg.obj $(BUILDDIR)winctrls.obj \ - $(BUILDDIR)windefs.obj $(BUILDDIR)windlg.obj \ - $(BUILDDIR)window.obj $(BUILDDIR)winhandl.obj \ - $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ - $(BUILDDIR)winjump.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ - $(BUILDDIR)winprint.obj $(BUILDDIR)winproxy.obj \ - $(BUILDDIR)winsecur.obj $(BUILDDIR)winser.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ + $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ + $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj \ + $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ + $(BUILDDIR)wcwidth.obj $(BUILDDIR)wincfg.obj \ + $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ + $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhelp.obj \ + $(BUILDDIR)winhsock.obj $(BUILDDIR)winjump.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winprint.obj \ + $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)winselgui.obj $(BUILDDIR)winser.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)puttytel.exe \ @@ -700,55 +784,68 @@ $(BUILDDIR)puttytel.exe: $(BUILDDIR)be_misc.obj $(BUILDDIR)be_nos_s.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)minibidi.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)miscucs.obj $(BUILDDIR)nocproxy.obj \ - $(BUILDDIR)nogss.obj ole32.lib $(BUILDDIR)pinger.obj \ - $(BUILDDIR)proxy.obj $(BUILDDIR)puttytel.res \ - $(BUILDDIR)raw.obj $(BUILDDIR)rlogin.obj \ - $(BUILDDIR)sercfg.obj $(BUILDDIR)sessprep.obj \ + $(BUILDDIR)nogss.obj $(BUILDDIR)norand.obj ole32.lib \ + $(BUILDDIR)pinger.obj $(BUILDDIR)proxy.obj \ + $(BUILDDIR)puttytel.res $(BUILDDIR)raw.obj \ + $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj shell32.lib $(BUILDDIR)sizetip.obj \ - $(BUILDDIR)stripctrl.obj $(BUILDDIR)telnet.obj \ - $(BUILDDIR)terminal.obj $(BUILDDIR)timing.obj \ - $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ - $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ - $(BUILDDIR)wincfg.obj $(BUILDDIR)winctrls.obj \ - $(BUILDDIR)windefs.obj $(BUILDDIR)windlg.obj \ - $(BUILDDIR)window.obj $(BUILDDIR)winhandl.obj \ - $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ - $(BUILDDIR)winjump.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ - $(BUILDDIR)winprint.obj $(BUILDDIR)winproxy.obj \ - $(BUILDDIR)winsecur.obj $(BUILDDIR)winser.obj \ + $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ + $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ + $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ + $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ + $(BUILDDIR)wcwidth.obj $(BUILDDIR)wincfg.obj \ + $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ + $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ + $(BUILDDIR)winhandl.obj $(BUILDDIR)winhelp.obj \ + $(BUILDDIR)winhsock.obj $(BUILDDIR)winjump.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winprint.obj \ + $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ + $(BUILDDIR)winselgui.obj $(BUILDDIR)winser.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj $(EXTRA_libs) $(BUILDDIR)testcrypt.exe: $(BUILDDIR)ecc.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)mpint.obj \ - $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ - $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcrc.obj \ - $(BUILDDIR)sshcrcda.obj $(BUILDDIR)sshdes.obj \ - $(BUILDDIR)sshdh.obj $(BUILDDIR)sshdss.obj \ - $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ - $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshprime.obj \ - $(BUILDDIR)sshprng.obj $(BUILDDIR)sshrsa.obj \ - $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)testcrypt.obj \ + $(BUILDDIR)memory.obj $(BUILDDIR)millerrabin.obj \ + $(BUILDDIR)mpint.obj $(BUILDDIR)mpunsafe.obj \ + $(BUILDDIR)pockle.obj $(BUILDDIR)primecandidate.obj \ + $(BUILDDIR)smallprimes.obj $(BUILDDIR)sshaes.obj \ + $(BUILDDIR)ssharcf.obj $(BUILDDIR)sshargon2.obj \ + $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ + $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshccp.obj \ + $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ + $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ + $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ + $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ + $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ + $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ + $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrsa.obj \ + $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ + $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ + $(BUILDDIR)sshsha3.obj $(BUILDDIR)testcrypt.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)winmiscs.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)testcrypt.exe \ /lldmap:$(BUILDDIR)testcrypt.map \ /subsystem:console$(SUBSYSVER) $(EXTRA_console) \ $(BUILDDIR)ecc.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)mpint.obj \ - $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblowf.obj \ - $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcrc.obj \ - $(BUILDDIR)sshcrcda.obj $(BUILDDIR)sshdes.obj \ - $(BUILDDIR)sshdh.obj $(BUILDDIR)sshdss.obj \ - $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ - $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshprime.obj \ - $(BUILDDIR)sshprng.obj $(BUILDDIR)sshrsa.obj \ - $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)testcrypt.obj \ + $(BUILDDIR)memory.obj $(BUILDDIR)millerrabin.obj \ + $(BUILDDIR)mpint.obj $(BUILDDIR)mpunsafe.obj \ + $(BUILDDIR)pockle.obj $(BUILDDIR)primecandidate.obj \ + $(BUILDDIR)smallprimes.obj $(BUILDDIR)sshaes.obj \ + $(BUILDDIR)ssharcf.obj $(BUILDDIR)sshargon2.obj \ + $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ + $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshccp.obj \ + $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ + $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ + $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ + $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ + $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ + $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ + $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrsa.obj \ + $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ + $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ + $(BUILDDIR)sshsha3.obj $(BUILDDIR)testcrypt.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)winmiscs.obj $(EXTRA_libs) @@ -801,18 +898,25 @@ $(BUILDDIR)callback.obj: ../callback.c ../putty.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)cgtest.obj: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ +$(BUILDDIR)cgtest.obj: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h \ + ../sshkeygen.h ../mpint.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)cmdgen.obj: ../cmdgen.c ../putty.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +$(BUILDDIR)clicons.obj: ../clicons.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + +$(BUILDDIR)cmdgen.obj: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)cmdline.obj: ../cmdline.c ../putty.h ../defs.h ../puttyps.h \ @@ -834,6 +938,13 @@ $(BUILDDIR)config.obj: ../config.c ../putty.h ../dialog.h ../storage.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)console.obj: ../console.c ../putty.h ../misc.h ../console.h \ + ../defs.h ../puttyps.h ../network.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../windows/winstuff.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)cproxy.obj: ../cproxy.c ../putty.h ../ssh.h ../network.h \ ../proxy.h ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -863,8 +974,8 @@ $(BUILDDIR)fromucs.obj: ../charset/fromucs.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)fuzzterm.obj: ../fuzzterm.c ../putty.h ../terminal.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ +$(BUILDDIR)fuzzterm.obj: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h @@ -907,10 +1018,10 @@ $(BUILDDIR)gtkcomm.obj: ../unix/gtkcomm.c ../putty.h ../terminal.h \ $(BUILDDIR)gtkdlg.obj: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h \ ../unix/gtkcols.h ../unix/gtkfont.h ../unix/gtkmisc.h \ ../unix/x11misc.h ../storage.h ../dialog.h ../tree234.h \ - ../licence.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h + ../licence.h ../ssh.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkfont.obj: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ @@ -987,6 +1098,12 @@ $(BUILDDIR)memory.obj: ../memory.c ../defs.h ../puttymem.h ../misc.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)millerrabin.obj: ../millerrabin.c ../ssh.h ../sshkeygen.h \ + ../mpint.h ../mpunsafe.h ../puttymem.h ../tree234.h \ + ../network.h ../misc.h ../sshttymodes.h ../defs.h \ + ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)mimeenc.obj: ../charset/mimeenc.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< @@ -1013,6 +1130,10 @@ $(BUILDDIR)mpint.obj: ../mpint.c ../defs.h ../misc.h ../puttymem.h \ ../mpint.h ../mpint_i.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)mpunsafe.obj: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h \ + ../mpint.h ../mpint_i.h ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)nocmdline.obj: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ @@ -1032,6 +1153,12 @@ $(BUILDDIR)nogss.obj: ../nogss.c ../putty.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)norand.obj: ../norand.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)noterm.obj: ../noterm.c ../putty.h ../terminal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ @@ -1062,9 +1189,9 @@ $(BUILDDIR)pageant.obj: ../pageant.c ../putty.h ../mpint.h ../ssh.h \ $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pageant.res: $(BUILDDIR)pageant.rcpp ../windows/rcstuff.h \ - ../windows/pageant.ico ../windows/pageants.ico \ - ../windows/pageant.mft ../windows/win_res.h ../version.h \ - ../licence.h + ../windows/pageant-rc.h ../windows/pageant.ico \ + ../windows/pageants.ico ../windows/pageant.mft \ + ../windows/win_res.h ../version.h ../licence.h $(RC) $(BUILDDIR)pageant.rcpp /FO $(BUILDDIR)pageant.res $(BUILDDIR)pgssapi.obj: ../pgssapi.c ../putty.h ../pgssapi.h ../defs.h \ @@ -1084,6 +1211,11 @@ $(BUILDDIR)plink.res: $(BUILDDIR)plink.rcpp ../windows/rcstuff.h \ ../windows/putty.ico ../version.h ../licence.h $(RC) $(BUILDDIR)plink.rcpp /FO $(BUILDDIR)plink.res +$(BUILDDIR)pockle.obj: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h \ + ../mpunsafe.h ../tree234.h ../puttymem.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)portfwd.obj: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -1091,6 +1223,12 @@ $(BUILDDIR)portfwd.obj: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)primecandidate.obj: ../primecandidate.c ../ssh.h ../mpint.h \ + ../mpunsafe.h ../sshkeygen.h ../puttymem.h ../tree234.h \ + ../network.h ../misc.h ../sshttymodes.h ../defs.h \ + ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)procnet.obj: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< @@ -1131,6 +1269,13 @@ $(BUILDDIR)psftpcommon.obj: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)psocks.obj: ../psocks.c ../putty.h ../misc.h ../ssh.h \ + ../sshchan.h ../psocks.h ../defs.h ../puttyps.h ../network.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)putty.res: $(BUILDDIR)putty.rcpp ../windows/rcstuff.h \ ../windows/putty.mft ../windows/win_res.h \ ../windows/putty.ico ../windows/puttycfg.ico ../version.h \ @@ -1138,8 +1283,9 @@ $(BUILDDIR)putty.res: $(BUILDDIR)putty.rcpp ../windows/rcstuff.h \ $(RC) $(BUILDDIR)putty.rcpp /FO $(BUILDDIR)putty.res $(BUILDDIR)puttygen.res: $(BUILDDIR)puttygen.rcpp ../windows/rcstuff.h \ - ../windows/puttygen.ico ../windows/puttygen.mft \ - ../windows/win_res.h ../version.h ../licence.h + ../windows/puttygen-rc.h ../windows/puttygen.ico \ + ../windows/puttygen.mft ../windows/win_res.h ../version.h \ + ../licence.h $(RC) $(BUILDDIR)puttygen.rcpp /FO $(BUILDDIR)puttygen.res $(BUILDDIR)puttytel.res: $(BUILDDIR)puttytel.rcpp ../windows/rcstuff.h \ @@ -1175,13 +1321,6 @@ $(BUILDDIR)scpserver.obj: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)sercfg.obj: ../sercfg.c ../putty.h ../dialog.h ../storage.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< - $(BUILDDIR)sesschan.obj: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h \ ../sshserver.h ../sftp.h ../sshsignals.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -1230,6 +1369,11 @@ $(BUILDDIR)slookup.obj: ../charset/slookup.c ../charset/charset.h \ ../charset/utf8.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)smallprimes.obj: ../smallprimes.c ../ssh.h ../sshkeygen.h \ + ../puttymem.h ../tree234.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)ssh.obj: ../ssh.c ../putty.h ../pageant.h ../tree234.h \ ../storage.h ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h \ @@ -1288,10 +1432,10 @@ $(BUILDDIR)ssh1login.obj: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h \ $(BUILDDIR)ssh1login-server.obj: ../ssh1login-server.c ../putty.h ../mpint.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2bpp.obj: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h \ @@ -1351,8 +1495,8 @@ $(BUILDDIR)ssh2kex-client.obj: ../ssh2kex-client.c ../putty.h ../ssh.h \ $(BUILDDIR)ssh2kex-server.obj: ../ssh2kex-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ - ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshkeygen.h ../storage.h ../ssh2transport.h ../mpint.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ @@ -1403,6 +1547,13 @@ $(BUILDDIR)ssharcf.obj: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)sshargon2.obj: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)sshauxcrypt.obj: ../sshauxcrypt.c ../ssh.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h @@ -1413,6 +1564,11 @@ $(BUILDDIR)sshbcrypt.obj: ../sshbcrypt.c ../ssh.h ../sshblowf.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)sshblake2.obj: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h \ + ../network.h ../misc.h ../sshttymodes.h ../defs.h \ + ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)sshblowf.obj: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h @@ -1456,9 +1612,9 @@ $(BUILDDIR)sshdss.obj: ../sshdss.c ../ssh.h ../mpint.h ../misc.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)sshdssg.obj: ../sshdssg.c ../misc.h ../ssh.h ../mpint.h ../defs.h \ - ../puttymem.h ../marshal.h ../tree234.h ../network.h \ - ../sshttymodes.h +$(BUILDDIR)sshdssg.obj: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h \ + ../mpint.h ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ + ../network.h ../sshttymodes.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshecc.obj: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h \ @@ -1466,9 +1622,9 @@ $(BUILDDIR)sshecc.obj: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)sshecdsag.obj: ../sshecdsag.c ../ssh.h ../mpint.h ../puttymem.h \ - ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ - ../defs.h ../marshal.h +$(BUILDDIR)sshecdsag.obj: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h \ + ../puttymem.h ../tree234.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshgssc.obj: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h \ @@ -1493,12 +1649,12 @@ $(BUILDDIR)sshmd5.obj: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)sshprime.obj: ../sshprime.c ../ssh.h ../mpint.h ../puttymem.h \ - ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ - ../defs.h ../marshal.h +$(BUILDDIR)sshprime.obj: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h \ + ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)sshprng.obj: ../sshprng.c ../putty.h ../ssh.h ../mpint.h \ +$(BUILDDIR)sshprng.obj: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ @@ -1524,17 +1680,17 @@ $(BUILDDIR)sshrsa.obj: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)sshrsag.obj: ../sshrsag.c ../ssh.h ../mpint.h ../puttymem.h \ - ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ - ../defs.h ../marshal.h +$(BUILDDIR)sshrsag.obj: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h \ + ../puttymem.h ../tree234.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshserver.obj: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h \ - ../sshppl.h ../sshserver.h ../sshgssc.h ../sshgss.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h + ../sshppl.h ../sshchan.h ../sshserver.h ../sshgssc.h \ + ../sshgss.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../pgssapi.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshsh256.obj: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h \ @@ -1552,6 +1708,11 @@ $(BUILDDIR)sshsha.obj: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)sshsha3.obj: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h \ + ../network.h ../misc.h ../sshttymodes.h ../defs.h \ + ../marshal.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)sshshare.obj: ../sshshare.c ../putty.h ../tree234.h ../ssh.h \ ../sshcr.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ @@ -1559,6 +1720,13 @@ $(BUILDDIR)sshshare.obj: ../sshshare.c ../putty.h ../tree234.h ../ssh.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)sshutils.obj: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)sshverstring.obj: ../sshverstring.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshcr.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ @@ -1578,6 +1746,12 @@ $(BUILDDIR)stripctrl.obj: ../stripctrl.c ../putty.h ../terminal.h ../misc.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)supdup.obj: ../supdup.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)telnet.obj: ../telnet.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ @@ -1591,8 +1765,8 @@ $(BUILDDIR)terminal.obj: ../terminal.c ../putty.h ../terminal.h ../defs.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)testcrypt.obj: ../testcrypt.c ../defs.h ../ssh.h ../misc.h \ - ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ +$(BUILDDIR)testcrypt.obj: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h \ + ../misc.h ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< @@ -1628,8 +1802,8 @@ $(BUILDDIR)utf8.obj: ../charset/utf8.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)utils.obj: ../utils.c ../defs.h ../misc.h ../puttymem.h \ - ../marshal.h +$(BUILDDIR)utils.obj: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h \ + ../marshal.h ../tree234.h ../network.h ../sshttymodes.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ux_x11.obj: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h \ @@ -1660,13 +1834,20 @@ $(BUILDDIR)uxcfg.obj: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)uxcons.obj: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ +$(BUILDDIR)uxcliloop.obj: ../unix/uxcliloop.c ../putty.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)uxcons.obj: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h \ + ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)uxfdsock.obj: ../unix/uxfdsock.c ../tree234.h ../putty.h \ ../network.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -1724,10 +1905,10 @@ $(BUILDDIR)uxpgnt.obj: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)uxplink.obj: ../unix/uxplink.c ../putty.h ../storage.h \ +$(BUILDDIR)uxplink.obj: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h \ ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< @@ -1750,24 +1931,31 @@ $(BUILDDIR)uxproxy.obj: ../unix/uxproxy.c ../tree234.h ../putty.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)uxpsusan.obj: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h \ + ../sshserver.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)uxpterm.obj: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)uxpty.obj: ../unix/uxpty.c ../putty.h ../ssh.h ../tree234.h \ - ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../puttymem.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +$(BUILDDIR)uxpty.obj: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h \ + ../tree234.h ../sshttymodes.h ../sshsignals.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)uxputty.obj: ../unix/uxputty.c ../putty.h ../storage.h \ +$(BUILDDIR)uxputty.obj: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsel.obj: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h \ @@ -1797,10 +1985,10 @@ $(BUILDDIR)uxsftp.obj: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h \ $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsftpserver.obj: ../unix/uxsftpserver.c ../putty.h ../ssh.h \ - ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ - ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h + ../sshserver.h ../sftp.h ../tree234.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxshare.obj: ../unix/uxshare.c ../tree234.h ../putty.h \ @@ -1813,6 +2001,13 @@ $(BUILDDIR)uxshare.obj: ../unix/uxshare.c ../tree234.h ../putty.h \ $(BUILDDIR)uxsignal.obj: ../unix/uxsignal.c ../defs.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)uxsocks.obj: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)uxstore.obj: ../unix/uxstore.c ../putty.h ../storage.h \ ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ @@ -1826,14 +2021,18 @@ $(BUILDDIR)uxucs.obj: ../unix/uxucs.c ../putty.h ../charset/charset.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)uxutils.obj: ../unix/uxutils.c ../putty.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +$(BUILDDIR)uxutils.obj: ../unix/uxutils.c ../putty.h ../ssh.h \ + ../unix/uxutils.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)version.obj: ../version.c ../empty.h ../version.h +$(BUILDDIR)version.obj: ../version.c ../putty.h ../ssh.h ../empty.h \ + ../version.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wcwidth.obj: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h \ @@ -1848,11 +2047,11 @@ $(BUILDDIR)wildcard.obj: ../wildcard.c ../putty.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< -$(BUILDDIR)wincapi.obj: ../windows/wincapi.c ../putty.h ../windows/wincapi.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h +$(BUILDDIR)wincapi.obj: ../windows/wincapi.c ../putty.h ../ssh.h \ + ../windows/wincapi.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wincfg.obj: ../windows/wincfg.c ../putty.h ../dialog.h \ @@ -1862,11 +2061,18 @@ $(BUILDDIR)wincfg.obj: ../windows/wincfg.c ../putty.h ../dialog.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)wincliloop.obj: ../windows/wincliloop.c ../putty.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)wincons.obj: ../windows/wincons.c ../putty.h ../storage.h \ - ../ssh.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ - ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ - ../windows/winhelp.h ../charset/charset.h + ../ssh.h ../console.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winctrls.obj: ../windows/winctrls.c ../putty.h ../misc.h \ @@ -1884,19 +2090,19 @@ $(BUILDDIR)windefs.obj: ../windows/windefs.c ../putty.h ../defs.h \ $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)windlg.obj: ../windows/windlg.c ../putty.h ../ssh.h \ - ../windows/win_res.h ../storage.h ../dialog.h ../licence.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../windows/win_res.h ../windows/winseat.h ../storage.h \ + ../dialog.h ../licence.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)window.obj: ../windows/window.c ../putty.h ../terminal.h \ ../storage.h ../windows/win_res.h ../windows/winsecur.h \ - ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ - ../charset/charset.h + ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wingss.obj: ../windows/wingss.c ../putty.h ../pgssapi.h \ @@ -1948,9 +2154,10 @@ $(BUILDDIR)winmiscs.obj: ../windows/winmiscs.c ../putty.h ../defs.h \ $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnet.obj: ../windows/winnet.c ../putty.h ../network.h \ - ../tree234.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../tree234.h ../ssh.h ../defs.h ../puttyps.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnohlp.obj: ../windows/winnohlp.c ../putty.h ../defs.h \ @@ -1987,26 +2194,27 @@ $(BUILDDIR)winnps.obj: ../windows/winnps.c ../tree234.h ../putty.h \ $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winpgen.obj: ../windows/winpgen.c ../putty.h ../ssh.h \ - ../licence.h ../windows/winsecur.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../sshkeygen.h ../licence.h ../windows/winsecur.h \ + ../windows/puttygen-rc.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winpgnt.obj: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h \ - ../tree234.h ../windows/winsecur.h ../pageant.h ../licence.h \ - ../defs.h ../puttyps.h ../network.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../tree234.h ../windows/winsecur.h ../windows/wincapi.h \ + ../pageant.h ../licence.h ../windows/pageant-rc.h ../defs.h \ + ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ + ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winpgntc.obj: ../windows/winpgntc.c ../putty.h ../pageant.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winplink.obj: ../windows/winplink.c ../putty.h ../storage.h \ @@ -2037,6 +2245,20 @@ $(BUILDDIR)winsecur.obj: ../windows/winsecur.c ../putty.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)winselcli.obj: ../windows/winselcli.c ../putty.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + +$(BUILDDIR)winselgui.obj: ../windows/winselgui.c ../putty.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)winser.obj: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ @@ -2058,6 +2280,13 @@ $(BUILDDIR)winshare.obj: ../windows/winshare.c ../tree234.h ../putty.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< +$(BUILDDIR)winsocks.obj: ../windows/winsocks.c ../putty.h ../ssh.h \ + ../psocks.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< + $(BUILDDIR)winstore.obj: ../windows/winstore.c ../putty.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -2154,7 +2383,7 @@ $(BUILDDIR)puttytel.rcpp: ../windows/puttytel.rc ../windows/winhelp.rc2 \ $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< cleantestprogs: - -rm -f $(BUILDDIR)testcrypt.exe + -rm -f $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe clean: rm -f $(BUILDDIR)*.obj $(BUILDDIR)*.exe $(BUILDDIR)*.rcpp $(BUILDDIR)*.res \ diff --git a/windows/Makefile.lcc b/windows/Makefile.lcc index 83c898a..0d24da2 100644 --- a/windows/Makefile.lcc +++ b/windows/Makefile.lcc @@ -67,11 +67,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=-DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # @@ -105,285 +100,339 @@ RCFLAGS = -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ # Get include directory for resource compiler -all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ - puttytel.exe testcrypt.exe +all: pageant.exe plink.exe pscp.exe psftp.exe psocks.exe putty.exe \ + puttygen.exe puttytel.exe testcrypt.exe -pageant.exe: aqsync.obj conf.obj ecc.obj marshal.obj memory.obj misc.obj \ - mpint.obj pageant.obj pageant.res sshaes.obj sshauxcrypt.obj \ +pageant.exe: aqsync.obj be_misc.obj callback.obj conf.obj ecc.obj \ + errsock.obj marshal.obj memory.obj misc.obj mpint.obj \ + pageant.obj pageant.res sshaes.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshdes.obj sshdss.obj \ + sshecc.obj sshhmac.obj sshmd5.obj sshpubk.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + stripctrl.obj tree234.obj utils.obj version.obj wcwidth.obj \ + wincapi.obj winhandl.obj winhelp.obj winhsock.obj \ + winmisc.obj winmiscs.obj winnet.obj winnpc.obj winnps.obj \ + winpgnt.obj winpgntc.obj winsecur.obj winselgui.obj \ + winutils.obj + lcclnk -subsystem windows -o pageant.exe aqsync.obj be_misc.obj \ + callback.obj conf.obj ecc.obj errsock.obj marshal.obj \ + memory.obj misc.obj mpint.obj pageant.obj pageant.res \ + sshaes.obj sshargon2.obj sshauxcrypt.obj sshblake2.obj \ sshdes.obj sshdss.obj sshecc.obj sshhmac.obj sshmd5.obj \ sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ - stripctrl.obj tree234.obj utils.obj version.obj wcwidth.obj \ - winhelp.obj winmisc.obj winmiscs.obj winpgnt.obj \ - winpgntc.obj winsecur.obj winutils.obj - lcclnk -subsystem windows -o pageant.exe aqsync.obj conf.obj ecc.obj \ - marshal.obj memory.obj misc.obj mpint.obj pageant.obj \ - pageant.res sshaes.obj sshauxcrypt.obj sshdes.obj sshdss.obj \ - sshecc.obj sshhmac.obj sshmd5.obj sshpubk.obj sshrsa.obj \ - sshsh256.obj sshsh512.obj sshsha.obj stripctrl.obj \ - tree234.obj utils.obj version.obj wcwidth.obj winhelp.obj \ - winmisc.obj winmiscs.obj winpgnt.obj winpgntc.obj \ - winsecur.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \ - winspool.lib winmm.lib imm32.lib + sshsha3.obj stripctrl.obj tree234.obj utils.obj version.obj \ + wcwidth.obj wincapi.obj winhandl.obj winhelp.obj \ + winhsock.obj winmisc.obj winmiscs.obj winnet.obj winnpc.obj \ + winnps.obj winpgnt.obj winpgntc.obj winsecur.obj \ + winselgui.obj winutils.obj shell32.lib wsock32.lib \ + ws2_32.lib winspool.lib winmm.lib imm32.lib plink.exe: agentf.obj aqsync.obj be_all_s.obj be_misc.obj callback.obj \ - cmdline.obj conf.obj cproxy.obj ecc.obj errsock.obj \ - ldisc.obj logging.obj mainchan.obj marshal.obj memory.obj \ - misc.obj miscucs.obj mpint.obj noterm.obj nullplug.obj \ - pgssapi.obj pinger.obj plink.res portfwd.obj proxy.obj \ - raw.obj rlogin.obj sessprep.obj settings.obj ssh.obj \ - ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ - ssh1connection-client.obj ssh1login.obj ssh2bpp.obj \ - ssh2bpp-bare.obj ssh2censor.obj ssh2connection.obj \ - ssh2connection-client.obj ssh2kex-client.obj ssh2transhk.obj \ - ssh2transport.obj ssh2userauth.obj sshaes.obj ssharcf.obj \ - sshauxcrypt.obj sshblowf.obj sshccp.obj sshcommon.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj \ - sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ - sshsh512.obj sshsha.obj sshshare.obj sshverstring.obj \ - sshzlib.obj stripctrl.obj telnet.obj timing.obj tree234.obj \ + clicons.obj cmdline.obj conf.obj console.obj cproxy.obj \ + ecc.obj errsock.obj ldisc.obj logging.obj mainchan.obj \ + marshal.obj memory.obj misc.obj miscucs.obj mpint.obj \ + noterm.obj nullplug.obj pgssapi.obj pinger.obj plink.res \ + portfwd.obj proxy.obj raw.obj rlogin.obj sessprep.obj \ + settings.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + ssh2connection.obj ssh2connection-client.obj \ + ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ + stripctrl.obj supdup.obj telnet.obj timing.obj tree234.obj \ utils.obj version.obj wcwidth.obj wildcard.obj wincapi.obj \ - wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ - winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ - winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ - winplink.obj winproxy.obj winsecur.obj winser.obj \ - winshare.obj winstore.obj wintime.obj winucs.obj winx11.obj \ - x11fwd.obj + wincliloop.obj wincons.obj windefs.obj wingss.obj \ + winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ + winnet.obj winnohlp.obj winnoise.obj winnojmp.obj winnpc.obj \ + winnps.obj winpgntc.obj winplink.obj winproxy.obj \ + winsecur.obj winselcli.obj winser.obj winshare.obj \ + winstore.obj wintime.obj winucs.obj winx11.obj x11fwd.obj lcclnk -o plink.exe agentf.obj aqsync.obj be_all_s.obj be_misc.obj \ - callback.obj cmdline.obj conf.obj cproxy.obj ecc.obj \ - errsock.obj ldisc.obj logging.obj mainchan.obj marshal.obj \ - memory.obj misc.obj miscucs.obj mpint.obj noterm.obj \ - nullplug.obj pgssapi.obj pinger.obj plink.res portfwd.obj \ - proxy.obj raw.obj rlogin.obj sessprep.obj settings.obj \ - ssh.obj ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ - ssh1connection-client.obj ssh1login.obj ssh2bpp.obj \ - ssh2bpp-bare.obj ssh2censor.obj ssh2connection.obj \ - ssh2connection-client.obj ssh2kex-client.obj ssh2transhk.obj \ - ssh2transport.obj ssh2userauth.obj sshaes.obj ssharcf.obj \ - sshauxcrypt.obj sshblowf.obj sshccp.obj sshcommon.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj \ - sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ - sshsh512.obj sshsha.obj sshshare.obj sshverstring.obj \ - sshzlib.obj stripctrl.obj telnet.obj timing.obj tree234.obj \ + callback.obj clicons.obj cmdline.obj conf.obj console.obj \ + cproxy.obj ecc.obj errsock.obj ldisc.obj logging.obj \ + mainchan.obj marshal.obj memory.obj misc.obj miscucs.obj \ + mpint.obj noterm.obj nullplug.obj pgssapi.obj pinger.obj \ + plink.res portfwd.obj proxy.obj raw.obj rlogin.obj \ + sessprep.obj settings.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + ssh2connection.obj ssh2connection-client.obj \ + ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ + stripctrl.obj supdup.obj telnet.obj timing.obj tree234.obj \ utils.obj version.obj wcwidth.obj wildcard.obj wincapi.obj \ - wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ - winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ - winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ - winplink.obj winproxy.obj winsecur.obj winser.obj \ - winshare.obj winstore.obj wintime.obj winucs.obj winx11.obj \ - x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ - winmm.lib imm32.lib + wincliloop.obj wincons.obj windefs.obj wingss.obj \ + winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ + winnet.obj winnohlp.obj winnoise.obj winnojmp.obj winnpc.obj \ + winnps.obj winpgntc.obj winplink.obj winproxy.obj \ + winsecur.obj winselcli.obj winser.obj winshare.obj \ + winstore.obj wintime.obj winucs.obj winx11.obj x11fwd.obj \ + shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ + imm32.lib pscp.exe: agentf.obj aqsync.obj be_misc.obj be_ssh.obj callback.obj \ - cmdline.obj conf.obj cproxy.obj ecc.obj errsock.obj \ - logging.obj mainchan.obj marshal.obj memory.obj misc.obj \ - miscucs.obj mpint.obj nullplug.obj pgssapi.obj pinger.obj \ - portfwd.obj proxy.obj pscp.obj pscp.res psftpcommon.obj \ - settings.obj sftp.obj sftpcommon.obj ssh.obj ssh1bpp.obj \ - ssh1censor.obj ssh1connection.obj ssh1connection-client.obj \ - ssh1login.obj ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + clicons.obj cmdline.obj conf.obj console.obj cproxy.obj \ + ecc.obj errsock.obj logging.obj mainchan.obj marshal.obj \ + memory.obj misc.obj miscucs.obj mpint.obj nullplug.obj \ + pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ + pscp.res psftpcommon.obj settings.obj sftp.obj \ + sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ - ssh2userauth.obj sshaes.obj ssharcf.obj sshauxcrypt.obj \ - sshblowf.obj sshccp.obj sshcommon.obj sshcrc.obj \ - sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshecc.obj \ - sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj sshprng.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshshare.obj sshverstring.obj sshzlib.obj \ + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ - wcwidth.obj wildcard.obj wincapi.obj wincons.obj windefs.obj \ - wingss.obj winhandl.obj winhsock.obj winmisc.obj \ - winmiscs.obj winnet.obj winnohlp.obj winnoise.obj \ - winnojmp.obj winnpc.obj winnps.obj winpgntc.obj winproxy.obj \ - winsecur.obj winsftp.obj winshare.obj winstore.obj \ - wintime.obj winucs.obj x11fwd.obj + wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ + wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ + winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ + winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ + winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ + winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj lcclnk -o pscp.exe agentf.obj aqsync.obj be_misc.obj be_ssh.obj \ - callback.obj cmdline.obj conf.obj cproxy.obj ecc.obj \ - errsock.obj logging.obj mainchan.obj marshal.obj memory.obj \ - misc.obj miscucs.obj mpint.obj nullplug.obj pgssapi.obj \ - pinger.obj portfwd.obj proxy.obj pscp.obj pscp.res \ - psftpcommon.obj settings.obj sftp.obj sftpcommon.obj ssh.obj \ - ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ - ssh1connection-client.obj ssh1login.obj ssh2bpp.obj \ - ssh2bpp-bare.obj ssh2censor.obj ssh2connection.obj \ - ssh2connection-client.obj ssh2kex-client.obj ssh2transhk.obj \ - ssh2transport.obj ssh2userauth.obj sshaes.obj ssharcf.obj \ - sshauxcrypt.obj sshblowf.obj sshccp.obj sshcommon.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj \ - sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ - sshsh512.obj sshsha.obj sshshare.obj sshverstring.obj \ - sshzlib.obj stripctrl.obj timing.obj tree234.obj utils.obj \ - version.obj wcwidth.obj wildcard.obj wincapi.obj wincons.obj \ - windefs.obj wingss.obj winhandl.obj winhsock.obj winmisc.obj \ - winmiscs.obj winnet.obj winnohlp.obj winnoise.obj \ - winnojmp.obj winnpc.obj winnps.obj winpgntc.obj winproxy.obj \ - winsecur.obj winsftp.obj winshare.obj winstore.obj \ - wintime.obj winucs.obj x11fwd.obj shell32.lib wsock32.lib \ - ws2_32.lib winspool.lib winmm.lib imm32.lib + callback.obj clicons.obj cmdline.obj conf.obj console.obj \ + cproxy.obj ecc.obj errsock.obj logging.obj mainchan.obj \ + marshal.obj memory.obj misc.obj miscucs.obj mpint.obj \ + nullplug.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + pscp.obj pscp.res psftpcommon.obj settings.obj sftp.obj \ + sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + ssh2connection.obj ssh2connection-client.obj \ + ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ + stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ + wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ + wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ + winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ + winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ + winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ + winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj \ + shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ + imm32.lib psftp.exe: agentf.obj aqsync.obj be_misc.obj be_ssh.obj callback.obj \ - cmdline.obj conf.obj cproxy.obj ecc.obj errsock.obj \ - logging.obj mainchan.obj marshal.obj memory.obj misc.obj \ - miscucs.obj mpint.obj nullplug.obj pgssapi.obj pinger.obj \ - portfwd.obj proxy.obj psftp.obj psftp.res psftpcommon.obj \ - settings.obj sftp.obj sftpcommon.obj ssh.obj ssh1bpp.obj \ - ssh1censor.obj ssh1connection.obj ssh1connection-client.obj \ - ssh1login.obj ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + clicons.obj cmdline.obj conf.obj console.obj cproxy.obj \ + ecc.obj errsock.obj logging.obj mainchan.obj marshal.obj \ + memory.obj misc.obj miscucs.obj mpint.obj nullplug.obj \ + pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ + psftp.res psftpcommon.obj settings.obj sftp.obj \ + sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ - ssh2userauth.obj sshaes.obj ssharcf.obj sshauxcrypt.obj \ - sshblowf.obj sshccp.obj sshcommon.obj sshcrc.obj \ - sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshecc.obj \ - sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj sshprng.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshshare.obj sshverstring.obj sshzlib.obj \ + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ - wcwidth.obj wildcard.obj wincapi.obj wincons.obj windefs.obj \ - wingss.obj winhandl.obj winhsock.obj winmisc.obj \ - winmiscs.obj winnet.obj winnohlp.obj winnoise.obj \ - winnojmp.obj winnpc.obj winnps.obj winpgntc.obj winproxy.obj \ - winsecur.obj winsftp.obj winshare.obj winstore.obj \ - wintime.obj winucs.obj x11fwd.obj + wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ + wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ + winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ + winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ + winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ + winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj lcclnk -o psftp.exe agentf.obj aqsync.obj be_misc.obj be_ssh.obj \ - callback.obj cmdline.obj conf.obj cproxy.obj ecc.obj \ - errsock.obj logging.obj mainchan.obj marshal.obj memory.obj \ - misc.obj miscucs.obj mpint.obj nullplug.obj pgssapi.obj \ - pinger.obj portfwd.obj proxy.obj psftp.obj psftp.res \ - psftpcommon.obj settings.obj sftp.obj sftpcommon.obj ssh.obj \ - ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ - ssh1connection-client.obj ssh1login.obj ssh2bpp.obj \ - ssh2bpp-bare.obj ssh2censor.obj ssh2connection.obj \ - ssh2connection-client.obj ssh2kex-client.obj ssh2transhk.obj \ - ssh2transport.obj ssh2userauth.obj sshaes.obj ssharcf.obj \ - sshauxcrypt.obj sshblowf.obj sshccp.obj sshcommon.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj \ - sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ - sshsh512.obj sshsha.obj sshshare.obj sshverstring.obj \ - sshzlib.obj stripctrl.obj timing.obj tree234.obj utils.obj \ - version.obj wcwidth.obj wildcard.obj wincapi.obj wincons.obj \ - windefs.obj wingss.obj winhandl.obj winhsock.obj winmisc.obj \ - winmiscs.obj winnet.obj winnohlp.obj winnoise.obj \ - winnojmp.obj winnpc.obj winnps.obj winpgntc.obj winproxy.obj \ - winsecur.obj winsftp.obj winshare.obj winstore.obj \ - wintime.obj winucs.obj x11fwd.obj shell32.lib wsock32.lib \ - ws2_32.lib winspool.lib winmm.lib imm32.lib + callback.obj clicons.obj cmdline.obj conf.obj console.obj \ + cproxy.obj ecc.obj errsock.obj logging.obj mainchan.obj \ + marshal.obj memory.obj misc.obj miscucs.obj mpint.obj \ + nullplug.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ + psftp.obj psftp.res psftpcommon.obj settings.obj sftp.obj \ + sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + ssh2connection.obj ssh2connection-client.obj \ + ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ + stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ + wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ + wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ + winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ + winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ + winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ + winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj \ + shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ + imm32.lib + +psocks.exe: be_misc.obj callback.obj conf.obj console.obj errsock.obj \ + logging.obj marshal.obj memory.obj misc.obj nocproxy.obj \ + norand.obj portfwd.obj proxy.obj psocks.obj sshutils.obj \ + stripctrl.obj time.obj timing.obj tree234.obj utils.obj \ + version.obj wcwidth.obj wincliloop.obj wincons.obj \ + winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ + winnet.obj winnohlp.obj winproxy.obj winselcli.obj \ + winsocks.obj + lcclnk -o psocks.exe be_misc.obj callback.obj conf.obj console.obj \ + errsock.obj logging.obj marshal.obj memory.obj misc.obj \ + nocproxy.obj norand.obj portfwd.obj proxy.obj psocks.obj \ + sshutils.obj stripctrl.obj time.obj timing.obj tree234.obj \ + utils.obj version.obj wcwidth.obj wincliloop.obj wincons.obj \ + winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ + winnet.obj winnohlp.obj winproxy.obj winselcli.obj \ + winsocks.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ + winmm.lib imm32.lib putty.exe: agentf.obj aqsync.obj be_all_s.obj be_misc.obj callback.obj \ cmdline.obj conf.obj config.obj cproxy.obj dialog.obj \ ecc.obj errsock.obj ldisc.obj logging.obj mainchan.obj \ marshal.obj memory.obj minibidi.obj misc.obj miscucs.obj \ mpint.obj nullplug.obj pgssapi.obj pinger.obj portfwd.obj \ - proxy.obj putty.res raw.obj rlogin.obj sercfg.obj \ - sessprep.obj settings.obj sizetip.obj ssh.obj ssh1bpp.obj \ - ssh1censor.obj ssh1connection.obj ssh1connection-client.obj \ - ssh1login.obj ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ + proxy.obj putty.res raw.obj rlogin.obj sessprep.obj \ + settings.obj sizetip.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ + ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ + ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ - ssh2userauth.obj sshaes.obj ssharcf.obj sshauxcrypt.obj \ - sshblowf.obj sshccp.obj sshcommon.obj sshcrc.obj \ - sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshecc.obj \ - sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj sshprng.obj \ - sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj sshsh512.obj \ - sshsha.obj sshshare.obj sshverstring.obj sshzlib.obj \ - stripctrl.obj telnet.obj terminal.obj timing.obj tree234.obj \ - utils.obj version.obj wcwidth.obj wildcard.obj wincapi.obj \ - wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ - wingss.obj winhandl.obj winhelp.obj winhsock.obj winjump.obj \ - winmisc.obj winmiscs.obj winnet.obj winnoise.obj winnpc.obj \ - winnps.obj winpgntc.obj winprint.obj winproxy.obj \ - winsecur.obj winser.obj winshare.obj winstore.obj \ - wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj + ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ + sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ + sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ + sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ + stripctrl.obj supdup.obj telnet.obj terminal.obj timing.obj \ + tree234.obj utils.obj version.obj wcwidth.obj wildcard.obj \ + wincapi.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ + window.obj wingss.obj winhandl.obj winhelp.obj winhsock.obj \ + winjump.obj winmisc.obj winmiscs.obj winnet.obj winnoise.obj \ + winnpc.obj winnps.obj winpgntc.obj winprint.obj winproxy.obj \ + winsecur.obj winselgui.obj winser.obj winshare.obj \ + winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \ + x11fwd.obj lcclnk -subsystem windows -o putty.exe agentf.obj aqsync.obj be_all_s.obj \ be_misc.obj callback.obj cmdline.obj conf.obj config.obj \ cproxy.obj dialog.obj ecc.obj errsock.obj ldisc.obj \ logging.obj mainchan.obj marshal.obj memory.obj minibidi.obj \ misc.obj miscucs.obj mpint.obj nullplug.obj pgssapi.obj \ pinger.obj portfwd.obj proxy.obj putty.res raw.obj \ - rlogin.obj sercfg.obj sessprep.obj settings.obj sizetip.obj \ - ssh.obj ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ + rlogin.obj sessprep.obj settings.obj sizetip.obj ssh.obj \ + ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ ssh1connection-client.obj ssh1login.obj ssh2bpp.obj \ ssh2bpp-bare.obj ssh2censor.obj ssh2connection.obj \ ssh2connection-client.obj ssh2kex-client.obj ssh2transhk.obj \ ssh2transport.obj ssh2userauth.obj sshaes.obj ssharcf.obj \ - sshauxcrypt.obj sshblowf.obj sshccp.obj sshcommon.obj \ - sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj sshmd5.obj \ - sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshsh256.obj \ - sshsh512.obj sshsha.obj sshshare.obj sshverstring.obj \ - sshzlib.obj stripctrl.obj telnet.obj terminal.obj timing.obj \ + sshargon2.obj sshauxcrypt.obj sshblake2.obj sshblowf.obj \ + sshccp.obj sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj \ + sshdh.obj sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj \ + sshmac.obj sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj \ + sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ + sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ + stripctrl.obj supdup.obj telnet.obj terminal.obj timing.obj \ tree234.obj utils.obj version.obj wcwidth.obj wildcard.obj \ wincapi.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ window.obj wingss.obj winhandl.obj winhelp.obj winhsock.obj \ winjump.obj winmisc.obj winmiscs.obj winnet.obj winnoise.obj \ winnpc.obj winnps.obj winpgntc.obj winprint.obj winproxy.obj \ - winsecur.obj winser.obj winshare.obj winstore.obj \ - wintime.obj winucs.obj winutils.obj winx11.obj x11fwd.obj \ - shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ - imm32.lib + winsecur.obj winselgui.obj winser.obj winshare.obj \ + winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \ + x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ + winmm.lib imm32.lib -puttygen.exe: conf.obj ecc.obj import.obj marshal.obj memory.obj misc.obj \ - mpint.obj notiming.obj puttygen.res sshaes.obj \ - sshauxcrypt.obj sshbcrypt.obj sshblowf.obj sshdes.obj \ - sshdss.obj sshdssg.obj sshecc.obj sshecdsag.obj sshhmac.obj \ - sshmd5.obj sshprime.obj sshprng.obj sshpubk.obj sshrand.obj \ - sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj sshsha.obj \ +puttygen.exe: conf.obj ecc.obj import.obj marshal.obj memory.obj \ + millerrabin.obj misc.obj mpint.obj mpunsafe.obj notiming.obj \ + pockle.obj primecandidate.obj puttygen.res smallprimes.obj \ + sshaes.obj sshargon2.obj sshauxcrypt.obj sshbcrypt.obj \ + sshblake2.obj sshblowf.obj sshdes.obj sshdss.obj sshdssg.obj \ + sshecc.obj sshecdsag.obj sshhmac.obj sshmd5.obj sshprime.obj \ + sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ stripctrl.obj tree234.obj utils.obj version.obj wcwidth.obj \ winctrls.obj winhelp.obj winmisc.obj winmiscs.obj \ winnoise.obj winnojmp.obj winpgen.obj winsecur.obj \ winstore.obj wintime.obj winutils.obj lcclnk -subsystem windows -o puttygen.exe conf.obj ecc.obj import.obj \ - marshal.obj memory.obj misc.obj mpint.obj notiming.obj \ - puttygen.res sshaes.obj sshauxcrypt.obj sshbcrypt.obj \ - sshblowf.obj sshdes.obj sshdss.obj sshdssg.obj sshecc.obj \ - sshecdsag.obj sshhmac.obj sshmd5.obj sshprime.obj \ - sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ - sshsh256.obj sshsh512.obj sshsha.obj stripctrl.obj \ - tree234.obj utils.obj version.obj wcwidth.obj winctrls.obj \ - winhelp.obj winmisc.obj winmiscs.obj winnoise.obj \ - winnojmp.obj winpgen.obj winsecur.obj winstore.obj \ - wintime.obj winutils.obj shell32.lib wsock32.lib ws2_32.lib \ - winspool.lib winmm.lib imm32.lib + marshal.obj memory.obj millerrabin.obj misc.obj mpint.obj \ + mpunsafe.obj notiming.obj pockle.obj primecandidate.obj \ + puttygen.res smallprimes.obj sshaes.obj sshargon2.obj \ + sshauxcrypt.obj sshbcrypt.obj sshblake2.obj sshblowf.obj \ + sshdes.obj sshdss.obj sshdssg.obj sshecc.obj sshecdsag.obj \ + sshhmac.obj sshmd5.obj sshprime.obj sshprng.obj sshpubk.obj \ + sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj \ + sshsha.obj sshsha3.obj stripctrl.obj tree234.obj utils.obj \ + version.obj wcwidth.obj winctrls.obj winhelp.obj winmisc.obj \ + winmiscs.obj winnoise.obj winnojmp.obj winpgen.obj \ + winsecur.obj winstore.obj wintime.obj winutils.obj \ + shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ + imm32.lib puttytel.exe: be_misc.obj be_nos_s.obj callback.obj cmdline.obj conf.obj \ config.obj dialog.obj errsock.obj ldisc.obj logging.obj \ marshal.obj memory.obj minibidi.obj misc.obj miscucs.obj \ - nocproxy.obj nogss.obj pinger.obj proxy.obj puttytel.res \ - raw.obj rlogin.obj sercfg.obj sessprep.obj settings.obj \ - sizetip.obj stripctrl.obj telnet.obj terminal.obj timing.obj \ - tree234.obj utils.obj version.obj wcwidth.obj wincfg.obj \ - winctrls.obj windefs.obj windlg.obj window.obj winhandl.obj \ - winhelp.obj winhsock.obj winjump.obj winmisc.obj \ - winmiscs.obj winnet.obj winprint.obj winproxy.obj \ - winsecur.obj winser.obj winstore.obj wintime.obj winucs.obj \ - winutils.obj + nocproxy.obj nogss.obj norand.obj pinger.obj proxy.obj \ + puttytel.res raw.obj rlogin.obj sessprep.obj settings.obj \ + sizetip.obj stripctrl.obj supdup.obj telnet.obj terminal.obj \ + timing.obj tree234.obj utils.obj version.obj wcwidth.obj \ + wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ + winhandl.obj winhelp.obj winhsock.obj winjump.obj \ + winmisc.obj winmiscs.obj winnet.obj winprint.obj \ + winproxy.obj winsecur.obj winselgui.obj winser.obj \ + winstore.obj wintime.obj winucs.obj winutils.obj lcclnk -subsystem windows -o puttytel.exe be_misc.obj be_nos_s.obj \ callback.obj cmdline.obj conf.obj config.obj dialog.obj \ errsock.obj ldisc.obj logging.obj marshal.obj memory.obj \ minibidi.obj misc.obj miscucs.obj nocproxy.obj nogss.obj \ - pinger.obj proxy.obj puttytel.res raw.obj rlogin.obj \ - sercfg.obj sessprep.obj settings.obj sizetip.obj \ - stripctrl.obj telnet.obj terminal.obj timing.obj tree234.obj \ - utils.obj version.obj wcwidth.obj wincfg.obj winctrls.obj \ - windefs.obj windlg.obj window.obj winhandl.obj winhelp.obj \ - winhsock.obj winjump.obj winmisc.obj winmiscs.obj winnet.obj \ - winprint.obj winproxy.obj winsecur.obj winser.obj \ - winstore.obj wintime.obj winucs.obj winutils.obj shell32.lib \ - wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib + norand.obj pinger.obj proxy.obj puttytel.res raw.obj \ + rlogin.obj sessprep.obj settings.obj sizetip.obj \ + stripctrl.obj supdup.obj telnet.obj terminal.obj timing.obj \ + tree234.obj utils.obj version.obj wcwidth.obj wincfg.obj \ + winctrls.obj windefs.obj windlg.obj window.obj winhandl.obj \ + winhelp.obj winhsock.obj winjump.obj winmisc.obj \ + winmiscs.obj winnet.obj winprint.obj winproxy.obj \ + winsecur.obj winselgui.obj winser.obj winstore.obj \ + wintime.obj winucs.obj winutils.obj shell32.lib wsock32.lib \ + ws2_32.lib winspool.lib winmm.lib imm32.lib -testcrypt.exe: ecc.obj marshal.obj memory.obj mpint.obj sshaes.obj \ - ssharcf.obj sshauxcrypt.obj sshblowf.obj sshccp.obj \ +testcrypt.exe: ecc.obj marshal.obj memory.obj millerrabin.obj mpint.obj \ + mpunsafe.obj pockle.obj primecandidate.obj smallprimes.obj \ + sshaes.obj ssharcf.obj sshargon2.obj sshauxcrypt.obj \ + sshblake2.obj sshblowf.obj sshccp.obj sshcrc.obj \ + sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshdssg.obj \ + sshecc.obj sshecdsag.obj sshhmac.obj sshmd5.obj sshprime.obj \ + sshprng.obj sshpubk.obj sshrsa.obj sshrsag.obj sshsh256.obj \ + sshsh512.obj sshsha.obj sshsha3.obj testcrypt.obj \ + tree234.obj utils.obj winmiscs.obj + lcclnk -o testcrypt.exe ecc.obj marshal.obj memory.obj millerrabin.obj \ + mpint.obj mpunsafe.obj pockle.obj primecandidate.obj \ + smallprimes.obj sshaes.obj ssharcf.obj sshargon2.obj \ + sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ - sshecc.obj sshhmac.obj sshmd5.obj sshprime.obj sshprng.obj \ - sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ - testcrypt.obj tree234.obj utils.obj winmiscs.obj - lcclnk -o testcrypt.exe ecc.obj marshal.obj memory.obj mpint.obj \ - sshaes.obj ssharcf.obj sshauxcrypt.obj sshblowf.obj \ - sshccp.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ - sshdss.obj sshecc.obj sshhmac.obj sshmd5.obj sshprime.obj \ - sshprng.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ + sshdssg.obj sshecc.obj sshecdsag.obj sshhmac.obj sshmd5.obj \ + sshprime.obj sshprng.obj sshpubk.obj sshrsa.obj sshrsag.obj \ + sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ testcrypt.obj tree234.obj utils.obj winmiscs.obj shell32.lib \ wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib @@ -428,15 +477,20 @@ callback.obj: ..\callback.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\callback.c -cgtest.obj: ..\cgtest.c ..\cmdgen.c ..\putty.h ..\ssh.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ - ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ - ..\charset\charset.h +cgtest.obj: ..\cgtest.c ..\cmdgen.c ..\putty.h ..\ssh.h ..\sshkeygen.h \ + ..\mpint.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ + ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cgtest.c -cmdgen.obj: ..\cmdgen.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ - ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ - ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ +clicons.obj: ..\clicons.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ + ..\unix\unix.h ..\puttymem.h ..\tree234.h \ + ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\clicons.c +cmdgen.obj: ..\cmdgen.c ..\putty.h ..\ssh.h ..\sshkeygen.h ..\mpint.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdgen.c @@ -456,6 +510,11 @@ config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\defs.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\config.c +console.obj: ..\console.c ..\putty.h ..\misc.h ..\console.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ + ..\puttymem.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\console.c cproxy.obj: ..\cproxy.c ..\putty.h ..\ssh.h ..\network.h ..\proxy.h \ ..\marshal.h ..\defs.h ..\puttyps.h ..\misc.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ @@ -478,10 +537,11 @@ errsock.obj: ..\errsock.c ..\tree234.h ..\putty.h ..\network.h ..\defs.h \ lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\errsock.c fromucs.obj: ..\charset\fromucs.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\fromucs.c -fuzzterm.obj: ..\fuzzterm.c ..\putty.h ..\terminal.h ..\defs.h ..\puttyps.h \ - ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ - ..\tree234.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h +fuzzterm.obj: ..\fuzzterm.c ..\putty.h ..\dialog.h ..\terminal.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\tree234.h ..\windows\winstuff.h \ + ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\fuzzterm.c gtkapp.obj: ..\unix\gtkapp.c ..\putty.h ..\unix\gtkmisc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ @@ -513,10 +573,10 @@ gtkcomm.obj: ..\unix\gtkcomm.c ..\putty.h ..\terminal.h ..\unix\gtkcompat.h \ gtkdlg.obj: ..\unix\gtkdlg.c ..\putty.h ..\unix\gtkcompat.h \ ..\unix\gtkcols.h ..\unix\gtkfont.h ..\unix\gtkmisc.h \ ..\unix\x11misc.h ..\storage.h ..\dialog.h ..\tree234.h \ - ..\licence.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ - ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ - ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ - ..\charset\charset.h + ..\licence.h ..\ssh.h ..\defs.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkdlg.c gtkfont.obj: ..\unix\gtkfont.c ..\putty.h ..\unix\gtkfont.h \ ..\unix\gtkcompat.h ..\unix\gtkmisc.h ..\tree234.h \ @@ -577,6 +637,10 @@ marshal.obj: ..\marshal.c ..\marshal.h ..\misc.h ..\defs.h ..\puttymem.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\marshal.c memory.obj: ..\memory.c ..\defs.h ..\puttymem.h ..\misc.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\memory.c +millerrabin.obj: ..\millerrabin.c ..\ssh.h ..\sshkeygen.h ..\mpint.h \ + ..\mpunsafe.h ..\puttymem.h ..\tree234.h ..\network.h \ + ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\millerrabin.c mimeenc.obj: ..\charset\mimeenc.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\mimeenc.c minibidi.obj: ..\minibidi.c ..\putty.h ..\misc.h ..\defs.h ..\puttyps.h \ @@ -597,6 +661,9 @@ miscucs.obj: ..\miscucs.c ..\putty.h ..\misc.h ..\defs.h ..\puttyps.h \ mpint.obj: ..\mpint.c ..\defs.h ..\misc.h ..\puttymem.h ..\mpint.h \ ..\mpint_i.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\mpint.c +mpunsafe.obj: ..\mpunsafe.c ..\defs.h ..\misc.h ..\puttymem.h ..\mpint.h \ + ..\mpint_i.h ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\mpunsafe.c nocmdline.obj: ..\nocmdline.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -612,6 +679,11 @@ nogss.obj: ..\nogss.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nogss.c +norand.obj: ..\norand.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ + ..\unix\unix.h ..\puttymem.h ..\tree234.h \ + ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\norand.c noterm.obj: ..\noterm.c ..\putty.h ..\terminal.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\tree234.h ..\windows\winstuff.h ..\unix\unix.h \ @@ -636,10 +708,10 @@ pageant.obj: ..\pageant.c ..\putty.h ..\mpint.h ..\ssh.h ..\sshcr.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pageant.c pageant.res: ..\windows\pageant.rc ..\windows\rcstuff.h \ - ..\windows\winhelp.rc2 ..\windows\pageant.ico \ - ..\windows\pageants.ico ..\windows\version.rc2 \ - ..\windows\pageant.mft ..\windows\win_res.h ..\version.h \ - ..\licence.h + ..\windows\pageant-rc.h ..\windows\winhelp.rc2 \ + ..\windows\pageant.ico ..\windows\pageants.ico \ + ..\windows\version.rc2 ..\windows\pageant.mft \ + ..\windows\win_res.h ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\pageant.rc pgssapi.obj: ..\pgssapi.c ..\putty.h ..\pgssapi.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ @@ -654,12 +726,20 @@ pinger.obj: ..\pinger.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ plink.res: ..\windows\plink.rc ..\windows\rcstuff.h ..\windows\putty.ico \ ..\windows\version.rc2 ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\plink.rc +pockle.obj: ..\pockle.c ..\ssh.h ..\sshkeygen.h ..\mpint.h ..\mpunsafe.h \ + ..\tree234.h ..\puttymem.h ..\network.h ..\misc.h \ + ..\sshttymodes.h ..\defs.h ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pockle.c portfwd.obj: ..\portfwd.c ..\putty.h ..\ssh.h ..\sshchan.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\portfwd.c +primecandidate.obj: ..\primecandidate.c ..\ssh.h ..\mpint.h ..\mpunsafe.h \ + ..\sshkeygen.h ..\puttymem.h ..\tree234.h ..\network.h \ + ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\primecandidate.c procnet.obj: ..\unix\procnet.c ..\misc.h ..\defs.h ..\puttymem.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\procnet.c @@ -692,6 +772,12 @@ psftpcommon.obj: ..\psftpcommon.c ..\putty.h ..\sftp.h ..\psftp.h ..\defs.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\psftpcommon.c +psocks.obj: ..\psocks.c ..\putty.h ..\misc.h ..\ssh.h ..\sshchan.h \ + ..\psocks.h ..\defs.h ..\puttyps.h ..\network.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\psocks.c putty.res: ..\windows\putty.rc ..\windows\rcstuff.h ..\windows\winhelp.rc2 \ ..\windows\win_res.rc2 ..\windows\putty.mft \ ..\windows\win_res.h ..\windows\putty.ico \ @@ -699,9 +785,10 @@ putty.res: ..\windows\putty.rc ..\windows\rcstuff.h ..\windows\winhelp.rc2 \ ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\putty.rc puttygen.res: ..\windows\puttygen.rc ..\windows\rcstuff.h \ - ..\windows\winhelp.rc2 ..\windows\puttygen.ico \ - ..\windows\version.rc2 ..\windows\puttygen.mft \ - ..\windows\win_res.h ..\version.h ..\licence.h + ..\windows\winhelp.rc2 ..\windows\puttygen-rc.h \ + ..\windows\puttygen.ico ..\windows\version.rc2 \ + ..\windows\puttygen.mft ..\windows\win_res.h ..\version.h \ + ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\puttygen.rc puttytel.res: ..\windows\puttytel.rc ..\windows\rcstuff.h \ ..\windows\winhelp.rc2 ..\windows\win_res.rc2 \ @@ -729,12 +816,6 @@ scpserver.obj: ..\scpserver.c ..\putty.h ..\ssh.h ..\sshcr.h ..\sshchan.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\scpserver.c -sercfg.obj: ..\sercfg.c ..\putty.h ..\dialog.h ..\storage.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ - ..\charset\charset.h - lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sercfg.c sesschan.obj: ..\sesschan.c ..\putty.h ..\ssh.h ..\sshchan.h ..\sshserver.h \ ..\sftp.h ..\sshsignals.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\puttymem.h \ @@ -772,6 +853,10 @@ sizetip.obj: ..\windows\sizetip.c ..\putty.h ..\defs.h ..\puttyps.h \ slookup.obj: ..\charset\slookup.c ..\charset\charset.h ..\charset\internal.h \ ..\charset\enum.c ..\charset\sbcsdat.c ..\charset\utf8.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\slookup.c +smallprimes.obj: ..\smallprimes.c ..\ssh.h ..\sshkeygen.h ..\puttymem.h \ + ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ + ..\defs.h ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\smallprimes.c ssh.obj: ..\ssh.c ..\putty.h ..\pageant.h ..\tree234.h ..\storage.h \ ..\marshal.h ..\ssh.h ..\sshcr.h ..\sshbpp.h ..\sshppl.h \ ..\sshchan.h ..\sshgssc.h ..\sshgss.h ..\defs.h ..\puttyps.h \ @@ -820,11 +905,11 @@ ssh1login.obj: ..\ssh1login.c ..\putty.h ..\ssh.h ..\mpint.h ..\sshbpp.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1login.c ssh1login-server.obj: ..\ssh1login-server.c ..\putty.h ..\mpint.h ..\ssh.h \ - ..\sshbpp.h ..\sshppl.h ..\sshcr.h ..\sshserver.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ - ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ - ..\charset\charset.h + ..\sshbpp.h ..\sshppl.h ..\sshcr.h ..\sshserver.h \ + ..\sshkeygen.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ + ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1login-server.c ssh2bpp.obj: ..\ssh2bpp.c ..\putty.h ..\ssh.h ..\sshbpp.h ..\sshcr.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ @@ -875,12 +960,13 @@ ssh2kex-client.obj: ..\ssh2kex-client.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2kex-client.c ssh2kex-server.obj: ..\ssh2kex-server.c ..\putty.h ..\ssh.h ..\sshbpp.h \ - ..\sshppl.h ..\sshcr.h ..\sshserver.h ..\storage.h \ - ..\ssh2transport.h ..\mpint.h ..\defs.h ..\puttyps.h \ - ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ - ..\puttymem.h ..\tree234.h ..\sshttymodes.h ..\sshgssc.h \ - ..\sshgss.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\pgssapi.h ..\windows\winhelp.h ..\charset\charset.h + ..\sshppl.h ..\sshcr.h ..\sshserver.h ..\sshkeygen.h \ + ..\storage.h ..\ssh2transport.h ..\mpint.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\sshgssc.h ..\sshgss.h ..\windows\winstuff.h \ + ..\unix\unix.h ..\pgssapi.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2kex-server.c ssh2transhk.obj: ..\ssh2transhk.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ @@ -918,6 +1004,12 @@ sshaes.obj: ..\sshaes.c ..\ssh.h ..\mpint_i.h ..\puttymem.h ..\tree234.h \ ssharcf.obj: ..\ssharcf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssharcf.c +sshargon2.obj: ..\sshargon2.c ..\putty.h ..\ssh.h ..\marshal.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\misc.h ..\sshsignals.h \ + ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshargon2.c sshauxcrypt.obj: ..\sshauxcrypt.c ..\ssh.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h @@ -926,6 +1018,10 @@ sshbcrypt.obj: ..\sshbcrypt.c ..\ssh.h ..\sshblowf.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshbcrypt.c +sshblake2.obj: ..\sshblake2.c ..\ssh.h ..\puttymem.h ..\tree234.h \ + ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ + ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshblake2.c sshblowf.obj: ..\sshblowf.c ..\ssh.h ..\sshblowf.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h @@ -959,17 +1055,17 @@ sshdss.obj: ..\sshdss.c ..\ssh.h ..\mpint.h ..\misc.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdss.c -sshdssg.obj: ..\sshdssg.c ..\misc.h ..\ssh.h ..\mpint.h ..\defs.h \ - ..\puttymem.h ..\marshal.h ..\tree234.h ..\network.h \ - ..\sshttymodes.h +sshdssg.obj: ..\sshdssg.c ..\misc.h ..\ssh.h ..\sshkeygen.h ..\mpint.h \ + ..\defs.h ..\puttymem.h ..\marshal.h ..\tree234.h \ + ..\network.h ..\sshttymodes.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdssg.c sshecc.obj: ..\sshecc.c ..\ssh.h ..\mpint.h ..\ecc.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshecc.c -sshecdsag.obj: ..\sshecdsag.c ..\ssh.h ..\mpint.h ..\puttymem.h ..\tree234.h \ - ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ - ..\marshal.h +sshecdsag.obj: ..\sshecdsag.c ..\ssh.h ..\sshkeygen.h ..\mpint.h \ + ..\puttymem.h ..\tree234.h ..\network.h ..\misc.h \ + ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshecdsag.c sshgssc.obj: ..\sshgssc.c ..\putty.h ..\sshgssc.h ..\misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ @@ -986,11 +1082,11 @@ sshmac.obj: ..\sshmac.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ sshmd5.obj: ..\sshmd5.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshmd5.c -sshprime.obj: ..\sshprime.c ..\ssh.h ..\mpint.h ..\puttymem.h ..\tree234.h \ - ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ - ..\marshal.h +sshprime.obj: ..\sshprime.c ..\ssh.h ..\mpint.h ..\mpunsafe.h ..\sshkeygen.h \ + ..\puttymem.h ..\tree234.h ..\network.h ..\misc.h \ + ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshprime.c -sshprng.obj: ..\sshprng.c ..\putty.h ..\ssh.h ..\mpint.h ..\defs.h \ +sshprng.obj: ..\sshprng.c ..\putty.h ..\ssh.h ..\mpint_i.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ @@ -1012,13 +1108,13 @@ sshrsa.obj: ..\sshrsa.c ..\ssh.h ..\mpint.h ..\misc.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrsa.c -sshrsag.obj: ..\sshrsag.c ..\ssh.h ..\mpint.h ..\puttymem.h ..\tree234.h \ - ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ - ..\marshal.h +sshrsag.obj: ..\sshrsag.c ..\ssh.h ..\sshkeygen.h ..\mpint.h ..\puttymem.h \ + ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ + ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrsag.c sshserver.obj: ..\sshserver.c ..\putty.h ..\ssh.h ..\sshbpp.h ..\sshppl.h \ - ..\sshserver.h ..\sshgssc.h ..\sshgss.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshchan.h ..\sshserver.h ..\sshgssc.h ..\sshgss.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\pgssapi.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h @@ -1032,12 +1128,21 @@ sshsh512.obj: ..\sshsh512.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ sshsha.obj: ..\sshsha.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsha.c +sshsha3.obj: ..\sshsha3.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ + ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsha3.c sshshare.obj: ..\sshshare.c ..\putty.h ..\tree234.h ..\ssh.h ..\sshcr.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshshare.c +sshutils.obj: ..\sshutils.c ..\putty.h ..\ssh.h ..\sshchan.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshutils.c sshverstring.obj: ..\sshverstring.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshcr.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ @@ -1053,6 +1158,11 @@ stripctrl.obj: ..\stripctrl.c ..\putty.h ..\terminal.h ..\misc.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\stripctrl.c +supdup.obj: ..\supdup.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ + ..\unix\unix.h ..\puttymem.h ..\tree234.h \ + ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\supdup.c telnet.obj: ..\telnet.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ @@ -1063,9 +1173,9 @@ terminal.obj: ..\terminal.c ..\putty.h ..\terminal.h ..\defs.h ..\puttyps.h \ ..\tree234.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\terminal.c -testcrypt.obj: ..\testcrypt.c ..\defs.h ..\ssh.h ..\misc.h ..\mpint.h \ - ..\ecc.h ..\testcrypt.h ..\puttymem.h ..\tree234.h \ - ..\network.h ..\sshttymodes.h ..\marshal.h +testcrypt.obj: ..\testcrypt.c ..\defs.h ..\ssh.h ..\sshkeygen.h ..\misc.h \ + ..\mpint.h ..\ecc.h ..\testcrypt.h ..\puttymem.h \ + ..\tree234.h ..\network.h ..\sshttymodes.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\testcrypt.c testsc.obj: ..\testsc.c ..\defs.h ..\putty.h ..\ssh.h ..\misc.h ..\mpint.h \ ..\ecc.h ..\puttyps.h ..\network.h ..\marshal.h \ @@ -1089,7 +1199,8 @@ tree234.obj: ..\tree234.c ..\defs.h ..\tree234.h ..\puttymem.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\tree234.c utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\utf8.c -utils.obj: ..\utils.c ..\defs.h ..\misc.h ..\puttymem.h ..\marshal.h +utils.obj: ..\utils.c ..\defs.h ..\misc.h ..\ssh.h ..\puttymem.h \ + ..\marshal.h ..\tree234.h ..\network.h ..\sshttymodes.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\utils.c ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ @@ -1114,8 +1225,13 @@ uxcfg.obj: ..\unix\uxcfg.c ..\putty.h ..\dialog.h ..\storage.h ..\defs.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcfg.c -uxcons.obj: ..\unix\uxcons.c ..\putty.h ..\storage.h ..\ssh.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ +uxcliloop.obj: ..\unix\uxcliloop.c ..\putty.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ + ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcliloop.c +uxcons.obj: ..\unix\uxcons.c ..\putty.h ..\storage.h ..\ssh.h ..\console.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h @@ -1168,10 +1284,11 @@ uxpgnt.obj: ..\unix\uxpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\pageant.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpgnt.c -uxplink.obj: ..\unix\uxplink.c ..\putty.h ..\storage.h ..\tree234.h \ +uxplink.obj: ..\unix\uxplink.c ..\putty.h ..\ssh.h ..\storage.h ..\tree234.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h + ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxplink.c uxpoll.obj: ..\unix\uxpoll.c ..\putty.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ @@ -1188,21 +1305,28 @@ uxproxy.obj: ..\unix\uxproxy.c ..\tree234.h ..\putty.h ..\network.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxproxy.c +uxpsusan.obj: ..\unix\uxpsusan.c ..\putty.h ..\mpint.h ..\ssh.h \ + ..\sshserver.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ + ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpsusan.c uxpterm.obj: ..\unix\uxpterm.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpterm.c -uxpty.obj: ..\unix\uxpty.c ..\putty.h ..\ssh.h ..\tree234.h ..\sshttymodes.h \ - ..\sshsignals.h ..\defs.h ..\puttyps.h ..\network.h \ - ..\misc.h ..\marshal.h ..\puttymem.h ..\windows\winstuff.h \ - ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h - lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpty.c -uxputty.obj: ..\unix\uxputty.c ..\putty.h ..\storage.h ..\unix\gtkcompat.h \ - ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ +uxpty.obj: ..\unix\uxpty.c ..\putty.h ..\ssh.h ..\sshserver.h ..\tree234.h \ + ..\sshttymodes.h ..\sshsignals.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\misc.h ..\marshal.h ..\puttymem.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpty.c +uxputty.obj: ..\unix\uxputty.c ..\putty.h ..\ssh.h ..\storage.h \ + ..\unix\gtkcompat.h ..\defs.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ + ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ + ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxputty.c uxsel.obj: ..\unix\uxsel.c ..\putty.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ @@ -1226,11 +1350,11 @@ uxsftp.obj: ..\unix\uxsftp.c ..\putty.h ..\ssh.h ..\psftp.h ..\defs.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsftp.c -uxsftpserver.obj: ..\unix\uxsftpserver.c ..\putty.h ..\ssh.h ..\sftp.h \ - ..\tree234.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ - ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ - ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ - ..\charset\charset.h +uxsftpserver.obj: ..\unix\uxsftpserver.c ..\putty.h ..\ssh.h ..\sshserver.h \ + ..\sftp.h ..\tree234.h ..\defs.h ..\puttyps.h ..\network.h \ + ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsftpserver.c uxshare.obj: ..\unix\uxshare.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\ssh.h ..\defs.h ..\puttyps.h ..\misc.h \ @@ -1240,6 +1364,12 @@ uxshare.obj: ..\unix\uxshare.c ..\tree234.h ..\putty.h ..\network.h \ lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxshare.c uxsignal.obj: ..\unix\uxsignal.c ..\defs.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsignal.c +uxsocks.obj: ..\unix\uxsocks.c ..\putty.h ..\ssh.h ..\psocks.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsocks.c uxstore.obj: ..\unix\uxstore.c ..\putty.h ..\storage.h ..\tree234.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ @@ -1250,13 +1380,17 @@ uxucs.obj: ..\unix\uxucs.c ..\putty.h ..\charset\charset.h ..\terminal.h \ ..\sshsignals.h ..\tree234.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxucs.c -uxutils.obj: ..\unix\uxutils.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ - ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ - ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ +uxutils.obj: ..\unix\uxutils.c ..\putty.h ..\ssh.h ..\unix\uxutils.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxutils.c -version.obj: ..\version.c ..\empty.h ..\version.h +version.obj: ..\version.c ..\putty.h ..\ssh.h ..\empty.h ..\version.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\version.c wcwidth.obj: ..\wcwidth.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ @@ -1268,10 +1402,10 @@ wildcard.obj: ..\wildcard.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\wildcard.c -wincapi.obj: ..\windows\wincapi.c ..\putty.h ..\windows\wincapi.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ +wincapi.obj: ..\windows\wincapi.c ..\putty.h ..\ssh.h ..\windows\wincapi.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincapi.c wincfg.obj: ..\windows\wincfg.c ..\putty.h ..\dialog.h ..\storage.h \ @@ -1280,11 +1414,16 @@ wincfg.obj: ..\windows\wincfg.c ..\putty.h ..\dialog.h ..\storage.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincfg.c -wincons.obj: ..\windows\wincons.c ..\putty.h ..\storage.h ..\ssh.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ - ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ - ..\charset\charset.h +wincliloop.obj: ..\windows\wincliloop.c ..\putty.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ + ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincliloop.c +wincons.obj: ..\windows\wincons.c ..\putty.h ..\storage.h ..\ssh.h \ + ..\console.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ + ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincons.c winctrls.obj: ..\windows\winctrls.c ..\putty.h ..\misc.h ..\dialog.h \ ..\defs.h ..\puttyps.h ..\network.h ..\marshal.h \ @@ -1298,17 +1437,18 @@ windefs.obj: ..\windows\windefs.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\windefs.c windlg.obj: ..\windows\windlg.c ..\putty.h ..\ssh.h ..\windows\win_res.h \ - ..\storage.h ..\dialog.h ..\licence.h ..\defs.h ..\puttyps.h \ - ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ - ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winseat.h ..\storage.h ..\dialog.h ..\licence.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\windlg.c window.obj: ..\windows\window.c ..\putty.h ..\terminal.h ..\storage.h \ - ..\windows\win_res.h ..\windows\winsecur.h ..\tree234.h \ - ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h + ..\windows\win_res.h ..\windows\winsecur.h \ + ..\windows\winseat.h ..\tree234.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\window.c wingss.obj: ..\windows\wingss.c ..\putty.h ..\pgssapi.h ..\sshgss.h \ ..\sshgssc.h ..\misc.h ..\defs.h ..\puttyps.h ..\network.h \ @@ -1349,9 +1489,10 @@ winmiscs.obj: ..\windows\winmiscs.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winmiscs.c winnet.obj: ..\windows\winnet.c ..\putty.h ..\network.h ..\tree234.h \ - ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ - ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ - ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h + ..\ssh.h ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnet.c winnohlp.obj: ..\windows\winnohlp.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ @@ -1378,23 +1519,26 @@ winnps.obj: ..\windows\winnps.c ..\tree234.h ..\putty.h ..\network.h \ ..\puttymem.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnps.c -winpgen.obj: ..\windows\winpgen.c ..\putty.h ..\ssh.h ..\licence.h \ - ..\windows\winsecur.h ..\defs.h ..\puttyps.h ..\network.h \ - ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ - ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ - ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h +winpgen.obj: ..\windows\winpgen.c ..\putty.h ..\ssh.h ..\sshkeygen.h \ + ..\licence.h ..\windows\winsecur.h ..\windows\puttygen-rc.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgen.c winpgnt.obj: ..\windows\winpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\tree234.h \ - ..\windows\winsecur.h ..\pageant.h ..\licence.h ..\defs.h \ - ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ - ..\puttymem.h ..\sshttymodes.h ..\windows\winstuff.h \ - ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h + ..\windows\winsecur.h ..\windows\wincapi.h ..\pageant.h \ + ..\licence.h ..\windows\pageant-rc.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ + ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgnt.c winpgntc.obj: ..\windows\winpgntc.c ..\putty.h ..\pageant.h \ - ..\windows\winsecur.h ..\defs.h ..\puttyps.h ..\network.h \ - ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ - ..\unix\unix.h ..\puttymem.h ..\tree234.h \ - ..\windows\winhelp.h ..\charset\charset.h + ..\windows\winsecur.h ..\windows\wincapi.h ..\defs.h \ + ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ + ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ + ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgntc.c winplink.obj: ..\windows\winplink.c ..\putty.h ..\storage.h ..\tree234.h \ ..\windows\winsecur.h ..\defs.h ..\puttyps.h ..\network.h \ @@ -1418,6 +1562,16 @@ winsecur.obj: ..\windows\winsecur.c ..\putty.h ..\windows\winsecur.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winsecur.c +winselcli.obj: ..\windows\winselcli.c ..\putty.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ + ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winselcli.c +winselgui.obj: ..\windows\winselgui.c ..\putty.h ..\defs.h ..\puttyps.h \ + ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ + ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winselgui.c winser.obj: ..\windows\winser.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ @@ -1436,6 +1590,12 @@ winshare.obj: ..\windows\winshare.c ..\tree234.h ..\putty.h ..\network.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winshare.c +winsocks.obj: ..\windows\winsocks.c ..\putty.h ..\ssh.h ..\psocks.h \ + ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ + ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ + ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ + ..\charset\charset.h + lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winsocks.c winstore.obj: ..\windows\winstore.c ..\putty.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ diff --git a/windows/Makefile.mgw b/windows/Makefile.mgw index 75d7b4a..6a16955 100644 --- a/windows/Makefile.mgw +++ b/windows/Makefile.mgw @@ -67,11 +67,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=-DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # @@ -120,68 +115,99 @@ CFLAGS += -DWINVER=0x0500 -D_WIN32_WINDOWS=0x0410 -D_WIN32_WINNT=0x0500 .SUFFIXES: -all: pageant.exe plink.exe pscp.exe psftp.exe putty.exe puttygen.exe \ - puttytel.exe testcrypt.exe +all: pageant.exe plink.exe pscp.exe psftp.exe psocks.exe putty.exe \ + puttygen.exe puttytel.exe testcrypt.exe -pageant.exe: aqsync.o conf.o ecc.o marshal.o memory.o misc.o mpint.o \ - pageant.o pageant.res.o sshaes.o sshauxcrypt.o sshdes.o \ - sshdss.o sshecc.o sshhmac.o sshmd5.o sshpubk.o sshrsa.o \ - sshsh256.o sshsh512.o sshsha.o stripctrl.o tree234.o utils.o \ - version.o wcwidth.o winhelp.o winmisc.o winmiscs.o winpgnt.o \ - winpgntc.o winsecur.o winutils.o +pageant.exe: aqsync.o be_misc.o callback.o conf.o ecc.o errsock.o marshal.o \ + memory.o misc.o mpint.o pageant.o pageant.res.o sshaes.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshdes.o sshdss.o \ + sshecc.o sshhmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ + sshsh512.o sshsha.o sshsha3.o stripctrl.o tree234.o utils.o \ + version.o wcwidth.o wincapi.o winhandl.o winhelp.o \ + winhsock.o winmisc.o winmiscs.o winnet.o winnpc.o winnps.o \ + winpgnt.o winpgntc.o winsecur.o winselgui.o winutils.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map aqsync.o \ - conf.o ecc.o marshal.o memory.o misc.o mpint.o pageant.o \ - pageant.res.o sshaes.o sshauxcrypt.o sshdes.o sshdss.o \ + be_misc.o callback.o conf.o ecc.o errsock.o marshal.o \ + memory.o misc.o mpint.o pageant.o pageant.res.o sshaes.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshdes.o sshdss.o \ sshecc.o sshhmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ - sshsh512.o sshsha.o stripctrl.o tree234.o utils.o version.o \ - wcwidth.o winhelp.o winmisc.o winmiscs.o winpgnt.o \ - winpgntc.o winsecur.o winutils.o -ladvapi32 -lcomdlg32 \ - -lgdi32 -limm32 -lole32 -lshell32 -luser32 + sshsh512.o sshsha.o sshsha3.o stripctrl.o tree234.o utils.o \ + version.o wcwidth.o wincapi.o winhandl.o winhelp.o \ + winhsock.o winmisc.o winmiscs.o winnet.o winnpc.o winnps.o \ + winpgnt.o winpgntc.o winsecur.o winselgui.o winutils.o \ + -ladvapi32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ + -luser32 -plink.exe: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o \ - conf.o cproxy.o ecc.o errsock.o ldisc.o logging.o mainchan.o \ - marshal.o memory.o misc.o miscucs.o mpint.o noterm.o \ - nullplug.o pgssapi.o pinger.o plink.res.o portfwd.o proxy.o \ - raw.o rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o \ +plink.exe: agentf.o aqsync.o be_all_s.o be_misc.o callback.o clicons.o \ + cmdline.o conf.o console.o cproxy.o ecc.o errsock.o ldisc.o \ + logging.o mainchan.o marshal.o memory.o misc.o miscucs.o \ + mpint.o noterm.o nullplug.o pgssapi.o pinger.o plink.res.o \ + portfwd.o proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o \ + ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ + ssh2censor.o ssh2connection.o ssh2connection-client.o \ + ssh2kex-client.o ssh2transhk.o ssh2transport.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ + telnet.o timing.o tree234.o utils.o version.o wcwidth.o \ + wildcard.o wincapi.o wincliloop.o wincons.o windefs.o \ + wingss.o winhandl.o winhsock.o winmisc.o winmiscs.o winnet.o \ + winnohlp.o winnoise.o winnojmp.o winnpc.o winnps.o \ + winpgntc.o winplink.o winproxy.o winsecur.o winselcli.o \ + winser.o winshare.o winstore.o wintime.o winucs.o winx11.o \ + x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,plink.map agentf.o aqsync.o \ + be_all_s.o be_misc.o callback.o clicons.o cmdline.o conf.o \ + console.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ + mainchan.o marshal.o memory.o misc.o miscucs.o mpint.o \ + noterm.o nullplug.o pgssapi.o pinger.o plink.res.o portfwd.o \ + proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-client.o \ ssh1login.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-client.o ssh2kex-client.o \ ssh2transhk.o ssh2transport.o ssh2userauth.o sshaes.o \ - ssharcf.o sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o \ - sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o \ - sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o \ - sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o timing.o \ - tree234.o utils.o version.o wcwidth.o wildcard.o wincapi.o \ - wincons.o windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ - winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ - winnpc.o winnps.o winpgntc.o winplink.o winproxy.o \ - winsecur.o winser.o winshare.o winstore.o wintime.o winucs.o \ - winx11.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,plink.map agentf.o aqsync.o \ - be_all_s.o be_misc.o callback.o cmdline.o conf.o cproxy.o \ - ecc.o errsock.o ldisc.o logging.o mainchan.o marshal.o \ - memory.o misc.o miscucs.o mpint.o noterm.o nullplug.o \ - pgssapi.o pinger.o plink.res.o portfwd.o proxy.o raw.o \ - rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-client.o ssh1login.o \ - ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ - ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ - ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ + sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ + sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ + sshsha.o sshsha3.o sshshare.o sshutils.o sshverstring.o \ + sshzlib.o stripctrl.o supdup.o telnet.o timing.o tree234.o \ + utils.o version.o wcwidth.o wildcard.o wincapi.o \ + wincliloop.o wincons.o windefs.o wingss.o winhandl.o \ + winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ + winnoise.o winnojmp.o winnpc.o winnps.o winpgntc.o \ + winplink.o winproxy.o winsecur.o winselcli.o winser.o \ + winshare.o winstore.o wintime.o winucs.o winx11.o x11fwd.o \ + -ladvapi32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ + -luser32 + +pscp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o \ + cmdline.o conf.o console.o cproxy.o ecc.o errsock.o \ + logging.o mainchan.o marshal.o memory.o misc.o miscucs.o \ + mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ + pscp.o pscp.res.o psftpcommon.o settings.o sftp.o \ + sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ + ssh2censor.o ssh2connection.o ssh2connection-client.o \ + ssh2kex-client.o ssh2transhk.o ssh2transport.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o timing.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o timing.o \ tree234.o utils.o version.o wcwidth.o wildcard.o wincapi.o \ - wincons.o windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ - winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ - winnpc.o winnps.o winpgntc.o winplink.o winproxy.o \ - winsecur.o winser.o winshare.o winstore.o wintime.o winucs.o \ - winx11.o x11fwd.o -ladvapi32 -lcomdlg32 -lgdi32 -limm32 \ - -lole32 -lshell32 -luser32 - -pscp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ + wincliloop.o wincons.o windefs.o wingss.o winhandl.o \ + winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ + winnoise.o winnojmp.o winnpc.o winnps.o winpgntc.o \ + winproxy.o winsecur.o winselcli.o winsftp.o winshare.o \ + winstore.o wintime.o winucs.o x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map agentf.o aqsync.o be_misc.o \ + be_ssh.o callback.o clicons.o cmdline.o conf.o console.o \ cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ memory.o misc.o miscucs.o mpint.o nullplug.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o pscp.res.o psftpcommon.o \ @@ -190,139 +216,145 @@ pscp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o timing.o tree234.o \ - utils.o version.o wcwidth.o wildcard.o wincapi.o wincons.o \ - windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ - winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ - winnpc.o winnps.o winpgntc.o winproxy.o winsecur.o winsftp.o \ - winshare.o winstore.o wintime.o winucs.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map agentf.o aqsync.o be_misc.o \ - be_ssh.o callback.o cmdline.o conf.o cproxy.o ecc.o \ - errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ - miscucs.o mpint.o nullplug.o pgssapi.o pinger.o portfwd.o \ - proxy.o pscp.o pscp.res.o psftpcommon.o settings.o sftp.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ + sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ + sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ + stripctrl.o timing.o tree234.o utils.o version.o wcwidth.o \ + wildcard.o wincapi.o wincliloop.o wincons.o windefs.o \ + wingss.o winhandl.o winhsock.o winmisc.o winmiscs.o winnet.o \ + winnohlp.o winnoise.o winnojmp.o winnpc.o winnps.o \ + winpgntc.o winproxy.o winsecur.o winselcli.o winsftp.o \ + winshare.o winstore.o wintime.o winucs.o x11fwd.o -ladvapi32 \ + -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 -luser32 + +psftp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o \ + cmdline.o conf.o console.o cproxy.o ecc.o errsock.o \ + logging.o mainchan.o marshal.o memory.o misc.o miscucs.o \ + mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ + psftp.o psftp.res.o psftpcommon.o settings.o sftp.o \ sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ - ssh2userauth.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o timing.o \ + tree234.o utils.o version.o wcwidth.o wildcard.o wincapi.o \ + wincliloop.o wincons.o windefs.o wingss.o winhandl.o \ + winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ + winnoise.o winnojmp.o winnpc.o winnps.o winpgntc.o \ + winproxy.o winsecur.o winselcli.o winsftp.o winshare.o \ + winstore.o wintime.o winucs.o x11fwd.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map agentf.o aqsync.o \ + be_misc.o be_ssh.o callback.o clicons.o cmdline.o conf.o \ + console.o cproxy.o ecc.o errsock.o logging.o mainchan.o \ + marshal.o memory.o misc.o miscucs.o mpint.o nullplug.o \ + pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftp.res.o \ + psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o \ + ssh1censor.o ssh1connection.o ssh1connection-client.o \ + ssh1login.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ + ssh2connection.o ssh2connection-client.o ssh2kex-client.o \ + ssh2transhk.o ssh2transport.o ssh2userauth.o sshaes.o \ + ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshshare.o sshverstring.o sshzlib.o stripctrl.o \ - timing.o tree234.o utils.o version.o wcwidth.o wildcard.o \ - wincapi.o wincons.o windefs.o wingss.o winhandl.o winhsock.o \ - winmisc.o winmiscs.o winnet.o winnohlp.o winnoise.o \ - winnojmp.o winnpc.o winnps.o winpgntc.o winproxy.o \ - winsecur.o winsftp.o winshare.o winstore.o wintime.o \ + sshsha.o sshsha3.o sshshare.o sshutils.o sshverstring.o \ + sshzlib.o stripctrl.o timing.o tree234.o utils.o version.o \ + wcwidth.o wildcard.o wincapi.o wincliloop.o wincons.o \ + windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ + winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ + winnpc.o winnps.o winpgntc.o winproxy.o winsecur.o \ + winselcli.o winsftp.o winshare.o winstore.o wintime.o \ winucs.o x11fwd.o -ladvapi32 -lcomdlg32 -lgdi32 -limm32 \ -lole32 -lshell32 -luser32 -psftp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o cmdline.o conf.o \ - cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ - memory.o misc.o miscucs.o mpint.o nullplug.o pgssapi.o \ - pinger.o portfwd.o proxy.o psftp.o psftp.res.o psftpcommon.o \ - settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-client.o ssh1login.o \ - ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ - ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ - ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o timing.o tree234.o \ - utils.o version.o wcwidth.o wildcard.o wincapi.o wincons.o \ - windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ - winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ - winnpc.o winnps.o winpgntc.o winproxy.o winsecur.o winsftp.o \ - winshare.o winstore.o wintime.o winucs.o x11fwd.o - $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map agentf.o aqsync.o \ - be_misc.o be_ssh.o callback.o cmdline.o conf.o cproxy.o \ - ecc.o errsock.o logging.o mainchan.o marshal.o memory.o \ - misc.o miscucs.o mpint.o nullplug.o pgssapi.o pinger.o \ - portfwd.o proxy.o psftp.o psftp.res.o psftpcommon.o \ - settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-client.o ssh1login.o \ - ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ - ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ - ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ - sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ - sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o timing.o tree234.o \ - utils.o version.o wcwidth.o wildcard.o wincapi.o wincons.o \ - windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ - winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ - winnpc.o winnps.o winpgntc.o winproxy.o winsecur.o winsftp.o \ - winshare.o winstore.o wintime.o winucs.o x11fwd.o -ladvapi32 \ - -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 -luser32 +psocks.exe: be_misc.o callback.o conf.o console.o errsock.o logging.o \ + marshal.o memory.o misc.o nocproxy.o norand.o portfwd.o \ + proxy.o psocks.o sshutils.o stripctrl.o time.o timing.o \ + tree234.o utils.o version.o wcwidth.o wincliloop.o wincons.o \ + winhandl.o winhsock.o winmisc.o winmiscs.o winnet.o \ + winnohlp.o winproxy.o winselcli.o winsocks.o + $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psocks.map be_misc.o callback.o \ + conf.o console.o errsock.o logging.o marshal.o memory.o \ + misc.o nocproxy.o norand.o portfwd.o proxy.o psocks.o \ + sshutils.o stripctrl.o time.o timing.o tree234.o utils.o \ + version.o wcwidth.o wincliloop.o wincons.o winhandl.o \ + winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ + winproxy.o winselcli.o winsocks.o -ladvapi32 -lcomdlg32 \ + -lgdi32 -limm32 -lole32 -lshell32 -luser32 putty.exe: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o \ conf.o config.o cproxy.o dialog.o ecc.o errsock.o ldisc.o \ logging.o mainchan.o marshal.o memory.o minibidi.o misc.o \ miscucs.o mpint.o nullplug.o pgssapi.o pinger.o portfwd.o \ - proxy.o putty.res.o raw.o rlogin.o sercfg.o sessprep.o \ - settings.o sizetip.o ssh.o ssh1bpp.o ssh1censor.o \ - ssh1connection.o ssh1connection-client.o ssh1login.o \ - ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ - ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ - ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + proxy.o putty.res.o raw.o rlogin.o sessprep.o settings.o \ + sizetip.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ + ssh2censor.o ssh2connection.o ssh2connection-client.o \ + ssh2kex-client.o ssh2transhk.o ssh2transport.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshsh256.o sshsh512.o sshsha.o sshshare.o \ - sshverstring.o sshzlib.o stripctrl.o telnet.o terminal.o \ - timing.o tree234.o utils.o version.o wcwidth.o wildcard.o \ - wincapi.o wincfg.o winctrls.o windefs.o windlg.o window.o \ - wingss.o winhandl.o winhelp.o winhsock.o winjump.o winmisc.o \ - winmiscs.o winnet.o winnoise.o winnpc.o winnps.o winpgntc.o \ - winprint.o winproxy.o winsecur.o winser.o winshare.o \ - winstore.o wintime.o winucs.o winutils.o winx11.o x11fwd.o \ - cencode.o cdecode.o + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ + telnet.o terminal.o timing.o tree234.o utils.o version.o \ + wcwidth.o wildcard.o wincapi.o wincfg.o winctrls.o windefs.o \ + windlg.o window.o wingss.o winhandl.o winhelp.o winhsock.o \ + winjump.o winmisc.o winmiscs.o winnet.o winnoise.o winnpc.o \ + winnps.o winpgntc.o winprint.o winproxy.o winsecur.o \ + winselgui.o winser.o winshare.o winstore.o wintime.o \ + winucs.o winutils.o winx11.o x11fwd.o \ + cencode.o cdecode.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,putty.map agentf.o \ aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ config.o cproxy.o dialog.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o minibidi.o misc.o miscucs.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ - putty.res.o raw.o rlogin.o sercfg.o sessprep.o settings.o \ - sizetip.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ + putty.res.o raw.o rlogin.o sessprep.o settings.o sizetip.o \ + ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ - ssh2userauth.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ - sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ - sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ - sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ - sshsha.o sshshare.o sshverstring.o sshzlib.o stripctrl.o \ + ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ + sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ + sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ + sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ + sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ telnet.o terminal.o timing.o tree234.o utils.o version.o \ wcwidth.o wildcard.o wincapi.o wincfg.o winctrls.o windefs.o \ windlg.o window.o wingss.o winhandl.o winhelp.o winhsock.o \ winjump.o winmisc.o winmiscs.o winnet.o winnoise.o winnpc.o \ winnps.o winpgntc.o winprint.o winproxy.o winsecur.o \ - winser.o winshare.o winstore.o wintime.o winucs.o winutils.o \ - winx11.o cencode.o cdecode.o x11fwd.o -ladvapi32 -lcomdlg32 -lgdi32 -limm32 \ - -lole32 -lshell32 -luser32 + winselgui.o winser.o winshare.o winstore.o wintime.o \ + winucs.o winutils.o winx11.o x11fwd.o cencode.o cdecode.o -ladvapi32 -lcomdlg32 \ + -lgdi32 -limm32 -lole32 -lshell32 -luser32 -puttygen.exe: conf.o ecc.o import.o marshal.o memory.o misc.o mpint.o \ - notiming.o puttygen.res.o sshaes.o sshauxcrypt.o sshbcrypt.o \ - sshblowf.o sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o \ - sshhmac.o sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o \ - sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ - stripctrl.o tree234.o utils.o version.o wcwidth.o winctrls.o \ - winhelp.o winmisc.o winmiscs.o winnoise.o winnojmp.o \ - winpgen.o winsecur.o winstore.o wintime.o winutils.o +puttygen.exe: conf.o ecc.o import.o marshal.o memory.o millerrabin.o misc.o \ + mpint.o mpunsafe.o notiming.o pockle.o primecandidate.o \ + puttygen.res.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o \ + tree234.o utils.o version.o wcwidth.o winctrls.o winhelp.o \ + winmisc.o winmiscs.o winnoise.o winnojmp.o winpgen.o \ + winsecur.o winstore.o wintime.o winutils.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map conf.o ecc.o \ - import.o marshal.o memory.o misc.o mpint.o notiming.o \ - puttygen.res.o sshaes.o sshauxcrypt.o sshbcrypt.o sshblowf.o \ - sshdes.o sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o \ - sshmd5.o sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o \ - sshrsag.o sshsh256.o sshsh512.o sshsha.o stripctrl.o \ + import.o marshal.o memory.o millerrabin.o misc.o mpint.o \ + mpunsafe.o notiming.o pockle.o primecandidate.o \ + puttygen.res.o smallprimes.o sshaes.o sshargon2.o \ + sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ + sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ + sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ + sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o \ tree234.o utils.o version.o wcwidth.o winctrls.o winhelp.o \ winmisc.o winmiscs.o winnoise.o winnojmp.o winpgen.o \ winsecur.o winstore.o wintime.o winutils.o -ladvapi32 \ @@ -330,44 +362,48 @@ puttygen.exe: conf.o ecc.o import.o marshal.o memory.o misc.o mpint.o \ puttytel.exe: be_misc.o be_nos_s.o callback.o cmdline.o conf.o config.o \ dialog.o errsock.o ldisc.o logging.o marshal.o memory.o \ - minibidi.o misc.o miscucs.o nocproxy.o nogss.o pinger.o \ - proxy.o puttytel.res.o raw.o rlogin.o sercfg.o sessprep.o \ - settings.o sizetip.o stripctrl.o telnet.o terminal.o \ - timing.o tree234.o utils.o version.o wcwidth.o wincfg.o \ - winctrls.o windefs.o windlg.o window.o winhandl.o winhelp.o \ - winhsock.o winjump.o winmisc.o winmiscs.o winnet.o \ - winprint.o winproxy.o winsecur.o winser.o winstore.o \ - wintime.o winucs.o winutils.o + minibidi.o misc.o miscucs.o nocproxy.o nogss.o norand.o \ + pinger.o proxy.o puttytel.res.o raw.o rlogin.o sessprep.o \ + settings.o sizetip.o stripctrl.o supdup.o telnet.o \ + terminal.o timing.o tree234.o utils.o version.o wcwidth.o \ + wincfg.o winctrls.o windefs.o windlg.o window.o winhandl.o \ + winhelp.o winhsock.o winjump.o winmisc.o winmiscs.o winnet.o \ + winprint.o winproxy.o winsecur.o winselgui.o winser.o \ + winstore.o wintime.o winucs.o winutils.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_misc.o \ be_nos_s.o callback.o cmdline.o conf.o config.o dialog.o \ errsock.o ldisc.o logging.o marshal.o memory.o minibidi.o \ - misc.o miscucs.o nocproxy.o nogss.o pinger.o proxy.o \ - puttytel.res.o raw.o rlogin.o sercfg.o sessprep.o settings.o \ - sizetip.o stripctrl.o telnet.o terminal.o timing.o tree234.o \ - utils.o version.o wcwidth.o wincfg.o winctrls.o windefs.o \ - windlg.o window.o winhandl.o winhelp.o winhsock.o winjump.o \ - winmisc.o winmiscs.o winnet.o winprint.o winproxy.o \ - winsecur.o winser.o winstore.o wintime.o winucs.o winutils.o \ - -ladvapi32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ - -luser32 - -testcrypt.exe: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ - sshauxcrypt.o sshblowf.o sshccp.o sshcrc.o sshcrcda.o \ - sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o sshmd5.o \ - sshprime.o sshprng.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ - testcrypt.o tree234.o utils.o winmiscs.o + misc.o miscucs.o nocproxy.o nogss.o norand.o pinger.o \ + proxy.o puttytel.res.o raw.o rlogin.o sessprep.o settings.o \ + sizetip.o stripctrl.o supdup.o telnet.o terminal.o timing.o \ + tree234.o utils.o version.o wcwidth.o wincfg.o winctrls.o \ + windefs.o windlg.o window.o winhandl.o winhelp.o winhsock.o \ + winjump.o winmisc.o winmiscs.o winnet.o winprint.o \ + winproxy.o winsecur.o winselgui.o winser.o winstore.o \ + wintime.o winucs.o winutils.o -ladvapi32 -lcomdlg32 -lgdi32 \ + -limm32 -lole32 -lshell32 -luser32 + +testcrypt.exe: ecc.o marshal.o memory.o millerrabin.o mpint.o mpunsafe.o \ + pockle.o primecandidate.o smallprimes.o sshaes.o ssharcf.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ + sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ + sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o testcrypt.o tree234.o utils.o winmiscs.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,testcrypt.map ecc.o marshal.o \ - memory.o mpint.o sshaes.o ssharcf.o sshauxcrypt.o sshblowf.o \ - sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ - sshecc.o sshhmac.o sshmd5.o sshprime.o sshprng.o sshrsa.o \ - sshsh256.o sshsh512.o sshsha.o testcrypt.o tree234.o utils.o \ - winmiscs.o + memory.o millerrabin.o mpint.o mpunsafe.o pockle.o \ + primecandidate.o smallprimes.o sshaes.o ssharcf.o \ + sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ + sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ + sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ + sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ + sshsha3.o testcrypt.o tree234.o utils.o winmiscs.o -cencode.o: ../windows/cencode.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/cencode.c +cencode.o: ../far2l/cencode.c + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../far2l/cencode.c -I../far2l -cdecode.o: ../windows/cencode.c - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/cdecode.c +cdecode.o: ../far2l/cencode.c + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../far2l/cdecode.c -I../far2l agentf.o: ../agentf.c ../putty.h ../ssh.h ../pageant.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ @@ -418,16 +454,22 @@ callback.o: ../callback.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../callback.c -cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cgtest.c -cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ +clicons.o: ../clicons.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../clicons.c + +cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h ../mpint.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c @@ -451,6 +493,12 @@ config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c +console.o: ../console.c ../putty.h ../misc.h ../console.h ../defs.h \ + ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ + ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../console.c + cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -478,10 +526,11 @@ errsock.o: ../errsock.c ../tree234.h ../putty.h ../network.h ../defs.h \ fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c -fuzzterm.o: ../fuzzterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../tree234.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h +fuzzterm.o: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../tree234.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../fuzzterm.c gtkapp.o: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h ../defs.h \ @@ -517,10 +566,11 @@ gtkcomm.o: ../unix/gtkcomm.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ gtkdlg.o: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h ../unix/gtkcols.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ - ../storage.h ../dialog.h ../tree234.h ../licence.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../storage.h ../dialog.h ../tree234.h ../licence.h ../ssh.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ @@ -593,6 +643,11 @@ marshal.o: ../marshal.c ../marshal.h ../misc.h ../defs.h ../puttymem.h memory.o: ../memory.c ../defs.h ../puttymem.h ../misc.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../memory.c +millerrabin.o: ../millerrabin.c ../ssh.h ../sshkeygen.h ../mpint.h \ + ../mpunsafe.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../millerrabin.c + mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c @@ -618,6 +673,10 @@ mpint.o: ../mpint.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpint.c +mpunsafe.o: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ + ../mpint_i.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpunsafe.c + nocmdline.o: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -636,6 +695,12 @@ nogss.o: ../nogss.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c +norand.o: ../norand.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../norand.c + noterm.o: ../noterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ @@ -665,10 +730,10 @@ pageant.o: ../pageant.c ../putty.h ../mpint.h ../ssh.h ../sshcr.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pageant.c pageant.res.o: ../windows/pageant.rc ../windows/rcstuff.h \ - ../windows/winhelp.rc2 ../windows/pageant.ico \ - ../windows/pageants.ico ../windows/version.rc2 \ - ../windows/pageant.mft ../windows/win_res.h ../version.h \ - ../licence.h + ../windows/pageant-rc.h ../windows/winhelp.rc2 \ + ../windows/pageant.ico ../windows/pageants.ico \ + ../windows/version.rc2 ../windows/pageant.mft \ + ../windows/win_res.h ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc -o pageant.res.o pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../defs.h ../puttyps.h \ @@ -687,6 +752,11 @@ plink.res.o: ../windows/plink.rc ../windows/rcstuff.h ../windows/putty.ico \ ../windows/version.rc2 ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc -o plink.res.o +pockle.o: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h ../mpunsafe.h \ + ../tree234.h ../puttymem.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pockle.c + portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -694,6 +764,11 @@ portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c +primecandidate.o: ../primecandidate.c ../ssh.h ../mpint.h ../mpunsafe.h \ + ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../primecandidate.c + procnet.o: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/procnet.c @@ -732,6 +807,13 @@ psftpcommon.o: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftpcommon.c +psocks.o: ../psocks.c ../putty.h ../misc.h ../ssh.h ../sshchan.h ../psocks.h \ + ../defs.h ../puttyps.h ../network.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psocks.c + putty.res.o: ../windows/putty.rc ../windows/rcstuff.h ../windows/winhelp.rc2 \ ../windows/win_res.rc2 ../windows/putty.mft \ ../windows/win_res.h ../windows/putty.ico \ @@ -740,9 +822,10 @@ putty.res.o: ../windows/putty.rc ../windows/rcstuff.h ../windows/winhelp.rc2 \ $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc -o putty.res.o puttygen.res.o: ../windows/puttygen.rc ../windows/rcstuff.h \ - ../windows/winhelp.rc2 ../windows/puttygen.ico \ - ../windows/version.rc2 ../windows/puttygen.mft \ - ../windows/win_res.h ../version.h ../licence.h + ../windows/winhelp.rc2 ../windows/puttygen-rc.h \ + ../windows/puttygen.ico ../windows/version.rc2 \ + ../windows/puttygen.mft ../windows/win_res.h ../version.h \ + ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc -o puttygen.res.o puttytel.res.o: ../windows/puttytel.rc ../windows/rcstuff.h \ @@ -777,13 +860,6 @@ scpserver.o: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h ../sshchan.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../scpserver.c -sercfg.o: ../sercfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h - $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sercfg.c - sesschan.o: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h ../sshserver.h \ ../sftp.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ @@ -829,6 +905,11 @@ slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c +smallprimes.o: ../smallprimes.c ../ssh.h ../sshkeygen.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../smallprimes.c + ssh.o: ../ssh.c ../putty.h ../pageant.h ../tree234.h ../storage.h \ ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h \ @@ -884,11 +965,11 @@ ssh1login.o: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h ../sshbpp.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login.c ssh1login-server.o: ../ssh1login-server.c ../putty.h ../mpint.h ../ssh.h \ - ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h + ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ + ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login-server.c ssh2bpp.o: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ @@ -947,12 +1028,13 @@ ssh2kex-client.o: ../ssh2kex-client.c ../putty.h ../ssh.h ../sshbpp.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-client.c ssh2kex-server.o: ../ssh2kex-server.c ../putty.h ../ssh.h ../sshbpp.h \ - ../sshppl.h ../sshcr.h ../sshserver.h ../storage.h \ - ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ - ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ - ../pgssapi.h ../windows/winhelp.h ../charset/charset.h + ../sshppl.h ../sshcr.h ../sshserver.h ../sshkeygen.h \ + ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ + ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-server.c ssh2transhk.o: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ @@ -997,6 +1079,13 @@ ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c +sshargon2.o: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ + ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshargon2.c + sshauxcrypt.o: ../sshauxcrypt.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h @@ -1007,6 +1096,10 @@ sshbcrypt.o: ../sshbcrypt.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbcrypt.c +sshblake2.o: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblake2.c + sshblowf.o: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h @@ -1047,9 +1140,9 @@ sshdss.o: ../sshdss.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c -sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../mpint.h ../defs.h \ - ../puttymem.h ../marshal.h ../tree234.h ../network.h \ - ../sshttymodes.h +sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h ../mpint.h \ + ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ + ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ @@ -1057,9 +1150,9 @@ sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecc.c -sshecdsag.o: ../sshecdsag.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshecdsag.o: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecdsag.c sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../defs.h \ @@ -1081,12 +1174,12 @@ sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c -sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h ../sshkeygen.h \ + ../puttymem.h ../tree234.h ../network.h ../misc.h \ + ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c -sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint.h ../defs.h \ +sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ @@ -1112,14 +1205,14 @@ sshrsa.o: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c -sshrsag.o: ../sshrsag.c ../ssh.h ../mpint.h ../puttymem.h ../tree234.h \ - ../network.h ../misc.h ../sshttymodes.h ../defs.h \ - ../marshal.h +sshrsag.o: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ + ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ + ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c sshserver.o: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h ../sshppl.h \ - ../sshserver.h ../sshgssc.h ../sshgss.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshchan.h ../sshserver.h ../sshgssc.h ../sshgss.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h @@ -1137,6 +1230,10 @@ sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c +sshsha3.o: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ + ../misc.h ../sshttymodes.h ../defs.h ../marshal.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha3.c + sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ @@ -1144,6 +1241,13 @@ sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshshare.c +sshutils.o: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshutils.c + sshverstring.o: ../sshverstring.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ @@ -1161,6 +1265,12 @@ stripctrl.o: ../stripctrl.c ../putty.h ../terminal.h ../misc.h ../marshal.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../stripctrl.c +supdup.o: ../supdup.c ../putty.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../supdup.c + telnet.o: ../telnet.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -1173,9 +1283,9 @@ terminal.o: ../terminal.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c -testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../misc.h ../mpint.h ../ecc.h \ - ../testcrypt.h ../puttymem.h ../tree234.h ../network.h \ - ../sshttymodes.h ../marshal.h +testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h ../misc.h \ + ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ + ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testcrypt.c testsc.o: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h ../mpint.h \ @@ -1207,7 +1317,8 @@ tree234.o: ../tree234.c ../defs.h ../tree234.h ../puttymem.h utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c -utils.o: ../utils.c ../defs.h ../misc.h ../puttymem.h ../marshal.h +utils.o: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h ../marshal.h \ + ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../utils.c ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../defs.h \ @@ -1237,8 +1348,14 @@ uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c -uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ +uxcliloop.o: ../unix/uxcliloop.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcliloop.c + +uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../console.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h @@ -1301,10 +1418,11 @@ uxpgnt.o: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h ../pageant.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpgnt.c -uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h +uxplink.o: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h ../tree234.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c uxpoll.o: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ @@ -1325,23 +1443,31 @@ uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c +uxpsusan.o: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpsusan.c + uxpterm.o: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c -uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../tree234.h ../sshttymodes.h \ - ../sshsignals.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../puttymem.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h +uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h ../tree234.h \ + ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../puttymem.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c -uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../unix/gtkcompat.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ - ../charset/charset.h +uxputty.o: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ + ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ @@ -1370,11 +1496,11 @@ uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c -uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sftp.h \ - ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ - ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sshserver.h \ + ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ + ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftpserver.c uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ @@ -1387,6 +1513,13 @@ uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ uxsignal.o: ../unix/uxsignal.c ../defs.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c +uxsocks.o: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsocks.c + uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ @@ -1399,14 +1532,18 @@ uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c -uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ +uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../unix/uxutils.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxutils.c -version.o: ../version.c ../empty.h ../version.h +version.o: ../version.c ../putty.h ../ssh.h ../empty.h ../version.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c wcwidth.o: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h ../network.h \ @@ -1421,10 +1558,10 @@ wildcard.o: ../wildcard.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c -wincapi.o: ../windows/wincapi.c ../putty.h ../windows/wincapi.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../tree234.h ../windows/winhelp.h \ +wincapi.o: ../windows/wincapi.c ../putty.h ../ssh.h ../windows/wincapi.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincapi.c @@ -1435,11 +1572,17 @@ wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c -wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h ../defs.h \ - ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ - ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ - ../charset/charset.h +wincliloop.o: ../windows/wincliloop.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincliloop.c + +wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ + ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ + ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h ../defs.h \ @@ -1455,18 +1598,19 @@ windefs.o: ../windows/windefs.c ../putty.h ../defs.h ../puttyps.h \ $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ - ../storage.h ../dialog.h ../licence.h ../defs.h ../puttyps.h \ - ../network.h ../misc.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winseat.h ../storage.h ../dialog.h ../licence.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ - ../windows/win_res.h ../windows/winsecur.h ../tree234.h \ - ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ - ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ - ../puttymem.h ../windows/winhelp.h ../charset/charset.h + ../windows/win_res.h ../windows/winsecur.h \ + ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ @@ -1514,10 +1658,11 @@ winmiscs.o: ../windows/winmiscs.c ../putty.h ../defs.h ../puttyps.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmiscs.c -winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../defs.h \ - ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ - ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ - ../windows/winhelp.h ../charset/charset.h +winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../ssh.h \ + ../defs.h ../puttyps.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c winnohlp.o: ../windows/winnohlp.c ../putty.h ../defs.h ../puttyps.h \ @@ -1550,25 +1695,28 @@ winnps.o: ../windows/winnps.c ../tree234.h ../putty.h ../network.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnps.c -winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../licence.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ - ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h +winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../sshkeygen.h \ + ../licence.h ../windows/winsecur.h ../windows/puttygen-rc.h \ + ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ - ../windows/winsecur.h ../pageant.h ../licence.h ../defs.h \ - ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ - ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ - ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../pageant.h \ + ../licence.h ../windows/pageant-rc.h ../defs.h ../puttyps.h \ + ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ + ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c winpgntc.o: ../windows/winpgntc.c ../putty.h ../pageant.h \ - ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ - ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ - ../unix/unix.h ../puttymem.h ../tree234.h \ - ../windows/winhelp.h ../charset/charset.h + ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ @@ -1597,6 +1745,18 @@ winsecur.o: ../windows/winsecur.c ../putty.h ../windows/winsecur.h ../defs.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsecur.c +winselcli.o: ../windows/winselcli.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselcli.c + +winselgui.o: ../windows/winselgui.c ../putty.h ../defs.h ../puttyps.h \ + ../network.h ../misc.h ../marshal.h ../sshsignals.h \ + ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselgui.c + winser.o: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ @@ -1618,6 +1778,13 @@ winshare.o: ../windows/winshare.c ../tree234.h ../putty.h ../network.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winshare.c +winsocks.o: ../windows/winsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ + ../puttyps.h ../network.h ../misc.h ../marshal.h \ + ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ + ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsocks.c + winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ diff --git a/windows/Makefile.vc b/windows/Makefile.vc index c2049f6..aca3a7e 100644 --- a/windows/Makefile.vc +++ b/windows/Makefile.vc @@ -67,11 +67,6 @@ # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # -# - XFLAGS=/DTELNET_DEFAULT -# Causes PuTTY to default to the Telnet protocol (in the absence -# of Default Settings and so on to the contrary). Normally PuTTY -# will default to SSH. -# # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # @@ -106,60 +101,73 @@ CFLAGS = $(CFLAGS) /DHAS_GSSAPI all: $(BUILDDIR)pageant.exe $(BUILDDIR)plink.exe $(BUILDDIR)pscp.exe \ - $(BUILDDIR)psftp.exe $(BUILDDIR)putty.exe \ - $(BUILDDIR)puttygen.exe $(BUILDDIR)puttytel.exe \ - $(BUILDDIR)testcrypt.exe + $(BUILDDIR)psftp.exe $(BUILDDIR)psocks.exe \ + $(BUILDDIR)putty.exe $(BUILDDIR)puttygen.exe \ + $(BUILDDIR)puttytel.exe $(BUILDDIR)testcrypt.exe -$(BUILDDIR)pageant.exe: $(BUILDDIR)aqsync.obj $(BUILDDIR)conf.obj \ - $(BUILDDIR)ecc.obj $(BUILDDIR)marshal.obj \ - $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ - $(BUILDDIR)mpint.obj $(BUILDDIR)pageant.obj \ - $(BUILDDIR)pageant.res $(BUILDDIR)sshaes.obj \ - $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshdes.obj \ - $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ - $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ - $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrsa.obj \ - $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ - $(BUILDDIR)sshsha.obj $(BUILDDIR)stripctrl.obj \ +$(BUILDDIR)pageant.exe: $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ + $(BUILDDIR)callback.obj $(BUILDDIR)conf.obj \ + $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ + $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ + $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ + $(BUILDDIR)pageant.obj $(BUILDDIR)pageant.res \ + $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ + $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ + $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ + $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ + $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshpubk.obj \ + $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshsh256.obj \ + $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ + $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ - $(BUILDDIR)winhelp.obj $(BUILDDIR)winmisc.obj \ - $(BUILDDIR)winmiscs.obj $(BUILDDIR)winpgnt.obj \ + $(BUILDDIR)wincapi.obj $(BUILDDIR)winhandl.obj \ + $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ + $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ + $(BUILDDIR)winnet.obj $(BUILDDIR)winnpc.obj \ + $(BUILDDIR)winnps.obj $(BUILDDIR)winpgnt.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winsecur.obj \ - $(BUILDDIR)winutils.obj + $(BUILDDIR)winselgui.obj $(BUILDDIR)winutils.obj type < + + + + + @@ -158,17 +163,25 @@ + + + + + + + + @@ -183,6 +196,7 @@ + @@ -194,8 +208,10 @@ + + diff --git a/windows/VS2010/pageant/pageant.vcxproj.filters b/windows/VS2010/pageant/pageant.vcxproj.filters index cf8e1ad..b4c90c0 100644 --- a/windows/VS2010/pageant/pageant.vcxproj.filters +++ b/windows/VS2010/pageant/pageant.vcxproj.filters @@ -16,10 +16,16 @@ Source Files + + Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -32,8 +38,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -54,6 +64,8 @@ Source Files Source Files + + Source Files Source Files @@ -64,18 +76,32 @@ Source Files Source Files + + Source Files + + Source Files Source Files + + Source Files Source Files Source Files + + Source Files + + Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -102,6 +128,8 @@ Header Files Header Files + + Header Files Header Files @@ -124,10 +152,14 @@ Header Files Header Files + + Header Files Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2010/plink/plink.vcxproj b/windows/VS2010/plink/plink.vcxproj index 36897fd..333d7dd 100644 --- a/windows/VS2010/plink/plink.vcxproj +++ b/windows/VS2010/plink/plink.vcxproj @@ -142,8 +142,10 @@ + + @@ -182,7 +184,9 @@ + + @@ -203,10 +207,13 @@ + + + @@ -215,6 +222,7 @@ + @@ -232,6 +240,7 @@ + @@ -242,6 +251,7 @@ + diff --git a/windows/VS2010/plink/plink.vcxproj.filters b/windows/VS2010/plink/plink.vcxproj.filters index 53ef921..6fb770c 100644 --- a/windows/VS2010/plink/plink.vcxproj.filters +++ b/windows/VS2010/plink/plink.vcxproj.filters @@ -21,10 +21,14 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -101,8 +105,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -143,14 +151,20 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -167,6 +181,8 @@ Source Files Source Files + + Source Files Source Files @@ -201,6 +217,8 @@ Source Files Source Files + + Source Files Source Files @@ -219,6 +237,8 @@ Header Files + + Header Files Header Files diff --git a/windows/VS2010/pscp/pscp.vcxproj b/windows/VS2010/pscp/pscp.vcxproj index 428318f..c8bc0e1 100644 --- a/windows/VS2010/pscp/pscp.vcxproj +++ b/windows/VS2010/pscp/pscp.vcxproj @@ -142,8 +142,10 @@ + + @@ -181,7 +183,9 @@ + + @@ -202,7 +206,9 @@ + + @@ -213,6 +219,7 @@ + @@ -229,6 +236,7 @@ + @@ -238,6 +246,7 @@ + diff --git a/windows/VS2010/pscp/pscp.vcxproj.filters b/windows/VS2010/pscp/pscp.vcxproj.filters index fd97dcc..c14db17 100644 --- a/windows/VS2010/pscp/pscp.vcxproj.filters +++ b/windows/VS2010/pscp/pscp.vcxproj.filters @@ -21,10 +21,14 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -99,8 +103,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -141,8 +149,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -163,6 +175,8 @@ Source Files Source Files + + Source Files Source Files @@ -195,6 +209,8 @@ Source Files Source Files + + Source Files Source Files @@ -211,6 +227,8 @@ Header Files + + Header Files Header Files diff --git a/windows/VS2010/psftp/psftp.vcxproj b/windows/VS2010/psftp/psftp.vcxproj index b7865e0..75bc871 100644 --- a/windows/VS2010/psftp/psftp.vcxproj +++ b/windows/VS2010/psftp/psftp.vcxproj @@ -142,8 +142,10 @@ + + @@ -181,7 +183,9 @@ + + @@ -202,7 +206,9 @@ + + @@ -213,6 +219,7 @@ + @@ -229,6 +236,7 @@ + @@ -238,6 +246,7 @@ + diff --git a/windows/VS2010/psftp/psftp.vcxproj.filters b/windows/VS2010/psftp/psftp.vcxproj.filters index f90dbe8..0de5965 100644 --- a/windows/VS2010/psftp/psftp.vcxproj.filters +++ b/windows/VS2010/psftp/psftp.vcxproj.filters @@ -21,10 +21,14 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -99,8 +103,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -141,8 +149,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -163,6 +175,8 @@ Source Files Source Files + + Source Files Source Files @@ -195,6 +209,8 @@ Source Files Source Files + + Source Files Source Files @@ -211,6 +227,8 @@ Header Files + + Header Files Header Files diff --git a/windows/VS2010/putty.sln b/windows/VS2010/putty.sln index 7134ea3..bff24d1 100644 --- a/windows/VS2010/putty.sln +++ b/windows/VS2010/putty.sln @@ -8,6 +8,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pscp", "pscp\pscp.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psftp", "psftp\psftp.vcxproj", "{3caa8123-13fe-450d-bfe7-fcda524d6830}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psocks", "psocks\psocks.vcxproj", "{0ce7217e-7277-408e-add3-4e2f6072486e}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "putty", "putty\putty.vcxproj", "{8f44624a-d188-4188-b9aa-5f99f20723e1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "puttygen", "puttygen\puttygen.vcxproj", "{617b1bef-aa10-42b4-8f69-4c183bc1ec0f}" @@ -22,22 +24,30 @@ Global Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.ActiveCfg = Debug|Win32 - {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.Build.0 = Debug|Win32 - {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.ActiveCfg = Release|Win32 - {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.Build.0 = Release|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.ActiveCfg = Debug|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.Build.0 = Debug|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.ActiveCfg = Release|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.Build.0 = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.ActiveCfg = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.Build.0 = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.ActiveCfg = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.Build.0 = Release|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.ActiveCfg = Debug|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.Build.0 = Debug|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.ActiveCfg = Release|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.Build.0 = Release|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.ActiveCfg = Debug|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.Build.0 = Debug|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.ActiveCfg = Release|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.Build.0 = Release|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.ActiveCfg = Debug|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.Build.0 = Debug|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.ActiveCfg = Release|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.Build.0 = Release|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.ActiveCfg = Debug|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.Build.0 = Debug|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.ActiveCfg = Release|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.Build.0 = Release|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.ActiveCfg = Debug|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.Build.0 = Debug|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.ActiveCfg = Release|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.Build.0 = Release|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.ActiveCfg = Debug|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.Build.0 = Debug|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.ActiveCfg = Release|Win32 @@ -46,14 +56,10 @@ Global {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.Build.0 = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.ActiveCfg = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.Build.0 = Release|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.ActiveCfg = Debug|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.Build.0 = Debug|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.ActiveCfg = Release|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.Build.0 = Release|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.ActiveCfg = Debug|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.Build.0 = Debug|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.ActiveCfg = Release|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.Build.0 = Release|Win32 + {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.ActiveCfg = Debug|Win32 + {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.Build.0 = Debug|Win32 + {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.ActiveCfg = Release|Win32 + {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/windows/VS2010/putty/putty.vcxproj b/windows/VS2010/putty/putty.vcxproj index 58a4cbc..b7e7502 100644 --- a/windows/VS2010/putty/putty.vcxproj +++ b/windows/VS2010/putty/putty.vcxproj @@ -166,7 +166,6 @@ - @@ -186,7 +185,9 @@ + + @@ -207,10 +208,13 @@ + + + @@ -241,6 +245,7 @@ + @@ -293,6 +298,7 @@ + diff --git a/windows/VS2010/putty/putty.vcxproj.filters b/windows/VS2010/putty/putty.vcxproj.filters index 8bdd448..1bda6c9 100644 --- a/windows/VS2010/putty/putty.vcxproj.filters +++ b/windows/VS2010/putty/putty.vcxproj.filters @@ -70,8 +70,6 @@ Source Files Source Files - - Source Files Source Files @@ -110,8 +108,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -152,14 +154,20 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -220,6 +228,8 @@ Source Files Source Files + + Source Files Source Files @@ -322,6 +332,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2010/puttygen/puttygen.vcxproj b/windows/VS2010/puttygen/puttygen.vcxproj index 214fe70..2ed3102 100644 --- a/windows/VS2010/puttygen/puttygen.vcxproj +++ b/windows/VS2010/puttygen/puttygen.vcxproj @@ -142,12 +142,19 @@ + + + + + + + @@ -165,6 +172,7 @@ + @@ -193,12 +201,14 @@ + + @@ -206,6 +216,7 @@ + diff --git a/windows/VS2010/puttygen/puttygen.vcxproj.filters b/windows/VS2010/puttygen/puttygen.vcxproj.filters index cb260be..5b6d168 100644 --- a/windows/VS2010/puttygen/puttygen.vcxproj.filters +++ b/windows/VS2010/puttygen/puttygen.vcxproj.filters @@ -21,18 +21,32 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files + + Source Files + + Source Files + + Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -67,6 +81,8 @@ Source Files Source Files + + Source Files Source Files @@ -121,6 +137,8 @@ Header Files Header Files + + Header Files Header Files @@ -133,6 +151,8 @@ Header Files Header Files + + Header Files Header Files @@ -147,6 +167,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2010/puttytel/puttytel.vcxproj b/windows/VS2010/puttytel/puttytel.vcxproj index e5f5562..11ea9c8 100644 --- a/windows/VS2010/puttytel/puttytel.vcxproj +++ b/windows/VS2010/puttytel/puttytel.vcxproj @@ -155,14 +155,15 @@ + - + @@ -186,6 +187,7 @@ + @@ -220,6 +222,7 @@ + diff --git a/windows/VS2010/puttytel/puttytel.vcxproj.filters b/windows/VS2010/puttytel/puttytel.vcxproj.filters index 3cbf267..7c0d2c5 100644 --- a/windows/VS2010/puttytel/puttytel.vcxproj.filters +++ b/windows/VS2010/puttytel/puttytel.vcxproj.filters @@ -48,6 +48,8 @@ Source Files Source Files + + Source Files Source Files @@ -56,14 +58,14 @@ Source Files Source Files - - Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -110,6 +112,8 @@ Source Files Source Files + + Source Files Source Files @@ -176,6 +180,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2010/testcrypt/testcrypt.vcxproj b/windows/VS2010/testcrypt/testcrypt.vcxproj index d7090f8..a21926a 100644 --- a/windows/VS2010/testcrypt/testcrypt.vcxproj +++ b/windows/VS2010/testcrypt/testcrypt.vcxproj @@ -135,10 +135,17 @@ + + + + + + + @@ -146,15 +153,20 @@ + + + + + @@ -168,12 +180,14 @@ + + diff --git a/windows/VS2010/testcrypt/testcrypt.vcxproj.filters b/windows/VS2010/testcrypt/testcrypt.vcxproj.filters index dfa2ba1..937a8bc 100644 --- a/windows/VS2010/testcrypt/testcrypt.vcxproj.filters +++ b/windows/VS2010/testcrypt/testcrypt.vcxproj.filters @@ -11,14 +11,28 @@ Source Files Source Files + + Source Files Source Files + + Source Files + + Source Files + + Source Files + + Source Files Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -33,8 +47,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -43,14 +61,20 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -75,6 +99,8 @@ Header Files Header Files + + Header Files Header Files @@ -87,6 +113,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2012/pageant/pageant.vcxproj b/windows/VS2012/pageant/pageant.vcxproj index 64e168a..e8db712 100644 --- a/windows/VS2012/pageant/pageant.vcxproj +++ b/windows/VS2012/pageant/pageant.vcxproj @@ -139,15 +139,20 @@ + + + + + @@ -158,17 +163,25 @@ + + + + + + + + @@ -183,6 +196,7 @@ + @@ -194,8 +208,10 @@ + + diff --git a/windows/VS2012/pageant/pageant.vcxproj.filters b/windows/VS2012/pageant/pageant.vcxproj.filters index cf8e1ad..b4c90c0 100644 --- a/windows/VS2012/pageant/pageant.vcxproj.filters +++ b/windows/VS2012/pageant/pageant.vcxproj.filters @@ -16,10 +16,16 @@ Source Files + + Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -32,8 +38,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -54,6 +64,8 @@ Source Files Source Files + + Source Files Source Files @@ -64,18 +76,32 @@ Source Files Source Files + + Source Files + + Source Files Source Files + + Source Files Source Files Source Files + + Source Files + + Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -102,6 +128,8 @@ Header Files Header Files + + Header Files Header Files @@ -124,10 +152,14 @@ Header Files Header Files + + Header Files Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2012/plink/plink.vcxproj b/windows/VS2012/plink/plink.vcxproj index 4c384ff..462e4c6 100644 --- a/windows/VS2012/plink/plink.vcxproj +++ b/windows/VS2012/plink/plink.vcxproj @@ -142,8 +142,10 @@ + + @@ -182,7 +184,9 @@ + + @@ -203,10 +207,13 @@ + + + @@ -215,6 +222,7 @@ + @@ -232,6 +240,7 @@ + @@ -242,6 +251,7 @@ + diff --git a/windows/VS2012/plink/plink.vcxproj.filters b/windows/VS2012/plink/plink.vcxproj.filters index 53ef921..6fb770c 100644 --- a/windows/VS2012/plink/plink.vcxproj.filters +++ b/windows/VS2012/plink/plink.vcxproj.filters @@ -21,10 +21,14 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -101,8 +105,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -143,14 +151,20 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -167,6 +181,8 @@ Source Files Source Files + + Source Files Source Files @@ -201,6 +217,8 @@ Source Files Source Files + + Source Files Source Files @@ -219,6 +237,8 @@ Header Files + + Header Files Header Files diff --git a/windows/VS2012/pscp/pscp.vcxproj b/windows/VS2012/pscp/pscp.vcxproj index 22de869..f3ecc86 100644 --- a/windows/VS2012/pscp/pscp.vcxproj +++ b/windows/VS2012/pscp/pscp.vcxproj @@ -142,8 +142,10 @@ + + @@ -181,7 +183,9 @@ + + @@ -202,7 +206,9 @@ + + @@ -213,6 +219,7 @@ + @@ -229,6 +236,7 @@ + @@ -238,6 +246,7 @@ + diff --git a/windows/VS2012/pscp/pscp.vcxproj.filters b/windows/VS2012/pscp/pscp.vcxproj.filters index fd97dcc..c14db17 100644 --- a/windows/VS2012/pscp/pscp.vcxproj.filters +++ b/windows/VS2012/pscp/pscp.vcxproj.filters @@ -21,10 +21,14 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -99,8 +103,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -141,8 +149,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -163,6 +175,8 @@ Source Files Source Files + + Source Files Source Files @@ -195,6 +209,8 @@ Source Files Source Files + + Source Files Source Files @@ -211,6 +227,8 @@ Header Files + + Header Files Header Files diff --git a/windows/VS2012/psftp/psftp.vcxproj b/windows/VS2012/psftp/psftp.vcxproj index 24d1099..6217b33 100644 --- a/windows/VS2012/psftp/psftp.vcxproj +++ b/windows/VS2012/psftp/psftp.vcxproj @@ -142,8 +142,10 @@ + + @@ -181,7 +183,9 @@ + + @@ -202,7 +206,9 @@ + + @@ -213,6 +219,7 @@ + @@ -229,6 +236,7 @@ + @@ -238,6 +246,7 @@ + diff --git a/windows/VS2012/psftp/psftp.vcxproj.filters b/windows/VS2012/psftp/psftp.vcxproj.filters index f90dbe8..0de5965 100644 --- a/windows/VS2012/psftp/psftp.vcxproj.filters +++ b/windows/VS2012/psftp/psftp.vcxproj.filters @@ -21,10 +21,14 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -99,8 +103,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -141,8 +149,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -163,6 +175,8 @@ Source Files Source Files + + Source Files Source Files @@ -195,6 +209,8 @@ Source Files Source Files + + Source Files Source Files @@ -211,6 +227,8 @@ Header Files + + Header Files Header Files diff --git a/windows/VS2012/putty.sln b/windows/VS2012/putty.sln index 3205af1..aafd7a9 100644 --- a/windows/VS2012/putty.sln +++ b/windows/VS2012/putty.sln @@ -8,6 +8,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pscp", "pscp\pscp.vcxproj", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psftp", "psftp\psftp.vcxproj", "{3caa8123-13fe-450d-bfe7-fcda524d6830}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psocks", "psocks\psocks.vcxproj", "{0ce7217e-7277-408e-add3-4e2f6072486e}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "putty", "putty\putty.vcxproj", "{8f44624a-d188-4188-b9aa-5f99f20723e1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "puttygen", "puttygen\puttygen.vcxproj", "{617b1bef-aa10-42b4-8f69-4c183bc1ec0f}" @@ -22,38 +24,42 @@ Global Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.ActiveCfg = Debug|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.Build.0 = Debug|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.ActiveCfg = Release|Win32 - {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.Build.0 = Release|Win32 + {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.ActiveCfg = Debug|Win32 + {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.Build.0 = Debug|Win32 + {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.ActiveCfg = Release|Win32 + {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.Build.0 = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.ActiveCfg = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.Build.0 = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.ActiveCfg = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.Build.0 = Release|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.ActiveCfg = Debug|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.Build.0 = Debug|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.ActiveCfg = Release|Win32 - {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.Build.0 = Release|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.ActiveCfg = Debug|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.Build.0 = Debug|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.ActiveCfg = Release|Win32 + {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.Build.0 = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.ActiveCfg = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.Build.0 = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.ActiveCfg = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.Build.0 = Release|Win32 - {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.ActiveCfg = Debug|Win32 - {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.Build.0 = Debug|Win32 - {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.ActiveCfg = Release|Win32 - {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.Build.0 = Release|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.ActiveCfg = Debug|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.Build.0 = Debug|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.ActiveCfg = Release|Win32 - {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.Build.0 = Release|Win32 - {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.ActiveCfg = Debug|Win32 - {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.Build.0 = Debug|Win32 - {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.ActiveCfg = Release|Win32 - {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.Build.0 = Release|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.ActiveCfg = Debug|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.Build.0 = Debug|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.ActiveCfg = Release|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.Build.0 = Release|Win32 + {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.ActiveCfg = Debug|Win32 + {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.Build.0 = Debug|Win32 + {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.ActiveCfg = Release|Win32 + {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.Build.0 = Release|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.ActiveCfg = Debug|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.Build.0 = Debug|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.ActiveCfg = Release|Win32 + {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.Build.0 = Release|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.ActiveCfg = Debug|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.Build.0 = Debug|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.ActiveCfg = Release|Win32 + {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.Build.0 = Release|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.ActiveCfg = Debug|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.Build.0 = Debug|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.ActiveCfg = Release|Win32 + {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/windows/VS2012/putty/putty.vcxproj b/windows/VS2012/putty/putty.vcxproj index 170b577..3fe1255 100644 --- a/windows/VS2012/putty/putty.vcxproj +++ b/windows/VS2012/putty/putty.vcxproj @@ -166,7 +166,6 @@ - @@ -186,7 +185,9 @@ + + @@ -207,10 +208,13 @@ + + + @@ -241,6 +245,7 @@ + @@ -293,6 +298,7 @@ + diff --git a/windows/VS2012/putty/putty.vcxproj.filters b/windows/VS2012/putty/putty.vcxproj.filters index 8bdd448..1bda6c9 100644 --- a/windows/VS2012/putty/putty.vcxproj.filters +++ b/windows/VS2012/putty/putty.vcxproj.filters @@ -70,8 +70,6 @@ Source Files Source Files - - Source Files Source Files @@ -110,8 +108,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -152,14 +154,20 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -220,6 +228,8 @@ Source Files Source Files + + Source Files Source Files @@ -322,6 +332,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2012/puttygen/puttygen.vcxproj b/windows/VS2012/puttygen/puttygen.vcxproj index 68c96d2..ac37a7d 100644 --- a/windows/VS2012/puttygen/puttygen.vcxproj +++ b/windows/VS2012/puttygen/puttygen.vcxproj @@ -142,12 +142,19 @@ + + + + + + + @@ -165,6 +172,7 @@ + @@ -193,12 +201,14 @@ + + @@ -206,6 +216,7 @@ + diff --git a/windows/VS2012/puttygen/puttygen.vcxproj.filters b/windows/VS2012/puttygen/puttygen.vcxproj.filters index cb260be..5b6d168 100644 --- a/windows/VS2012/puttygen/puttygen.vcxproj.filters +++ b/windows/VS2012/puttygen/puttygen.vcxproj.filters @@ -21,18 +21,32 @@ Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files + + Source Files + + Source Files + + Source Files Source Files + + Source Files Source Files Source Files + + Source Files Source Files @@ -67,6 +81,8 @@ Source Files Source Files + + Source Files Source Files @@ -121,6 +137,8 @@ Header Files Header Files + + Header Files Header Files @@ -133,6 +151,8 @@ Header Files Header Files + + Header Files Header Files @@ -147,6 +167,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2012/puttytel/puttytel.vcxproj b/windows/VS2012/puttytel/puttytel.vcxproj index ce0d295..c8d1c1a 100644 --- a/windows/VS2012/puttytel/puttytel.vcxproj +++ b/windows/VS2012/puttytel/puttytel.vcxproj @@ -155,14 +155,15 @@ + - + @@ -186,6 +187,7 @@ + @@ -220,6 +222,7 @@ + diff --git a/windows/VS2012/puttytel/puttytel.vcxproj.filters b/windows/VS2012/puttytel/puttytel.vcxproj.filters index 3cbf267..7c0d2c5 100644 --- a/windows/VS2012/puttytel/puttytel.vcxproj.filters +++ b/windows/VS2012/puttytel/puttytel.vcxproj.filters @@ -48,6 +48,8 @@ Source Files Source Files + + Source Files Source Files @@ -56,14 +58,14 @@ Source Files Source Files - - Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -110,6 +112,8 @@ Source Files Source Files + + Source Files Source Files @@ -176,6 +180,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/VS2012/testcrypt/testcrypt.vcxproj b/windows/VS2012/testcrypt/testcrypt.vcxproj index d05a1da..3979ac3 100644 --- a/windows/VS2012/testcrypt/testcrypt.vcxproj +++ b/windows/VS2012/testcrypt/testcrypt.vcxproj @@ -135,10 +135,17 @@ + + + + + + + @@ -146,15 +153,20 @@ + + + + + @@ -168,12 +180,14 @@ + + diff --git a/windows/VS2012/testcrypt/testcrypt.vcxproj.filters b/windows/VS2012/testcrypt/testcrypt.vcxproj.filters index dfa2ba1..937a8bc 100644 --- a/windows/VS2012/testcrypt/testcrypt.vcxproj.filters +++ b/windows/VS2012/testcrypt/testcrypt.vcxproj.filters @@ -11,14 +11,28 @@ Source Files Source Files + + Source Files Source Files + + Source Files + + Source Files + + Source Files + + Source Files Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -33,8 +47,12 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files @@ -43,14 +61,20 @@ Source Files Source Files + + Source Files Source Files + + Source Files Source Files Source Files Source Files + + Source Files Source Files @@ -75,6 +99,8 @@ Header Files Header Files + + Header Files Header Files @@ -87,6 +113,8 @@ Header Files Header Files + + Header Files Header Files diff --git a/windows/cdecode.h b/windows/cdecode.h deleted file mode 100644 index d0d7f48..0000000 --- a/windows/cdecode.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -cdecode.h - c header for a base64 decoding algorithm - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#ifndef BASE64_CDECODE_H -#define BASE64_CDECODE_H - -typedef enum -{ - step_a, step_b, step_c, step_d -} base64_decodestep; - -typedef struct -{ - base64_decodestep step; - char plainchar; -} base64_decodestate; - -void base64_init_decodestate(base64_decodestate* state_in); - -int base64_decode_value(char value_in); - -int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in); - -#endif /* BASE64_CDECODE_H */ diff --git a/windows/cencode.h b/windows/cencode.h deleted file mode 100644 index c1e3464..0000000 --- a/windows/cencode.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -cencode.h - c header for a base64 encoding algorithm - -This is part of the libb64 project, and has been placed in the public domain. -For details, see http://sourceforge.net/projects/libb64 -*/ - -#ifndef BASE64_CENCODE_H -#define BASE64_CENCODE_H - -typedef enum -{ - step_A, step_B, step_C -} base64_encodestep; - -typedef struct -{ - base64_encodestep step; - char result; - int stepcount; -} base64_encodestate; - -void base64_init_encodestate(base64_encodestate* state_in); - -char base64_encode_value(char value_in); - -int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in); - -int base64_encode_blockend(char* code_out, base64_encodestate* state_in); - -#endif /* BASE64_CENCODE_H */ diff --git a/windows/make.sh b/windows/make.sh index 26a8608..5a537e2 100755 --- a/windows/make.sh +++ b/windows/make.sh @@ -1,4 +1,9 @@ +# +# install compiler first: # sudo apt install mingw-w64 -#this will build x64 +# +# this will build x64 version: #make TOOLPATH=x86_64-w64-mingw32- -f Makefile.mgw putty.exe -make TOOLPATH=i686-w64-mingw32- -f Makefile.mgw putty.exe +# +# this will build x32 version: +make -j$(nproc --all) TOOLPATH=i686-w64-mingw32- -f Makefile.mgw putty.exe diff --git a/windows/make_install_images.sh b/windows/make_install_images.sh new file mode 100755 index 0000000..49041e2 --- /dev/null +++ b/windows/make_install_images.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +# Script to make the bitmap files that go into the PuTTY MSI installer. + +set -e + +# For convenience, allow this script to be run from the Windows +# subdirectory as well as the top level of the source tree. +if test -f installer.wxs -a ! -f putty.h -a -f ../putty.h; then + cd .. +fi + +convert -size 164x312 'gradient:blue-white' -distort SRT -90 -swirl 180 \ + \( icons/putty-48.png -geometry +28+24 \) -composite \ + \( icons/pscp-48.png -geometry +88+96 \) -composite \ + \( icons/puttygen-48.png -geometry +28+168 \) -composite \ + \( icons/pageant-48.png -geometry +88+240 \) -composite \ + windows/msidialog.bmp + +convert -size 493x58 canvas:white \ + \( icons/putty-48.png -geometry +440+5 \) -composite \ + windows/msibanner.bmp diff --git a/windows/msiplatform.py b/windows/msifixup.py similarity index 51% rename from windows/msiplatform.py rename to windows/msifixup.py index eea4272..1160641 100755 --- a/windows/msiplatform.py +++ b/windows/msifixup.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import argparse import os @@ -16,30 +16,43 @@ def run(command, verbose): sys.stdout.write("".join( "> {}\n".format(line) for line in out.splitlines())) -def set_platform(msi, platform, verbose): - run(["msidump", "-t", msi], verbose) +def make_changes(msi, args): + run(["msidump", "-t", msi], args.verbose) + build_cmd = ["msibuild", msi] - summary_stream = "_SummaryInformation.idt" + def change_table(filename): + with open(filename) as fh: + lines = [line.rstrip("\r\n").split("\t") + for line in iter(fh.readline, "")] - with open(summary_stream) as fh: - lines = [line.rstrip("\r\n").split("\t") - for line in iter(fh.readline, "")] + for line in lines[3:]: + yield line - for line in lines[3:]: - if line[0] == "7": - line[1] = ";".join([platform] + line[1].split(";", 1)[1:]) + with open(filename, "w") as fh: + for line in lines: + fh.write("\t".join(line) + "\r\n") - with open(summary_stream, "w") as fh: - for line in lines: - fh.write("\t".join(line) + "\r\n") + build_cmd.extend(["-i", filename]) - run(["msibuild", msi, "-i", summary_stream], verbose) + if args.platform is not None: + for line in change_table("_SummaryInformation.idt"): + if line[0] == "7": + line[1] = ";".join([args.platform] + line[1].split(";", 1)[1:]) + + if args.dialog_bmp_width is not None: + for line in change_table("Control.idt"): + if line[9] == "WixUI_Bmp_Dialog": + line[5] = args.dialog_bmp_width + + run(build_cmd, args.verbose) def main(): parser = argparse.ArgumentParser( description='Change the platform field of an MSI installer package.') parser.add_argument("msi", help="MSI installer file.") - parser.add_argument("platform", help="New value for the platform field.") + parser.add_argument("--platform", help="Change the platform field.") + parser.add_argument("--dialog-bmp-width", help="Change the width field" + " in all uses of WixUI_Bmp_Dialog.") parser.add_argument("-v", "--verbose", action="store_true", help="Log what this script is doing.") parser.add_argument("-k", "--keep", action="store_true", @@ -51,7 +64,7 @@ def main(): try: tempdir = tempfile.mkdtemp(dir=msidir) os.chdir(tempdir) - set_platform(msi, args.platform, args.verbose) + make_changes(msi, args) finally: if args.keep: sys.stdout.write( diff --git a/windows/pageant-rc.h b/windows/pageant-rc.h new file mode 100755 index 0000000..3133a9c --- /dev/null +++ b/windows/pageant-rc.h @@ -0,0 +1,33 @@ +/* + * Constant definitions for the Pageant resource file. + */ + +#define IDI_MAINICON 200 +#define IDI_TRAYICON 201 + +#define IDD_KEYLIST 211 +#define IDD_LOAD_PASSPHRASE 210 +#define IDD_ONDEMAND_PASSPHRASE 212 +#define IDD_ABOUT 213 +#define IDD_LICENCE 214 + +#define IDC_PASSPHRASE_STATIC1 100 +#define IDC_PASSPHRASE_FINGERPRINT 101 +#define IDC_PASSPHRASE_STATIC2 102 +#define IDC_PASSPHRASE_STATIC3 103 +#define IDC_PASSPHRASE_EDITBOX 104 + +#define IDC_KEYLIST_LISTBOX 100 +#define IDC_KEYLIST_ADDKEY 101 +#define IDC_KEYLIST_ADDKEY_ENC 110 +#define IDC_KEYLIST_REENCRYPT 106 +#define IDC_KEYLIST_REMOVE 102 +#define IDC_KEYLIST_HELP 103 +#define IDC_KEYLIST_FPTYPE_STATIC 104 +#define IDC_KEYLIST_FPTYPE 105 + +#define IDC_ABOUT_LICENCE 101 +#define IDC_ABOUT_WEBSITE 102 +#define IDC_ABOUT_TEXTBOX 1000 + +#define IDC_LICENCE_TEXTBOX 1000 diff --git a/windows/pageant.rc b/windows/pageant.rc index 2ffd395..a4a1519 100644 --- a/windows/pageant.rc +++ b/windows/pageant.rc @@ -7,57 +7,85 @@ #define APPNAME "Pageant" #define APPDESC "PuTTY SSH authentication agent" +#include "pageant-rc.h" + #include "winhelp.rc2" -200 ICON "pageant.ico" -201 ICON "pageants.ico" +IDI_MAINICON ICON "pageant.ico" +IDI_TRAYICON ICON "pageants.ico" -210 DIALOG DISCARDABLE 0, 0, 140, 60 +IDD_LOAD_PASSPHRASE DIALOG DISCARDABLE 0, 0, 140, 60 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Pageant: Enter Passphrase" +CAPTION "Pageant: Loading Encrypted Key" FONT 8, "MS Shell Dlg" BEGIN - CTEXT "Enter passphrase for key", 100, 10, 6, 120, 8 - CTEXT "", 101, 10, 16, 120, 8 - EDITTEXT 102, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL + CTEXT "Enter passphrase to load key", IDC_PASSPHRASE_STATIC1, 10, 6, 120, 8 + CTEXT "", IDC_PASSPHRASE_FINGERPRINT, 10, 16, 120, 8 + EDITTEXT IDC_PASSPHRASE_EDITBOX, 10, 26, 120, 12, + ES_PASSWORD | ES_AUTOHSCROLL DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14 PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14 END -211 DIALOG DISCARDABLE 0, 0, 330, 200 +IDD_ONDEMAND_PASSPHRASE DIALOG DISCARDABLE 0, 0, 250, 78 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Pageant: Decrypting Stored Key" +FONT 8, "MS Shell Dlg" +BEGIN + CTEXT "A client of Pageant wants to use the following encrypted key:", + IDC_PASSPHRASE_STATIC1, 10, 6, 230, 8 + CTEXT "", IDC_PASSPHRASE_FINGERPRINT, 10, 16, 230, 8 + CTEXT "If you intended this, click in this box to make sure it has", + IDC_PASSPHRASE_STATIC2, 10, 26, 230, 8 + CTEXT "input focus, then enter the passphrase to decrypt the key.", + IDC_PASSPHRASE_STATIC3, 10, 34, 230, 8 + EDITTEXT IDC_PASSPHRASE_EDITBOX, 10, 44, 230, 12, + ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "O&K", IDOK, 45, 60, 40, 14 + PUSHBUTTON "&Cancel", IDCANCEL, 105, 60, 40, 14 + PUSHBUTTON "&Help", IDHELP, 165, 60, 50, 14 +END + +IDD_KEYLIST DIALOG DISCARDABLE 0, 0, 450, 236 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant Key List" FONT 8, "MS Shell Dlg" BEGIN - LISTBOX 100, 10, 10, 310, 155, + LISTBOX 100, 10, 10, 420, 155, LBS_EXTENDEDSEL | LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "&Add Key", 101, 75, 162, 60, 14 - PUSHBUTTON "&Remove Key", 102, 195, 162, 60, 14 - PUSHBUTTON "&Help", 103, 10, 182, 50, 14 - DEFPUSHBUTTON "&Close", IDOK, 270, 182, 50, 14 + PUSHBUTTON "&Add Key", IDC_KEYLIST_ADDKEY, 10, 187, 60, 14 + PUSHBUTTON "Add Key (&encrypted)", IDC_KEYLIST_ADDKEY_ENC, 75, 187, 80, 14 + PUSHBUTTON "Re-e&ncrypt", IDC_KEYLIST_REENCRYPT, 315, 187, 60, 14 + PUSHBUTTON "&Remove", IDC_KEYLIST_REMOVE, 380, 187, 60, 14 + PUSHBUTTON "&Help", IDC_KEYLIST_HELP, 10, 212, 50, 14 + DEFPUSHBUTTON "&Close", IDOK, 390, 212, 50, 14 + LTEXT "&Fingerprint type:", IDC_KEYLIST_FPTYPE_STATIC, 10, 172, 60, 8 + COMBOBOX IDC_KEYLIST_FPTYPE, 70, 170, 60, 12, CBS_DROPDOWNLIST END /* Accelerators used: cl */ -213 DIALOG DISCARDABLE 140, 40, 270, 136 +IDD_ABOUT DIALOG DISCARDABLE 140, 40, 270, 136 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Pageant" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 216, 118, 48, 14 - PUSHBUTTON "View &Licence", 101, 6, 118, 70, 14 - PUSHBUTTON "Visit &Web Site", 102, 140, 118, 70, 14 - EDITTEXT 1000, 10, 6, 250, 110, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE + PUSHBUTTON "View &Licence", IDC_ABOUT_LICENCE, 6, 118, 70, 14 + PUSHBUTTON "Visit &Web Site", IDC_ABOUT_WEBSITE, 140, 118, 70, 14 + EDITTEXT IDC_ABOUT_TEXTBOX, 10, 6, 250, 110, + ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE END /* No accelerators used */ -214 DIALOG DISCARDABLE 50, 50, 326, 239 +IDD_LICENCE DIALOG DISCARDABLE 50, 50, 326, 239 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 148, 219, 44, 14 - EDITTEXT 1000, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE + EDITTEXT IDC_LICENCE_TEXTBOX, 10, 10, 306, 200, + ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END #include "version.rc2" diff --git a/windows/putty.exe b/windows/putty.exe deleted file mode 100755 index a75a963..0000000 Binary files a/windows/putty.exe and /dev/null differ diff --git a/windows/putty.mft b/windows/putty.mft index fdd000d..94b7be0 100644 --- a/windows/putty.mft +++ b/windows/putty.mft @@ -26,6 +26,10 @@ true + + PerMonitorV2 + diff --git a/windows/putty.rc b/windows/putty.rc index fd58517..08969ab 100644 --- a/windows/putty.rc +++ b/windows/putty.rc @@ -1,7 +1,7 @@ #include "rcstuff.h" #define APPNAME "PuTTY" -#define APPDESC "SSH, Telnet and Rlogin client" +#define APPDESC "SSH, Telnet, Rlogin, and SUPDUP client" #include "winhelp.rc2" #include "win_res.rc2" diff --git a/windows/puttygen-rc.h b/windows/puttygen-rc.h new file mode 100644 index 0000000..582648a --- /dev/null +++ b/windows/puttygen-rc.h @@ -0,0 +1,17 @@ +#define IDC_PPKVER_STATIC 100 +#define IDC_PPKVER_2 101 +#define IDC_PPKVER_3 102 +#define IDC_KDF_STATIC 103 +#define IDC_KDF_ARGON2ID 104 +#define IDC_KDF_ARGON2I 105 +#define IDC_KDF_ARGON2D 106 +#define IDC_ARGON2_MEM_STATIC 107 +#define IDC_ARGON2_MEM 108 +#define IDC_ARGON2_MEM_STATIC2 109 +#define IDC_PPK_AUTO_STATIC 110 +#define IDC_PPK_AUTO_YES 111 +#define IDC_PPK_AUTO_NO 112 +#define IDC_ARGON2_TIME_STATIC 113 +#define IDC_ARGON2_TIME 114 +#define IDC_ARGON2_PARALLEL_STATIC 115 +#define IDC_ARGON2_PARALLEL 116 diff --git a/windows/puttygen.rc b/windows/puttygen.rc index 48b6ec8..b910b6a 100644 --- a/windows/puttygen.rc +++ b/windows/puttygen.rc @@ -8,10 +8,11 @@ #define APPDESC "PuTTY SSH key generation utility" #include "winhelp.rc2" +#include "puttygen-rc.h" 200 ICON "puttygen.ico" -201 DIALOG DISCARDABLE 0, 0, 318, 270 +201 DIALOG DISCARDABLE 0, 0, 400, 270 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Key Generator" FONT 8, "MS Shell Dlg" @@ -53,6 +54,34 @@ BEGIN EDITTEXT 1000, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END +215 DIALOG DISCARDABLE 0, 0, 259, 98 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTYgen: Private Key File Parameters" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "PPK file version:", IDC_PPKVER_STATIC, 5, 6, 119, 8 + AUTORADIOBUTTON "2", IDC_PPKVER_2, 124, 5, 30, 10, WS_GROUP + AUTORADIOBUTTON "3", IDC_PPKVER_3, 154, 5, 30, 10 + LTEXT "Key derivation function:", IDC_KDF_STATIC, 5, 22, 119, 8 + AUTORADIOBUTTON "Argon2id", IDC_KDF_ARGON2ID, 124, 21, 45, 10, WS_GROUP + AUTORADIOBUTTON "Argon2i", IDC_KDF_ARGON2I, 169, 21, 45, 10, WS_GROUP + AUTORADIOBUTTON "Argon2d", IDC_KDF_ARGON2D, 214, 21, 45, 10 + LTEXT "Memory to use for passphrase hash:", IDC_ARGON2_MEM_STATIC, + 5, 36, 119, 8 + EDITTEXT IDC_ARGON2_MEM, 124, 34, 40, 12 + LTEXT "Kbyte", IDC_ARGON2_MEM_STATIC2, 174, 36, 34, 8 + LTEXT "Time to use for passphrase hash:", IDC_ARGON2_TIME_STATIC, + 5, 50, 119, 8 + EDITTEXT IDC_ARGON2_TIME, 124, 48, 40, 12 + AUTORADIOBUTTON "ms", IDC_PPK_AUTO_YES, 174, 49, 20, 10, WS_GROUP + AUTORADIOBUTTON "passes", IDC_PPK_AUTO_NO, 204, 49, 40, 10 + LTEXT "Parallelism for passphrase hash:", IDC_ARGON2_PARALLEL_STATIC, + 5, 64, 119, 8 + EDITTEXT IDC_ARGON2_PARALLEL, 124, 62, 60, 12 + DEFPUSHBUTTON "O&K", IDOK, 70, 80, 40, 14 + PUSHBUTTON "&Cancel", IDCANCEL, 134, 80, 40, 14 +END + #include "version.rc2" #ifndef NO_MANIFESTS diff --git a/windows/puttytel.mft b/windows/puttytel.mft index 81b4dda..0cc5891 100644 --- a/windows/puttytel.mft +++ b/windows/puttytel.mft @@ -26,6 +26,10 @@ true + + PerMonitorV2 + diff --git a/windows/sizetip.c b/windows/sizetip.c index cfc4c6e..46b1ec8 100644 --- a/windows/sizetip.c +++ b/windows/sizetip.c @@ -22,44 +22,43 @@ static LRESULT CALLBACK SizeTipWndProc(HWND hWnd, UINT nMsg, case WM_ERASEBKGND: return true; - case WM_PAINT: - { - HBRUSH hbr; - HGDIOBJ holdbr; - RECT cr; - int wtlen; - LPTSTR wt; - HDC hdc; + case WM_PAINT: { + HBRUSH hbr; + HGDIOBJ holdbr; + RECT cr; + int wtlen; + LPTSTR wt; + HDC hdc; - PAINTSTRUCT ps; - hdc = BeginPaint(hWnd, &ps); + PAINTSTRUCT ps; + hdc = BeginPaint(hWnd, &ps); - SelectObject(hdc, tip_font); - SelectObject(hdc, GetStockObject(BLACK_PEN)); + SelectObject(hdc, tip_font); + SelectObject(hdc, GetStockObject(BLACK_PEN)); - hbr = CreateSolidBrush(tip_bg); - holdbr = SelectObject(hdc, hbr); + hbr = CreateSolidBrush(tip_bg); + holdbr = SelectObject(hdc, hbr); - GetClientRect(hWnd, &cr); - Rectangle(hdc, cr.left, cr.top, cr.right, cr.bottom); + GetClientRect(hWnd, &cr); + Rectangle(hdc, cr.left, cr.top, cr.right, cr.bottom); - wtlen = GetWindowTextLength(hWnd); - wt = (LPTSTR) snewn(wtlen + 1, TCHAR); - GetWindowText(hWnd, wt, wtlen + 1); + wtlen = GetWindowTextLength(hWnd); + wt = (LPTSTR) snewn(wtlen + 1, TCHAR); + GetWindowText(hWnd, wt, wtlen + 1); - SetTextColor(hdc, tip_text); - SetBkColor(hdc, tip_bg); + SetTextColor(hdc, tip_text); + SetBkColor(hdc, tip_bg); - TextOut(hdc, cr.left + 3, cr.top + 3, wt, wtlen); + TextOut(hdc, cr.left + 3, cr.top + 3, wt, wtlen); - sfree(wt); + sfree(wt); - SelectObject(hdc, holdbr); - DeleteObject(hbr); + SelectObject(hdc, holdbr); + DeleteObject(hbr); - EndPaint(hWnd, &ps); - } + EndPaint(hWnd, &ps); return 0; + } case WM_NCHITTEST: return HTTRANSPARENT; @@ -69,22 +68,21 @@ static LRESULT CALLBACK SizeTipWndProc(HWND hWnd, UINT nMsg, tip_font = NULL; break; - case WM_SETTEXT: - { - LPCTSTR str = (LPCTSTR) lParam; - SIZE sz; - HDC hdc = CreateCompatibleDC(NULL); + case WM_SETTEXT: { + LPCTSTR str = (LPCTSTR) lParam; + SIZE sz; + HDC hdc = CreateCompatibleDC(NULL); - SelectObject(hdc, tip_font); - GetTextExtentPoint32(hdc, str, _tcslen(str), &sz); + SelectObject(hdc, tip_font); + GetTextExtentPoint32(hdc, str, _tcslen(str), &sz); - SetWindowPos(hWnd, NULL, 0, 0, sz.cx + 6, sz.cy + 6, - SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); - InvalidateRect(hWnd, NULL, false); + SetWindowPos(hWnd, NULL, 0, 0, sz.cx + 6, sz.cy + 6, + SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); + InvalidateRect(hWnd, NULL, false); - DeleteDC(hdc); - } + DeleteDC(hdc); break; + } } return DefWindowProc(hWnd, nMsg, wParam, lParam); diff --git a/windows/win_res.h b/windows/win_res.h index 737c3a7..d34f685 100644 --- a/windows/win_res.h +++ b/windows/win_res.h @@ -13,6 +13,9 @@ #define IDD_ABOUTBOX 111 #define IDD_RECONF 112 #define IDD_LICENCEBOX 113 +#define IDD_HK_ABSENT 114 +#define IDD_HK_WRONG 115 +#define IDD_HK_MOREINFO 116 #define IDN_LIST 1001 #define IDN_COPY 1002 @@ -29,6 +32,17 @@ #define IDC_HELPBTN 1005 #define IDC_ABOUT 1006 +#define IDC_HK_ICON 98 +#define IDC_HK_TITLE 99 +#define IDC_HK_ACCEPT 1001 +#define IDC_HK_ONCE 1000 +#define IDC_HK_FINGERPRINT 1002 +#define IDC_HK_MOREINFO 1003 + +#define IDC_HKI_SHA256 1000 +#define IDC_HKI_MD5 1001 +#define IDC_HKI_PUBKEY 1002 + #define ID_CUSTOM_CHMFILE 2000 #define TYPE_CUSTOM_CHMFILE 2000 diff --git a/windows/win_res.rc2 b/windows/win_res.rc2 index fc9f6dd..ccec312 100644 --- a/windows/win_res.rc2 +++ b/windows/win_res.rc2 @@ -62,4 +62,76 @@ BEGIN EDITTEXT IDA_TEXT, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END +/* No accelerators used */ +IDD_HK_ABSENT DIALOG DISCARDABLE 50, 50, 340, 148 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Security Alert" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "The server's host key is not cached in the registry. You have no", 100, 40, 20, 300, 8 + LTEXT "guarantee that the server is the computer you think it is.", 101, 40, 28, 300, 8 + LTEXT "The server's {KEYTYPE} key fingerprint is:", 102, 40, 40, 300, 8 + LTEXT "If you trust this host, press ""Accept"" to add the key to {APPNAME}'s", 103, 40, 60, 300, 8 + LTEXT "cache and carry on connecting.", 104, 40, 68, 300, 8 + LTEXT "If you want to carry on connecting just once, without adding the key", 105, 40, 80, 300, 8 + LTEXT "to the cache, press ""Connect Once"".", 106, 40, 88, 300, 8 + LTEXT "If you do not trust this host, press ""Cancel"" to abandon the connection.", 107, 40, 100, 300, 8 + + ICON "", IDC_HK_ICON, 10, 18, 0, 0 + + PUSHBUTTON "Cancel", IDCANCEL, 288, 128, 40, 14 + PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 128, 40, 14 + PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 128, 64, 14 + PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 128, 64, 14 + PUSHBUTTON "Help", IDHELP, 12, 128, 40, 14 + + EDITTEXT IDC_HK_FINGERPRINT, 40, 48, 300, 12, ES_READONLY | ES_LEFT, 0 +END + +/* No accelerators used */ +IDD_HK_WRONG DIALOG DISCARDABLE 50, 50, 340, 188 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY Security Alert" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "WARNING - POTENTIAL SECURITY BREACH!", IDC_HK_TITLE, 40, 20, 300, 12 + + LTEXT "The server's host key does not match the one {APPNAME} has cached in", 100, 40, 36, 300, 8 + LTEXT "the registry. This means that either the server administrator has", 101, 40, 44, 300, 8 + LTEXT "changed the host key, or you have actually connected to another", 102, 40, 52, 300, 8 + LTEXT "computer pretending to be the server.", 103, 40, 60, 300, 8 + LTEXT "The new {KEYTYPE} key fingerprint is:", 104, 40, 72, 300, 8 + LTEXT "If you were expecting this change and trust the new key, press", 105, 40, 92, 300, 8 + LTEXT """Accept"" to update {APPNAME}'s cache and continue connecting.", 106, 40, 100, 300, 8 + LTEXT "If you want to carry on connecting but without updating the cache,", 107, 40, 112, 300, 8 + LTEXT "press ""Connect Once"".", 108, 40, 120, 300, 8 + LTEXT "If you want to abandon the connection completely, press ""Cancel"".", 109, 40, 132, 300, 8 + LTEXT "Pressing ""Cancel"" is the ONLY guaranteed safe choice.", 110, 40, 140, 300, 8 + + ICON "", IDC_HK_ICON, 10, 16, 0, 0 + + PUSHBUTTON "Cancel", IDCANCEL, 288, 168, 40, 14 + PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 168, 40, 14 + PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 168, 64, 14 + PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 168, 64, 14 + PUSHBUTTON "Help", IDHELP, 12, 168, 40, 14 + + EDITTEXT IDC_HK_FINGERPRINT, 40, 80, 300, 12, ES_READONLY | ES_LEFT, 0 +END + +/* Accelerators used: clw */ +IDD_HK_MOREINFO DIALOG DISCARDABLE 140, 40, 400, 156 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "PuTTY: information about the server's host key" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "SHA256 fingerprint:", 100, 12, 12, 80, 8 + EDITTEXT IDC_HKI_SHA256, 100, 10, 288, 12, ES_READONLY + LTEXT "MD5 fingerprint:", 101, 12, 28, 80, 8 + EDITTEXT IDC_HKI_MD5, 100, 26, 288, 12, ES_READONLY + LTEXT "Full public key:", 102, 12, 44, 376, 8 + EDITTEXT IDC_HKI_PUBKEY, 12, 54, 376, 64, ES_READONLY | ES_MULTILINE | ES_LEFT | ES_AUTOVSCROLL, WS_EX_STATICEDGE + DEFPUSHBUTTON "&Close", IDOK, 176, 130, 48, 14 +END + #include "version.rc2" diff --git a/windows/wincapi.c b/windows/wincapi.c index 5ff7cdf..de78988 100644 --- a/windows/wincapi.c +++ b/windows/wincapi.c @@ -6,9 +6,13 @@ #if !defined NO_SECURITY -#define WINCAPI_GLOBAL +#include "putty.h" +#include "ssh.h" + #include "wincapi.h" +DEF_WINDOWS_FUNCTION(CryptProtectMemory); + bool got_crypt(void) { static bool attempted = false; @@ -19,16 +23,71 @@ bool got_crypt(void) attempted = true; crypt = load_system32_dll("crypt32.dll"); successful = crypt && -#ifdef COVERITY - /* The build toolchain I use with Coverity doesn't know - * about this function, so can't type-check it */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(crypt, CryptProtectMemory) -#else - GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory) -#endif - ; + GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory); } return successful; } +char *capi_obfuscate_string(const char *realname) +{ + char *cryptdata; + int cryptlen; + unsigned char digest[32]; + char retbuf[65]; + int i; + + cryptlen = strlen(realname) + 1; + cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1; + cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE; + cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE; + + cryptdata = snewn(cryptlen, char); + memset(cryptdata, 0, cryptlen); + strcpy(cryptdata, realname); + + /* + * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to + * use the same key in all processes with this user id, meaning + * that the next PuTTY process calling this function with the same + * input will get the same data. + * + * (Contrast with CryptProtectData, which invents a new session + * key every time since its API permits returning more data than + * was input, so calling _that_ and hashing the output would not + * be stable.) + * + * We don't worry too much if this doesn't work for some reason. + * Omitting this step still has _some_ privacy value (in that + * another user can test-hash things to confirm guesses as to + * where you might be connecting to, but cannot invert SHA-256 in + * the absence of any plausible guess). So we don't abort if we + * can't call CryptProtectMemory at all, or if it fails. + */ + if (got_crypt()) + p_CryptProtectMemory(cryptdata, cryptlen, + CRYPTPROTECTMEMORY_CROSS_PROCESS); + + /* + * We don't want to give away the length of the hostname either, + * so having got it back out of CryptProtectMemory we now hash it. + */ + { + ssh_hash *h = ssh_hash_new(&ssh_sha256); + put_string(h, cryptdata, cryptlen); + ssh_hash_final(h, digest); + } + + sfree(cryptdata); + + /* + * Finally, make printable. + */ + for (i = 0; i < 32; i++) { + sprintf(retbuf + 2*i, "%02x", digest[i]); + /* the last of those will also write the trailing NUL */ + } + + return dupstr(retbuf); +} + #endif /* !defined NO_SECURITY */ diff --git a/windows/wincapi.h b/windows/wincapi.h index a4958d2..732412e 100644 --- a/windows/wincapi.h +++ b/windows/wincapi.h @@ -1,18 +1,31 @@ /* - * wincapi.h: Windows Crypto API functions defined in wincrypt.c - * that use the crypt32 library. Also centralises the machinery - * for dynamically loading that library. + * wincapi.h: Windows Crypto API functions defined in wincapi.c that + * use the crypt32 library. Also centralises the machinery for + * dynamically loading that library, and our own functions using that + * in turn. */ #if !defined NO_SECURITY -#ifndef WINCAPI_GLOBAL -#define WINCAPI_GLOBAL extern -#endif - -DECL_WINDOWS_FUNCTION(WINCAPI_GLOBAL, BOOL, CryptProtectMemory, - (LPVOID,DWORD,DWORD)); +DECL_WINDOWS_FUNCTION(extern, BOOL, CryptProtectMemory, (LPVOID,DWORD,DWORD)); bool got_crypt(void); +/* + * Function to obfuscate an input string into something usable as a + * pathname for a Windows named pipe. Uses CryptProtectMemory to make + * the obfuscation depend on a key Windows stores for the owning user, + * and then hashes the string as well to make it have a manageable + * length and be composed of filename-legal characters. + * + * Rationale: Windows's named pipes all live in the same namespace, so + * one user can see what pipes another user has open. This is an + * undesirable privacy leak: in particular, if we used unobfuscated + * names for the connection-sharing pipe names, it would permit one + * user to know what username@host another user is SSHing to. + * + * The returned string is dynamically allocated. + */ +char *capi_obfuscate_string(const char *realname); + #endif diff --git a/windows/wincfg.c b/windows/wincfg.c index 7186778..fab3240 100644 --- a/windows/wincfg.c +++ b/windows/wincfg.c @@ -43,6 +43,8 @@ static void variable_pitch_handler(union control *ctrl, dlgparam *dlg, void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, bool midsession, int protocol) { + const struct BackendVtable *backvt; + bool resize_forbidden = false; struct controlset *s; union control *c; char *str; @@ -313,15 +315,21 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, /* * Resize-by-changing-font is a Windows insanity. */ - s = ctrl_getset(b, "Window", "size", "Set the size of the window"); - ctrl_radiobuttons(s, "When window is resized:", 'z', 1, - HELPCTX(window_resize), - conf_radiobutton_handler, - I(CONF_resize_action), - "Change the number of rows and columns", I(RESIZE_TERM), - "Change the size of the font", I(RESIZE_FONT), - "Change font size only when maximised", I(RESIZE_EITHER), - "Forbid resizing completely", I(RESIZE_DISABLED), NULL); + + backvt = backend_vt_from_proto(protocol); + if (backvt) + resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN); + if (!midsession || !resize_forbidden) { + s = ctrl_getset(b, "Window", "size", "Set the size of the window"); + ctrl_radiobuttons(s, "When window is resized:", 'z', 1, + HELPCTX(window_resize), + conf_radiobutton_handler, + I(CONF_resize_action), + "Change the number of rows and columns", I(RESIZE_TERM), + "Change the size of the font", I(RESIZE_FONT), + "Change font size only when maximised", I(RESIZE_EITHER), + "Forbid resizing completely", I(RESIZE_DISABLED), NULL); + } /* * Most of the Window/Behaviour stuff is there to mimic Windows @@ -383,12 +391,6 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, } } - /* - * Serial back end is available on Windows. - */ - if (!midsession || (protocol == PROT_SERIAL)) - ser_setup_config_box(b, midsession, 0x1F, 0x0F); - /* * $XAUTHORITY is not reliable on Windows, so we provide a * means to override it. diff --git a/windows/wincliloop.c b/windows/wincliloop.c new file mode 100644 index 0000000..26a4d3a --- /dev/null +++ b/windows/wincliloop.c @@ -0,0 +1,136 @@ +#include "putty.h" + +void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) +{ + SOCKET *sklist = NULL; + size_t skcount = 0, sksize = 0; + unsigned long now, next, then; + now = GETTICKCOUNT(); + + while (true) { + int nhandles; + HANDLE *handles; + DWORD n; + DWORD ticks; + + const HANDLE *extra_handles = NULL; + size_t n_extra_handles = 0; + if (!pre(ctx, &extra_handles, &n_extra_handles)) + break; + + if (toplevel_callback_pending()) { + ticks = 0; + next = now; + } else if (run_timers(now, &next)) { + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; + } else { + ticks = INFINITE; + /* no need to initialise next here because we can never + * get WAIT_TIMEOUT */ + } + + handles = handle_get_events(&nhandles); + size_t winselcli_index = -(size_t)1; + size_t extra_base = nhandles; + if (winselcli_event != INVALID_HANDLE_VALUE) { + winselcli_index = extra_base++; + handles = sresize(handles, extra_base, HANDLE); + handles[winselcli_index] = winselcli_event; + } + size_t total_handles = extra_base + n_extra_handles; + handles = sresize(handles, total_handles, HANDLE); + for (size_t i = 0; i < n_extra_handles; i++) + handles[extra_base + i] = extra_handles[i]; + + n = WaitForMultipleObjects(total_handles, handles, false, ticks); + + size_t extra_handle_index = n_extra_handles; + + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { + handle_got_event(handles[n - WAIT_OBJECT_0]); + } else if (winselcli_event != INVALID_HANDLE_VALUE && + n == WAIT_OBJECT_0 + winselcli_index) { + WSANETWORKEVENTS things; + SOCKET socket; + int i, socketstate; + + /* + * We must not call select_result() for any socket + * until we have finished enumerating within the tree. + * This is because select_result() may close the socket + * and modify the tree. + */ + /* Count the active sockets. */ + i = 0; + for (socket = first_socket(&socketstate); + socket != INVALID_SOCKET; + socket = next_socket(&socketstate)) i++; + + /* Expand the buffer if necessary. */ + sgrowarray(sklist, sksize, i); + + /* Retrieve the sockets into sklist. */ + skcount = 0; + for (socket = first_socket(&socketstate); + socket != INVALID_SOCKET; + socket = next_socket(&socketstate)) { + sklist[skcount++] = socket; + } + + /* Now we're done enumerating; go through the list. */ + for (i = 0; i < skcount; i++) { + WPARAM wp; + socket = sklist[i]; + wp = (WPARAM) socket; + if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { + static const struct { int bit, mask; } eventtypes[] = { + {FD_CONNECT_BIT, FD_CONNECT}, + {FD_READ_BIT, FD_READ}, + {FD_CLOSE_BIT, FD_CLOSE}, + {FD_OOB_BIT, FD_OOB}, + {FD_WRITE_BIT, FD_WRITE}, + {FD_ACCEPT_BIT, FD_ACCEPT}, + }; + int e; + + noise_ultralight(NOISE_SOURCE_IOID, socket); + + for (e = 0; e < lenof(eventtypes); e++) + if (things.lNetworkEvents & eventtypes[e].mask) { + LPARAM lp; + int err = things.iErrorCode[eventtypes[e].bit]; + lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); + select_result(wp, lp); + } + } + } + } else if (n >= WAIT_OBJECT_0 + extra_base && + n < WAIT_OBJECT_0 + extra_base + n_extra_handles) { + extra_handle_index = n - (WAIT_OBJECT_0 + extra_base); + } + + run_toplevel_callbacks(); + + if (n == WAIT_TIMEOUT) { + now = next; + } else { + now = GETTICKCOUNT(); + } + + sfree(handles); + + if (!post(ctx, extra_handle_index)) + break; + } + + sfree(sklist); +} + +bool cliloop_null_pre(void *vctx, const HANDLE **eh, size_t *neh) +{ return true; } +bool cliloop_null_post(void *vctx, size_t ehi) { return true; } diff --git a/windows/wincons.c b/windows/wincons.c index 81df8ce..414167b 100644 --- a/windows/wincons.c +++ b/windows/wincons.c @@ -5,17 +5,12 @@ #include #include -#include #include "putty.h" #include "storage.h" #include "ssh.h" +#include "console.h" -bool console_batch_mode = false; - -/* - * Clean up and exit. - */ void cleanup_exit(int code) { /* @@ -28,9 +23,6 @@ void cleanup_exit(int code) exit(code); } -/* - * Various error message and/or fatal exit functions. - */ void console_print_error_msg(const char *prefix, const char *msg) { fputs(prefix, stderr); @@ -40,108 +32,15 @@ void console_print_error_msg(const char *prefix, const char *msg) fflush(stderr); } -void console_print_error_msg_fmt_v( - const char *prefix, const char *fmt, va_list ap) -{ - char *msg = dupvprintf(fmt, ap); - console_print_error_msg(prefix, msg); - sfree(msg); -} - -void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v(prefix, fmt, ap); - va_end(ap); -} - -void modalfatalbox(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap); - va_end(ap); - cleanup_exit(1); -} - -void nonfatal(const char *fmt, ...) -{ - va_list ap; - va_start(ap, fmt); - console_print_error_msg_fmt_v("ERROR", fmt, ap); - va_end(ap); -} - -void console_connection_fatal(Seat *seat, const char *msg) -{ - console_print_error_msg("FATAL ERROR", msg); - cleanup_exit(1); -} - -void timer_change_notify(unsigned long next) -{ -} - int console_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { int ret; HANDLE hin; DWORD savemode, i; - - static const char absentmsg_batch[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's %s key fingerprint is:\n" - "%s\n" - "Connection abandoned.\n"; - static const char absentmsg[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's %s key fingerprint is:\n" - "%s\n" - "If you trust this host, enter \"y\" to add the key to\n" - "PuTTY's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, enter \"n\".\n" - "If you do not trust this host, press Return to abandon the\n" - "connection.\n" - "Store key in cache? (y/n) "; - - static const char wrongmsg_batch[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new %s key fingerprint is:\n" - "%s\n" - "Connection abandoned.\n"; - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "The server's host key does not match the one PuTTY has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new %s key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "enter \"y\" to update PuTTY's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, enter \"n\".\n" - "If you want to abandon the connection completely, press\n" - "Return to cancel. Pressing Return is the ONLY guaranteed\n" - "safe choice.\n" - "Update cached key? (y/n, Return cancels connection) "; - - static const char abandoned[] = "Connection abandoned.\n"; + const char *common_fmt, *intro, *prompt; char line[32]; @@ -154,37 +53,62 @@ int console_verify_ssh_host_key( return 1; if (ret == 2) { /* key was different */ - if (console_batch_mode) { - fprintf(stderr, wrongmsg_batch, keytype, fingerprint); - return 0; - } - fprintf(stderr, wrongmsg, keytype, fingerprint); - fflush(stderr); + common_fmt = hk_wrongmsg_common_fmt; + intro = hk_wrongmsg_interactive_intro; + prompt = hk_wrongmsg_interactive_prompt; + } else { /* key was absent */ + common_fmt = hk_absentmsg_common_fmt; + intro = hk_absentmsg_interactive_intro; + prompt = hk_absentmsg_interactive_prompt; } - if (ret == 1) { /* key was absent */ - if (console_batch_mode) { - fprintf(stderr, absentmsg_batch, keytype, fingerprint); - return 0; - } - fprintf(stderr, absentmsg, keytype, fingerprint); - fflush(stderr); + + FingerprintType fptype_default = + ssh2_pick_default_fingerprint(fingerprints); + + fprintf(stderr, common_fmt, keytype, fingerprints[fptype_default]); + if (console_batch_mode) { + fputs(console_abandoned_msg, stderr); + return 0; } - line[0] = '\0'; /* fail safe if ReadFile returns no data */ + fputs(intro, stderr); + fflush(stderr); - hin = GetStdHandle(STD_INPUT_HANDLE); - GetConsoleMode(hin, &savemode); - SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | - ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); - ReadFile(hin, line, sizeof(line) - 1, &i, NULL); - SetConsoleMode(hin, savemode); + while (true) { + fputs(prompt, stderr); + fflush(stderr); + + line[0] = '\0'; /* fail safe if ReadFile returns no data */ + + hin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hin, &savemode); + SetConsoleMode(hin, (savemode | ENABLE_ECHO_INPUT | + ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT)); + ReadFile(hin, line, sizeof(line) - 1, &i, NULL); + SetConsoleMode(hin, savemode); + + if (line[0] == 'i' || line[0] == 'I') { + fprintf(stderr, "Full public key:\n%s\n", keydisp); + if (fingerprints[SSH_FPTYPE_SHA256]) + fprintf(stderr, "SHA256 key fingerprint:\n%s\n", + fingerprints[SSH_FPTYPE_SHA256]); + if (fingerprints[SSH_FPTYPE_MD5]) + fprintf(stderr, "MD5 key fingerprint:\n%s\n", + fingerprints[SSH_FPTYPE_MD5]); + } else { + break; + } + } - if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n') { + /* In case of misplaced reflexes from another program, also recognise 'q' + * as 'abandon connection rather than trust this key' */ + if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && + line[0] != 'q' && line[0] != 'Q') { if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); return 1; } else { - fprintf(stderr, abandoned); + fputs(console_abandoned_msg, stderr); return 0; } } @@ -196,24 +120,16 @@ int console_confirm_weak_crypto_primitive( HANDLE hin; DWORD savemode, i; - static const char msg[] = - "The first %s supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Continue with connection? (y/n) "; - static const char msg_batch[] = - "The first %s supported by the server is\n" - "%s, which is below the configured warning threshold.\n" - "Connection abandoned.\n"; - static const char abandoned[] = "Connection abandoned.\n"; - char line[32]; + fprintf(stderr, weakcrypto_msg_common_fmt, algtype, algname); + if (console_batch_mode) { - fprintf(stderr, msg_batch, algtype, algname); + fputs(console_abandoned_msg, stderr); return 0; } - fprintf(stderr, msg, algtype, algname); + fputs(console_continue_prompt, stderr); fflush(stderr); hin = GetStdHandle(STD_INPUT_HANDLE); @@ -226,7 +142,7 @@ int console_confirm_weak_crypto_primitive( if (line[0] == 'y' || line[0] == 'Y') { return 1; } else { - fprintf(stderr, abandoned); + fputs(console_abandoned_msg, stderr); return 0; } } @@ -238,30 +154,16 @@ int console_confirm_weak_cached_hostkey( HANDLE hin; DWORD savemode, i; - static const char msg[] = - "The first host key type we have stored for this server\n" - "is %s, which is below the configured warning threshold.\n" - "The server also provides the following types of host key\n" - "above the threshold, which we do not have stored:\n" - "%s\n" - "Continue with connection? (y/n) "; - static const char msg_batch[] = - "The first host key type we have stored for this server\n" - "is %s, which is below the configured warning threshold.\n" - "The server also provides the following types of host key\n" - "above the threshold, which we do not have stored:\n" - "%s\n" - "Connection abandoned.\n"; - static const char abandoned[] = "Connection abandoned.\n"; - char line[32]; + fprintf(stderr, weakhk_msg_common_fmt, algname, betteralgs); + if (console_batch_mode) { - fprintf(stderr, msg_batch, algname, betteralgs); + fputs(console_abandoned_msg, stderr); return 0; } - fprintf(stderr, msg, algname, betteralgs); + fputs(console_continue_prompt, stderr); fflush(stderr); hin = GetStdHandle(STD_INPUT_HANDLE); @@ -274,7 +176,7 @@ int console_confirm_weak_cached_hostkey( if (line[0] == 'y' || line[0] == 'Y') { return 1; } else { - fprintf(stderr, abandoned); + fputs(console_abandoned_msg, stderr); return 0; } } @@ -311,9 +213,8 @@ bool console_set_trust_status(Seat *seat, bool trusted) * Ask whether to wipe a session log file before writing to it. * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). */ -static int console_askappend(LogPolicy *lp, Filename *filename, - void (*callback)(void *ctx, int result), - void *ctx) +int console_askappend(LogPolicy *lp, Filename *filename, + void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; DWORD savemode, i; @@ -400,7 +301,7 @@ void pgp_fingerprints(void) " " PGP_PREV_MASTER_KEY_FP "\n", stdout); } -static void console_logging_error(LogPolicy *lp, const char *string) +void console_logging_error(LogPolicy *lp, const char *string) { /* Ordinary Event Log entries are displayed in the same way as * logging errors, but only in verbose mode */ @@ -408,11 +309,11 @@ static void console_logging_error(LogPolicy *lp, const char *string) fflush(stderr); } -static void console_eventlog(LogPolicy *lp, const char *string) +void console_eventlog(LogPolicy *lp, const char *string) { /* Ordinary Event Log entries are displayed in the same way as * logging errors, but only in verbose mode */ - if (flags & FLAG_VERBOSE) + if (lp_verbose(lp)) console_logging_error(lp, string); } @@ -490,7 +391,6 @@ int console_get_userpass_input(prompts_t *p) for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { DWORD savemode, newmode; - size_t len; prompt_t *pr = p->prompts[curr_prompt]; GetConsoleMode(hin, &savemode); @@ -503,22 +403,37 @@ int console_get_userpass_input(prompts_t *p) console_write(hout, ptrlen_from_asciz(pr->prompt)); - len = 0; + bool failed = false; while (1) { - DWORD ret = 0; + /* + * Amount of data to try to read from the console in one + * go. This isn't completely arbitrary: a user reported + * that trying to read more than 31366 bytes at a time + * would fail with ERROR_NOT_ENOUGH_MEMORY on Windows 7, + * and Ruby's Win32 support module has evidence of a + * similar workaround: + * + * https://github.com/ruby/ruby/blob/0aa5195262d4193d3accf3e6b9bad236238b816b/win32/win32.c#L6842 + * + * To keep things simple, I stick with a nice round power + * of 2 rather than trying to go to the very limit of that + * bug. (We're typically reading user passphrases and the + * like here, so even this much is overkill really.) + */ + DWORD toread = 16384; + + size_t prev_result_len = pr->result->len; + void *ptr = strbuf_append(pr->result, toread); - prompt_ensure_result_size(pr, len * 5 / 4 + 512); - - if (!ReadFile(hin, pr->result + len, pr->resultsize - len - 1, - &ret, NULL) || ret == 0) { - len = (size_t)-1; + DWORD ret = 0; + if (!ReadFile(hin, ptr, toread, &ret, NULL) || ret == 0) { + failed = true; break; } - len += ret; - if (pr->result[len - 1] == '\n') { - len--; - if (pr->result[len - 1] == '\r') - len--; + + strbuf_shrink_to(pr->result, prev_result_len + ret); + if (strbuf_chomp(pr->result, '\n')) { + strbuf_chomp(pr->result, '\r'); break; } } @@ -528,19 +443,10 @@ int console_get_userpass_input(prompts_t *p) if (!pr->echo) console_write(hout, PTRLEN_LITERAL("\r\n")); - if (len == (size_t)-1) { + if (failed) { return 0; /* failure due to read error */ } - - pr->result[len] = '\0'; } return 1; /* success */ } - -static const LogPolicyVtable default_logpolicy_vt = { - console_eventlog, - console_askappend, - console_logging_error, -}; -LogPolicy default_logpolicy[1] = {{ &default_logpolicy_vt }}; diff --git a/windows/winctrls.c b/windows/winctrls.c index 3389ce0..59129ea 100644 --- a/windows/winctrls.c +++ b/windows/winctrls.c @@ -222,13 +222,15 @@ static void radioline_common(struct ctlpos *cp, char *text, int id, int i; int j; + r.left = GAPBETWEEN; + r.top = cp->ypos; if (text) { - r.left = GAPBETWEEN; - r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); + } else { + r.right = r.bottom = 0; } group = WS_GROUP; @@ -925,20 +927,18 @@ void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines, wid = xpos - left; switch (i) { - case 1: + case 1: { /* The drag list box. */ r.left = left; r.right = wid; r.top = cp->ypos; r.bottom = listheight; - { - HWND ctl; - ctl = doctl(cp, r, "LISTBOX", - WS_CHILD | WS_VISIBLE | WS_TABSTOP | - WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS, - WS_EX_CLIENTEDGE, - "", listid); - p_MakeDragList(ctl); - } + HWND ctl = doctl(cp, r, "LISTBOX", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | + WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS, + WS_EX_CLIENTEDGE, + "", listid); + p_MakeDragList(ctl); break; + } case 2: /* The "Up" and "Down" buttons. */ @@ -1325,6 +1325,28 @@ struct winctrl *winctrl_findbyindex(struct winctrls *wc, int index) return index234(wc->byid, index); } +static void move_windows(HWND hwnd, int base_id, int num_ids, LONG dy) +{ + if (!dy) + return; + for (int i = 0; i < num_ids; i++) { + HWND win = GetDlgItem(hwnd, base_id + i); + + RECT rect; + if (!GetWindowRect(win, &rect)) + continue; + + POINT p; + p.x = rect.left; + p.y = rect.top + dy; + if (!ScreenToClient(hwnd, &p)) + continue; + + SetWindowPos(win, NULL, p.x, p.y, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); + } +} + void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, struct ctlpos *cp, struct controlset *s, int *id) { @@ -1340,7 +1362,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, char shortcuts[MAX_SHORTCUTS_PER_CTRL]; int nshortcuts; char *escaped; - int i, actual_base_id, base_id, num_ids; + int i, actual_base_id, base_id, num_ids, align_id_relative; void *data; base_id = *id; @@ -1349,7 +1371,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, if (s->boxname && *s->boxname) { struct winctrl *c = snew(struct winctrl); c->ctrl = NULL; - c->base_id = base_id; + c->base_id = c->align_id = base_id; c->num_ids = 1; c->data = NULL; memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts)); @@ -1362,7 +1384,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, if (!s->boxname && s->boxtitle) { struct winctrl *c = snew(struct winctrl); c->ctrl = NULL; - c->base_id = base_id; + c->base_id = c->align_id = base_id; c->num_ids = 1; c->data = dupstr(s->boxtitle); memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts)); @@ -1491,24 +1513,28 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, /* Almost all controls start at base_id. */ actual_base_id = base_id; + /* For vertical alignment purposes, the most relevant control + * in a group is usually the last one. But that can be + * overridden occasionally. */ + align_id_relative = -1; + /* * Now we're ready to actually create the control, by * switching on its type. */ switch (ctrl->generic.type) { - case CTRL_TEXT: - { - char *wrapped, *escaped; - int lines; - num_ids = 1; - wrapped = staticwrap(&pos, cp->hwnd, - ctrl->generic.label, &lines); - escaped = shortcut_escape(wrapped, NO_SHORTCUT); - statictext(&pos, escaped, lines, base_id); - sfree(escaped); - sfree(wrapped); - } + case CTRL_TEXT: { + char *wrapped, *escaped; + int lines; + num_ids = 1; + wrapped = staticwrap(&pos, cp->hwnd, + ctrl->generic.label, &lines); + escaped = shortcut_escape(wrapped, NO_SHORTCUT); + statictext(&pos, escaped, lines, base_id); + sfree(escaped); + sfree(wrapped); break; + } case CTRL_EDITBOX: num_ids = 2; /* static, edit */ escaped = shortcut_escape(ctrl->editbox.label, @@ -1533,42 +1559,41 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, } sfree(escaped); break; - case CTRL_RADIO: + case CTRL_RADIO: { num_ids = ctrl->radio.nbuttons + 1; /* label as well */ - { - struct radio *buttons; - int i; + struct radio *buttons; + int i; - escaped = shortcut_escape(ctrl->radio.label, - ctrl->radio.shortcut); - shortcuts[nshortcuts++] = ctrl->radio.shortcut; - - buttons = snewn(ctrl->radio.nbuttons, struct radio); - - for (i = 0; i < ctrl->radio.nbuttons; i++) { - buttons[i].text = - shortcut_escape(ctrl->radio.buttons[i], - (char)(ctrl->radio.shortcuts ? - ctrl->radio.shortcuts[i] : - NO_SHORTCUT)); - buttons[i].id = base_id + 1 + i; - if (ctrl->radio.shortcuts) { - assert(nshortcuts < MAX_SHORTCUTS_PER_CTRL); - shortcuts[nshortcuts++] = ctrl->radio.shortcuts[i]; - } - } + escaped = shortcut_escape(ctrl->radio.label, + ctrl->radio.shortcut); + shortcuts[nshortcuts++] = ctrl->radio.shortcut; + + buttons = snewn(ctrl->radio.nbuttons, struct radio); + + for (i = 0; i < ctrl->radio.nbuttons; i++) { + buttons[i].text = + shortcut_escape(ctrl->radio.buttons[i], + (char)(ctrl->radio.shortcuts ? + ctrl->radio.shortcuts[i] : + NO_SHORTCUT)); + buttons[i].id = base_id + 1 + i; + if (ctrl->radio.shortcuts) { + assert(nshortcuts < MAX_SHORTCUTS_PER_CTRL); + shortcuts[nshortcuts++] = ctrl->radio.shortcuts[i]; + } + } - radioline_common(&pos, escaped, base_id, - ctrl->radio.ncolumns, - buttons, ctrl->radio.nbuttons); + radioline_common(&pos, escaped, base_id, + ctrl->radio.ncolumns, + buttons, ctrl->radio.nbuttons); - for (i = 0; i < ctrl->radio.nbuttons; i++) { - sfree(buttons[i].text); - } - sfree(buttons); - sfree(escaped); + for (i = 0; i < ctrl->radio.nbuttons; i++) { + sfree(buttons[i].text); } + sfree(buttons); + sfree(escaped); break; + } case CTRL_CHECKBOX: num_ids = 1; escaped = shortcut_escape(ctrl->checkbox.label, @@ -1661,6 +1686,10 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, unreachable("bad control type in winctrl_layout"); } + /* Translate the original align_id_relative of -1 into n-1 */ + if (align_id_relative < 0) + align_id_relative += num_ids; + /* * Create a `struct winctrl' for this control, and advance * the dialog ID counter, if it's actually been created @@ -1671,6 +1700,7 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, c->ctrl = ctrl; c->base_id = actual_base_id; + c->align_id = c->base_id + align_id_relative; c->num_ids = num_ids; c->data = data; memcpy(c->shortcuts, shortcuts, sizeof(shortcuts)); @@ -1678,6 +1708,31 @@ void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, winctrl_add_shortcuts(dp, c); if (actual_base_id == base_id) base_id += num_ids; + + if (ctrl->generic.align_next_to) { + /* + * Implement align_next_to by looking at the y extents + * of the two controls now that both are created, and + * moving one or the other downwards so that they're + * centred on a common horizontal line. + */ + struct winctrl *c2 = winctrl_findbyctrl( + wc, ctrl->generic.align_next_to); + HWND win1 = GetDlgItem(pos.hwnd, c->align_id); + HWND win2 = GetDlgItem(pos.hwnd, c2->align_id); + RECT rect1, rect2; + if (win1 && win2 && + GetWindowRect(win1, &rect1) && + GetWindowRect(win2, &rect2)) { + LONG top = (rect1.top < rect2.top ? rect1.top : rect2.top); + LONG bottom = (rect1.bottom > rect2.bottom ? + rect1.bottom : rect2.bottom); + move_windows(pos.hwnd, c->base_id, c->num_ids, + (top + bottom - rect1.top - rect1.bottom)/2); + move_windows(pos.hwnd, c2->base_id, c2->num_ids, + (top + bottom - rect2.top - rect2.bottom)/2); + } + } } else { sfree(data); } diff --git a/windows/windlg.c b/windows/windlg.c index faa5693..9c5fdb7 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -12,6 +12,7 @@ #include "putty.h" #include "ssh.h" #include "win_res.h" +#include "winseat.h" #include "storage.h" #include "dialog.h" #include "licence.h" @@ -78,23 +79,24 @@ static char *getevent(int i) return NULL; } +static HWND logbox; +HWND event_log_window(void) { return logbox; } + static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { int i; switch (msg) { - case WM_INITDIALOG: - { - char *str = dupprintf("%s Event Log", appname); - SetWindowText(hwnd, str); - sfree(str); - } - { - static int tabs[4] = { 78, 108 }; - SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2, - (LPARAM) tabs); - } + case WM_INITDIALOG: { + char *str = dupprintf("%s Event Log", appname); + SetWindowText(hwnd, str); + sfree(str); + + static int tabs[4] = { 78, 108 }; + SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2, + (LPARAM) tabs); + for (i = 0; i < ninitial; i++) SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING, 0, (LPARAM) events_initial[i]); @@ -102,6 +104,7 @@ static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg, SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING, 0, (LPARAM) events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX]); return 1; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: @@ -180,14 +183,13 @@ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - case WM_INITDIALOG: - { - char *str = dupprintf("%s Licence", appname); - SetWindowText(hwnd, str); - sfree(str); - SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n")); - } + case WM_INITDIALOG: { + char *str = dupprintf("%s Licence", appname); + SetWindowText(hwnd, str); + sfree(str); + SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n")); return 1; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: @@ -209,21 +211,21 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, char *str; switch (msg) { - case WM_INITDIALOG: + case WM_INITDIALOG: { str = dupprintf("About %s", appname); SetWindowText(hwnd, str); sfree(str); - { - char *buildinfo_text = buildinfo("\r\n"); - char *text = dupprintf - ("%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", - appname, ver, buildinfo_text, - "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); - sfree(buildinfo_text); - SetDlgItemText(hwnd, IDA_TEXT, text); - sfree(text); - } + char *buildinfo_text = buildinfo("\r\n"); + char *text = dupprintf + ("%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", + appname, ver, buildinfo_text, + "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); + sfree(buildinfo_text); + SetDlgItemText(hwnd, IDA_TEXT, text); + MakeDlgItemBorderless(hwnd, IDA_TEXT); + sfree(text); return 1; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: @@ -691,7 +693,7 @@ void defuse_showwindow(void) } } -bool do_config(void) +bool do_config(Conf *conf) { bool ret; @@ -721,7 +723,7 @@ bool do_config(void) return ret; } -bool do_reconfig(HWND hwnd, int protcfginfo) +bool do_reconfig(HWND hwnd, Conf *conf, int protcfginfo) { Conf *backup_conf; bool ret; @@ -776,7 +778,7 @@ static void win_gui_eventlog(LogPolicy *lp, const char *string) if (*location) sfree(*location); - *location = dupcat(timebuf, string, (const char *)NULL); + *location = dupcat(timebuf, string); if (logbox) { int count; SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING, @@ -797,10 +799,12 @@ static void win_gui_eventlog(LogPolicy *lp, const char *string) static void win_gui_logging_error(LogPolicy *lp, const char *event) { + WinGuiSeat *wgs = container_of(lp, WinGuiSeat, logpolicy); + /* Send 'can't open log file' errors to the terminal window. * (Marked as stderr, although terminal.c won't care.) */ - seat_stderr_pl(win_seat, ptrlen_from_asciz(event)); - seat_stderr_pl(win_seat, PTRLEN_LITERAL("\r\n")); + seat_stderr_pl(&wgs->seat, ptrlen_from_asciz(event)); + seat_stderr_pl(&wgs->seat, PTRLEN_LITERAL("\r\n")); } void showeventlog(HWND hwnd) @@ -818,44 +822,161 @@ void showabout(HWND hwnd) DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); } +struct hostkey_dialog_ctx { + const char *const *keywords; + const char *const *values; + FingerprintType fptype_default; + char **fingerprints; + const char *keydisp; + LPCTSTR iconid; + const char *helpctx; +}; + +static INT_PTR CALLBACK HostKeyMoreInfoProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: { + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *)lParam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); + + if (ctx->fingerprints[SSH_FPTYPE_SHA256]) + SetDlgItemText(hwnd, IDC_HKI_SHA256, + ctx->fingerprints[SSH_FPTYPE_SHA256]); + if (ctx->fingerprints[SSH_FPTYPE_MD5]) + SetDlgItemText(hwnd, IDC_HKI_MD5, + ctx->fingerprints[SSH_FPTYPE_MD5]); + + SetDlgItemText(hwnd, IDA_TEXT, ctx->keydisp); + + return 1; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 0); + return 0; + } + return 0; + case WM_CLOSE: + EndDialog(hwnd, 0); + return 0; + } + return 0; +} + +static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_INITDIALOG: { + strbuf *sb = strbuf_new(); + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *)lParam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); + for (int id = 100;; id++) { + char buf[256]; + + if (!GetDlgItemText(hwnd, id, buf, (int)lenof(buf))) + break; + + strbuf_clear(sb); + for (const char *p = buf; *p ;) { + if (*p == '{') { + for (size_t i = 0; ctx->keywords[i]; i++) { + if (strstartswith(p, ctx->keywords[i])) { + p += strlen(ctx->keywords[i]); + put_datapl(sb, ptrlen_from_asciz(ctx->values[i])); + goto matched; + } + } + } else { + put_byte(sb, *p++); + } + matched:; + } + + SetDlgItemText(hwnd, id, sb->s); + } + strbuf_free(sb); + + SetDlgItemText(hwnd, IDC_HK_FINGERPRINT, + ctx->fingerprints[ctx->fptype_default]); + MakeDlgItemBorderless(hwnd, IDC_HK_FINGERPRINT); + + HANDLE icon = LoadImage( + NULL, ctx->iconid, IMAGE_ICON, + GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), + LR_SHARED); + SendDlgItemMessage(hwnd, IDC_HK_ICON, STM_SETICON, (WPARAM)icon, 0); + + if (!has_help()) { + HWND item = GetDlgItem(hwnd, IDHELP); + if (item) + DestroyWindow(item); + } + + return 1; + } + case WM_CTLCOLORSTATIC: { + HDC hdc = (HDC)wParam; + HWND control = (HWND)lParam; + + if (GetWindowLongPtr(control, GWLP_ID) == IDC_HK_TITLE) { + SetBkMode(hdc, TRANSPARENT); + HFONT prev_font = (HFONT)SelectObject( + hdc, (HFONT)GetStockObject(SYSTEM_FONT)); + LOGFONT lf; + if (GetObject(prev_font, sizeof(lf), &lf)) { + lf.lfWeight = FW_BOLD; + lf.lfHeight = lf.lfHeight * 3 / 2; + HFONT bold_font = CreateFontIndirect(&lf); + if (bold_font) + SelectObject(hdc, bold_font); + } + return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE); + } + return 0; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_HK_ACCEPT: + case IDC_HK_ONCE: + case IDCANCEL: + EndDialog(hwnd, LOWORD(wParam)); + return 0; + case IDHELP: { + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + launch_help(hwnd, ctx->helpctx); + return 0; + } + case IDC_HK_MOREINFO: { + const struct hostkey_dialog_ctx *ctx = + (const struct hostkey_dialog_ctx *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_HK_MOREINFO), + hwnd, HostKeyMoreInfoProc, (LPARAM)ctx); + } + } + return 0; + case WM_CLOSE: + EndDialog(hwnd, IDCANCEL); + return 0; + } + return 0; +} + int win_seat_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { int ret; - static const char absentmsg[] = - "The server's host key is not cached in the registry. You\n" - "have no guarantee that the server is the computer you\n" - "think it is.\n" - "The server's %s key fingerprint is:\n" - "%s\n" - "If you trust this host, hit Yes to add the key to\n" - "%s's cache and carry on connecting.\n" - "If you want to carry on connecting just once, without\n" - "adding the key to the cache, hit No.\n" - "If you do not trust this host, hit Cancel to abandon the\n" - "connection.\n"; - - static const char wrongmsg[] = - "WARNING - POTENTIAL SECURITY BREACH!\n" - "\n" - "The server's host key does not match the one %s has\n" - "cached in the registry. This means that either the\n" - "server administrator has changed the host key, or you\n" - "have actually connected to another computer pretending\n" - "to be the server.\n" - "The new %s key fingerprint is:\n" - "%s\n" - "If you were expecting this change and trust the new key,\n" - "hit Yes to update %s's cache and continue connecting.\n" - "If you want to carry on connecting but without updating\n" - "the cache, hit No.\n" - "If you want to abandon the connection completely, hit\n" - "Cancel. Hitting Cancel is the ONLY guaranteed safe\n" "choice.\n"; - - static const char mbtitle[] = "%s Security Alert"; + WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); /* * Verify the key against the registry. @@ -864,36 +985,32 @@ int win_seat_verify_ssh_host_key( if (ret == 0) /* success - key matched OK */ return 1; - else if (ret == 2) { /* key was different */ - int mbret; - char *text = dupprintf(wrongmsg, appname, keytype, fingerprint, - appname); - char *caption = dupprintf(mbtitle, appname); - mbret = message_box(text, caption, - MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3, - HELPCTXID(errors_hostkey_changed)); - assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL); - sfree(text); - sfree(caption); - if (mbret == IDYES) { - store_host_key(host, port, keytype, keystr); - return 1; - } else if (mbret == IDNO) - return 1; - } else if (ret == 1) { /* key was absent */ - int mbret; - char *text = dupprintf(absentmsg, keytype, fingerprint, appname); - char *caption = dupprintf(mbtitle, appname); - mbret = message_box(text, caption, - MB_ICONWARNING | MB_YESNOCANCEL | MB_DEFBUTTON3, - HELPCTXID(errors_hostkey_absent)); - assert(mbret==IDYES || mbret==IDNO || mbret==IDCANCEL); - sfree(text); - sfree(caption); - if (mbret == IDYES) { + else { + static const char *const keywords[] = + { "{KEYTYPE}", "{APPNAME}", NULL }; + + const char *values[2]; + values[0] = keytype; + values[1] = appname; + + struct hostkey_dialog_ctx ctx[1]; + ctx->keywords = keywords; + ctx->values = values; + ctx->fingerprints = fingerprints; + ctx->fptype_default = ssh2_pick_default_fingerprint(fingerprints); + ctx->keydisp = keydisp; + ctx->iconid = (ret == 2 ? IDI_WARNING : IDI_QUESTION); + ctx->helpctx = (ret == 2 ? WINHELP_CTX_errors_hostkey_changed : + WINHELP_CTX_errors_hostkey_absent); + int dlgid = (ret == 2 ? IDD_HK_WRONG : IDD_HK_ABSENT); + int mbret = DialogBoxParam( + hinst, MAKEINTRESOURCE(dlgid), wgs->term_hwnd, + HostKeyDialogProc, (LPARAM)ctx); + assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL); + if (mbret == IDC_HK_ACCEPT) { store_host_key(host, port, keytype, keystr); return 1; - } else if (mbret == IDNO) + } else if (mbret == IDC_HK_ONCE) return 1; } return 0; /* abandon the connection */ @@ -995,12 +1112,12 @@ static int win_gui_askappend(LogPolicy *lp, Filename *filename, return 0; } -static const LogPolicyVtable default_logpolicy_vt = { - win_gui_eventlog, - win_gui_askappend, - win_gui_logging_error, +const LogPolicyVtable win_gui_logpolicy_vt = { + .eventlog = win_gui_eventlog, + .askappend = win_gui_askappend, + .logging_error = win_gui_logging_error, + .verbose = null_lp_verbose_yes, }; -LogPolicy default_logpolicy[1] = {{ &default_logpolicy_vt }}; /* * Warn about the obsolescent key file format. diff --git a/windows/window.c b/windows/window.c index 10f973f..3aa2a21 100644 --- a/windows/window.c +++ b/windows/window.c @@ -11,8 +11,8 @@ #include /* far2l base64 */ -#include -#include +#include <../far2l/cencode.h> +#include <../far2l/cdecode.h> #ifdef __WINE__ #define NO_MULTIMON /* winelib doesn't have this */ @@ -22,12 +22,12 @@ #define COMPILE_MULTIMON_STUBS #endif -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "terminal.h" #include "storage.h" #include "win_res.h" #include "winsecur.h" +#include "winseat.h" #include "tree234.h" #ifndef NO_MULTIMON @@ -71,8 +71,7 @@ #define WM_IGNORE_CLIP (WM_APP + 2) #define WM_FULLSCR_ON_MAX (WM_APP + 3) -#define WM_AGENT_CALLBACK (WM_APP + 4) -#define WM_GOT_CLIPDATA (WM_APP + 6) +#define WM_GOT_CLIPDATA (WM_APP + 4) /* Needed for Chinese support and apparently not always defined. */ #ifndef VK_PROCESSKEY @@ -87,19 +86,27 @@ #define WHEEL_DELTA 120 #endif +/* DPI awareness support */ +#ifndef WM_DPICHANGED +#define WM_DPICHANGED 0x02E0 +#define WM_DPICHANGED_BEFOREPARENT 0x02E2 +#define WM_DPICHANGED_AFTERPARENT 0x02E3 +#define WM_GETDPISCALEDSIZE 0x02E4 +#endif + /* VK_PACKET, used to send Unicode characters in WM_KEYDOWNs */ #ifndef VK_PACKET #define VK_PACKET 0xE7 #endif static Mouse_Button translate_button(Mouse_Button button); +static void show_mouseptr(bool show); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); -static void conftopalette(void); -static void systopalette(void); static void init_palette(void); static void init_fonts(int, int); +static void init_dpi_info(void); static void another_font(int); static void deinit_fonts(void); static void set_input_locale(HKL); @@ -150,6 +157,10 @@ static struct { enum { SYSMENU, CTXMENU }; static HMENU savedsess_menu; +static Conf *conf; +static LogContext *logctx; +static Terminal *term; + struct wm_netevent_params { /* Used to pass data to wm_netevent_callback */ WPARAM wParam; @@ -157,18 +168,11 @@ struct wm_netevent_params { }; static void conf_cache_data(void); -int cursor_type; -int vtmode; +static int cursor_type; +static int vtmode; static struct sesslist sesslist; /* for saved-session menu */ -struct agent_callback { - void (*callback)(void *, void *, int); - void *callback_ctx; - void *data; - int len; -}; - #define FONT_NORMAL 0 #define FONT_BOLD 1 #define FONT_UNDERLINE 2 @@ -194,18 +198,23 @@ static bool bold_colours; static enum { UND_LINE, UND_FONT } und_mode; -static int descent; - -#define NCFGCOLOURS 22 -#define NEXTCOLOURS 240 -#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) -static COLORREF colours[NALLCOLOURS]; -struct rgb { - int r, g, b; -} colours_rgb[NALLCOLOURS]; +static int descent, font_strikethrough_y; + +static COLORREF colours[OSC4_NCOLOURS]; static HPALETTE pal; static LPLOGPALETTE logpal; -static RGBTRIPLE defpal[NALLCOLOURS]; +bool tried_pal = false; +COLORREF colorref_modifier = 0; + +enum MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI, MDT_ANGULAR_DPI, MDT_RAW_DPI, MDT_DEFAULT }; +DECL_WINDOWS_FUNCTION(static, HRESULT, GetDpiForMonitor, (HMONITOR hmonitor, enum MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY)); +DECL_WINDOWS_FUNCTION(static, HRESULT, GetSystemMetricsForDpi, (int nIndex, UINT dpi)); +DECL_WINDOWS_FUNCTION(static, HRESULT, AdjustWindowRectExForDpi, (LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi)); + +static struct _dpi_info { + POINT cur_dpi; + RECT new_wnd_rect; +} dpi_info; static HBITMAP caretbm; @@ -216,6 +225,8 @@ static Mouse_Button lastbtn; static bool send_raw_mouse = false; static int wheel_accumulator = 0; +static bool pointer_indicates_raw_mouse = false; + static BusyStatus busy_status = BUSY_NOT; static char *window_name, *icon_name; @@ -240,6 +251,7 @@ static int wintw_char_width(TermWin *, int uc); static void wintw_free_draw_ctx(TermWin *); static void wintw_set_cursor_pos(TermWin *, int x, int y); static void wintw_set_raw_mouse_mode(TermWin *, bool enable); +static void wintw_set_raw_mouse_mode_pointer(TermWin *, bool enable); static void wintw_set_scrollbar(TermWin *, int total, int start, int page); static void wintw_bell(TermWin *, int mode); static void wintw_clip_write( @@ -251,47 +263,36 @@ static void wintw_request_resize(TermWin *, int w, int h); static void wintw_set_title(TermWin *, const char *title); static void wintw_set_icon_title(TermWin *, const char *icontitle); static void wintw_set_minimised(TermWin *, bool minimised); -static bool wintw_is_minimised(TermWin *); static void wintw_set_maximised(TermWin *, bool maximised); static void wintw_move(TermWin *, int x, int y); static void wintw_set_zorder(TermWin *, bool top); -static bool wintw_palette_get(TermWin *, int n, int *r, int *g, int *b); -static void wintw_palette_set(TermWin *, int n, int r, int g, int b); -static void wintw_palette_reset(TermWin *); -static void wintw_get_pos(TermWin *, int *x, int *y); -static void wintw_get_pixels(TermWin *, int *x, int *y); -static const char *wintw_get_title(TermWin *, bool icon); -static bool wintw_is_utf8(TermWin *); +static void wintw_palette_set(TermWin *, unsigned, unsigned, const rgb *); +static void wintw_palette_get_overrides(TermWin *, Terminal *); static const TermWinVtable windows_termwin_vt = { - wintw_setup_draw_ctx, - wintw_draw_text, - wintw_draw_cursor, - wintw_draw_trust_sigil, - wintw_char_width, - wintw_free_draw_ctx, - wintw_set_cursor_pos, - wintw_set_raw_mouse_mode, - wintw_set_scrollbar, - wintw_bell, - wintw_clip_write, - wintw_clip_request_paste, - wintw_refresh, - wintw_request_resize, - wintw_set_title, - wintw_set_icon_title, - wintw_set_minimised, - wintw_is_minimised, - wintw_set_maximised, - wintw_move, - wintw_set_zorder, - wintw_palette_get, - wintw_palette_set, - wintw_palette_reset, - wintw_get_pos, - wintw_get_pixels, - wintw_get_title, - wintw_is_utf8, + .setup_draw_ctx = wintw_setup_draw_ctx, + .draw_text = wintw_draw_text, + .draw_cursor = wintw_draw_cursor, + .draw_trust_sigil = wintw_draw_trust_sigil, + .char_width = wintw_char_width, + .free_draw_ctx = wintw_free_draw_ctx, + .set_cursor_pos = wintw_set_cursor_pos, + .set_raw_mouse_mode = wintw_set_raw_mouse_mode, + .set_raw_mouse_mode_pointer = wintw_set_raw_mouse_mode_pointer, + .set_scrollbar = wintw_set_scrollbar, + .bell = wintw_bell, + .clip_write = wintw_clip_write, + .clip_request_paste = wintw_clip_request_paste, + .refresh = wintw_refresh, + .request_resize = wintw_request_resize, + .set_title = wintw_set_title, + .set_icon_title = wintw_set_icon_title, + .set_minimised = wintw_set_minimised, + .set_maximised = wintw_set_maximised, + .move = wintw_move, + .set_zorder = wintw_set_zorder, + .palette_set = wintw_palette_set, + .palette_get_overrides = wintw_palette_get_overrides, }; static TermWin wintw[1]; @@ -307,28 +308,17 @@ static bool is_utf8(void) return ucsdata.line_codepage == CP_UTF8; } -static bool wintw_is_utf8(TermWin *tw) -{ - return is_utf8(); -} - static bool win_seat_is_utf8(Seat *seat) { return is_utf8(); } -char *win_seat_get_ttymode(Seat *seat, const char *mode) +static char *win_seat_get_ttymode(Seat *seat, const char *mode) { return term_get_ttymode(term, mode); } -bool win_seat_get_window_pixel_size(Seat *seat, int *x, int *y) -{ - win_get_pixels(wintw, x, y); - return true; -} - -StripCtrlChars *win_seat_stripctrl_new( +static StripCtrlChars *win_seat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) { return stripctrl_new_term(bs_out, false, 0, term); @@ -344,36 +334,39 @@ static void win_seat_connection_fatal(Seat *seat, const char *msg); static void win_seat_update_specials_menu(Seat *seat); static void win_seat_set_busy_status(Seat *seat, BusyStatus status); static bool win_seat_set_trust_status(Seat *seat, bool trusted); +static bool win_seat_get_cursor_position(Seat *seat, int *x, int *y); +static bool win_seat_get_window_pixel_size(Seat *seat, int *x, int *y); static const SeatVtable win_seat_vt = { - win_seat_output, - win_seat_eof, - win_seat_get_userpass_input, - win_seat_notify_remote_exit, - win_seat_connection_fatal, - win_seat_update_specials_menu, - win_seat_get_ttymode, - win_seat_set_busy_status, - win_seat_verify_ssh_host_key, - win_seat_confirm_weak_crypto_primitive, - win_seat_confirm_weak_cached_hostkey, - win_seat_is_utf8, - nullseat_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - win_seat_get_window_pixel_size, - win_seat_stripctrl_new, - win_seat_set_trust_status, + .output = win_seat_output, + .eof = win_seat_eof, + .get_userpass_input = win_seat_get_userpass_input, + .notify_remote_exit = win_seat_notify_remote_exit, + .connection_fatal = win_seat_connection_fatal, + .update_specials_menu = win_seat_update_specials_menu, + .get_ttymode = win_seat_get_ttymode, + .set_busy_status = win_seat_set_busy_status, + .verify_ssh_host_key = win_seat_verify_ssh_host_key, + .confirm_weak_crypto_primitive = win_seat_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = win_seat_confirm_weak_cached_hostkey, + .is_utf8 = win_seat_is_utf8, + .echoedit_update = nullseat_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = win_seat_get_window_pixel_size, + .stripctrl_new = win_seat_stripctrl_new, + .set_trust_status = win_seat_set_trust_status, + .verbose = nullseat_verbose_yes, + .interactive = nullseat_interactive_yes, + .get_cursor_position = win_seat_get_cursor_position, }; -static Seat win_seat_impl = { &win_seat_vt }; -Seat *const win_seat = &win_seat_impl; +static WinGuiSeat wgs = { .seat.vt = &win_seat_vt, + .logpolicy.vt = &win_gui_logpolicy_vt }; static void start_backend(void) { const struct BackendVtable *vt; - const char *error; - const char *title; - char *realhost; + char *error, *realhost; int i; /* @@ -389,8 +382,8 @@ static void start_backend(void) cleanup_exit(1); } - seat_set_trust_status(win_seat, true); - error = backend_init(vt, win_seat, &backend, logctx, conf, + seat_set_trust_status(&wgs.seat, true); + error = backend_init(vt, &wgs.seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, @@ -400,21 +393,14 @@ static void start_backend(void) char *str = dupprintf("%s Error", appname); char *msg = dupprintf("Unable to open connection to\n%s\n%s", conf_dest(conf), error); + sfree(error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); sfree(msg); exit(0); } - window_name = icon_name = NULL; - char *title_to_free = NULL; - title = conf_get_str(conf, CONF_wintitle); - if (!*title) { - title_to_free = dupprintf("%s - %s", realhost, appname); - title = title_to_free; - } + term_setup_window_titles(term, realhost); sfree(realhost); - win_set_title(wintw, title); - win_set_icon_title(wintw, title); /* * Connect the terminal to the backend for resize purposes. @@ -424,7 +410,7 @@ static void start_backend(void) /* * Set up a line discipline. */ - ldisc = ldisc_create(conf, term, backend, win_seat); + ldisc = ldisc_create(conf, term, backend, &wgs.seat); /* * Destroy the Restart Session menu item. (This will return @@ -438,8 +424,6 @@ static void start_backend(void) } session_closed = false; - - sfree(title_to_free); } static void close_session(void *ignored_context) @@ -461,7 +445,7 @@ static void close_session(void *ignored_context) backend_free(backend); backend = NULL; term_provide_backend(term, NULL); - seat_update_specials_menu(win_seat); + seat_update_specials_menu(&wgs.seat); } /* @@ -475,6 +459,13 @@ static void close_session(void *ignored_context) } } +const unsigned cmdline_tooltype = + TOOLTYPE_HOST_ARG | + TOOLTYPE_PORT_ARG | + TOOLTYPE_NO_VERBOSE_OPTION; + +HINSTANCE hinst; + int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; @@ -484,9 +475,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) dll_hijacking_protection(); hinst = inst; - hwnd = NULL; - flags = FLAG_VERBOSE | FLAG_INTERACTIVE; - cmdline_tooltype |= TOOLTYPE_HOST_ARG | TOOLTYPE_PORT_ARG; sk_init(); @@ -537,14 +525,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) char *p; bool special_launchable_argument = false; - default_protocol = be_default_protocol; + settings_set_default_protocol(be_default_protocol); /* Find the appropriate default port. */ { const struct BackendVtable *vt = - backend_vt_from_proto(default_protocol); - default_port = 0; /* illegal */ + backend_vt_from_proto(be_default_protocol); + settings_set_default_port(0); /* illegal */ if (vt) - default_port = vt->default_port; + settings_set_default_port(vt->default_port); } conf_set_int(conf, CONF_logtype, LGTYP_NONE); @@ -566,7 +554,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) (!p[2] || p[2] == '@' || p[2] == '&')) { /* &R restrict-acl prefix */ restrict_process_acl(); - restricted_acl = true; p += 2; } @@ -583,7 +570,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) i--; p[i] = '\0'; do_defaults(p + 1, conf); - if (!conf_launchable(conf) && !do_config()) { + if (!conf_launchable(conf) && !do_config(conf)) { cleanup_exit(0); } special_launchable_argument = true; @@ -606,7 +593,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) modalfatalbox("Serialised configuration data was invalid"); UnmapViewOfFile(cp); CloseHandle(filemap); - } else if (!do_config()) { + } else if (!do_config(conf)) { cleanup_exit(0); } special_launchable_argument = true; @@ -652,7 +639,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) "Are you really sure you want to continue?", appname); s2 = dupprintf("%s Warning", appname); - if (message_box(s1, s2, + if (message_box(NULL, s1, s2, MB_YESNO | MB_ICONWARNING | MB_DEFBUTTON2, HELPCTXID(option_cleanup)) == IDYES) { cleanup_all(); @@ -661,7 +648,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) sfree(s2); exit(0); } else if (!strcmp(p, "-pgpfp")) { - pgp_fingerprints(); + pgp_fingerprints_msgbox(NULL); exit(1); } else if (*p != '-') { cmdline_error("unexpected argument \"%s\"", p); @@ -678,7 +665,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * (explicitly) specified a launchable configuration. */ if (!(special_launchable_argument || cmdline_host_ok(conf))) { - if (!do_config()) + if (!do_config(conf)) cleanup_exit(0); } @@ -706,8 +693,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) conf_cache_data(); - conftopalette(); - /* * Guess some defaults for the window size. This all gets * updated later, so we don't really care too much. However, we @@ -733,19 +718,26 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { int winmode = WS_OVERLAPPEDWINDOW | WS_VSCROLL; int exwinmode = 0; + const struct BackendVtable *vt = + backend_vt_from_proto(be_default_protocol); + bool resize_forbidden = false; + if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) + resize_forbidden = true; wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); if (!conf_get_bool(conf, CONF_scrollbar)) winmode &= ~(WS_VSCROLL); - if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) + if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED || + resize_forbidden) winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); if (conf_get_bool(conf, CONF_alwaysontop)) exwinmode |= WS_EX_TOPMOST; if (conf_get_bool(conf, CONF_sunken_edge)) exwinmode |= WS_EX_CLIENTEDGE; - hwnd = CreateWindowExW(exwinmode, uappname, uappname, - winmode, CW_USEDEFAULT, CW_USEDEFAULT, - guess_width, guess_height, - NULL, NULL, inst, NULL); + wgs.term_hwnd = CreateWindowExW( + exwinmode, uappname, uappname, winmode, CW_USEDEFAULT, + CW_USEDEFAULT, guess_width, guess_height, NULL, NULL, inst, NULL); + memset(&dpi_info, 0, sizeof(struct _dpi_info)); + init_dpi_info(); sfree(uappname); } @@ -755,6 +747,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ init_fonts(0,0); + /* + * Prepare a logical palette. + */ + init_palette(); + /* * Initialise the terminal. (We have to do this _after_ * creating the window, since the terminal is the first thing @@ -764,7 +761,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) wintw->vt = &windows_termwin_vt; term = term_init(conf, &ucsdata, wintw); setup_clipboards(term, conf); - logctx = log_init(default_logpolicy, conf); + logctx = log_init(&wgs.logpolicy, conf); term_provide_logctx(term, logctx); term_size(term, conf_get_int(conf, CONF_height), conf_get_int(conf, CONF_width), @@ -775,8 +772,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ { RECT cr, wr; - GetWindowRect(hwnd, &wr); - GetClientRect(hwnd, &cr); + GetWindowRect(wgs.term_hwnd, &wr); + GetClientRect(wgs.term_hwnd, &cr); offset_width = offset_height = conf_get_int(conf, CONF_window_border); extra_width = wr.right - wr.left - cr.right + cr.left + offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top +offset_height*2; @@ -788,7 +785,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) */ guess_width = extra_width + font_width * term->cols; guess_height = extra_height + font_height * term->rows; - SetWindowPos(hwnd, NULL, 0, 0, guess_width, guess_height, + SetWindowPos(wgs.term_hwnd, NULL, 0, 0, guess_width, guess_height, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER); /* @@ -802,7 +799,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) caretbm = CreateBitmap(font_width, font_height, 1, 1, bits); sfree(bits); } - CreateCaret(hwnd, caretbm, font_width, font_height); + CreateCaret(wgs.term_hwnd, caretbm, font_width, font_height); /* * Initialise the scroll bar. @@ -816,7 +813,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) si.nMax = term->rows - 1; si.nPage = term->rows; si.nPos = 0; - SetScrollInfo(hwnd, SB_VERT, &si, false); + SetScrollInfo(wgs.term_hwnd, SB_VERT, &si, false); } /* @@ -834,7 +831,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) int j; char *str; - popup_menus[SYSMENU].menu = GetSystemMenu(hwnd, false); + popup_menus[SYSMENU].menu = GetSystemMenu(wgs.term_hwnd, false); popup_menus[CTXMENU].menu = CreatePopupMenu(); AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_COPY, "&Copy"); AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste"); @@ -871,10 +868,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } } - if (restricted_acl) { - lp_eventlog(default_logpolicy, "Running with restricted process ACL"); + if (restricted_acl()) { + lp_eventlog(&wgs.logpolicy, "Running with restricted process ACL"); } + winselgui_set_hwnd(wgs.term_hwnd); start_backend(); /* @@ -885,18 +883,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* * Finally show the window! */ - ShowWindow(hwnd, show); - SetForegroundWindow(hwnd); + ShowWindow(wgs.term_hwnd, show); + SetForegroundWindow(wgs.term_hwnd); - /* - * Set the palette up. - */ - pal = NULL; - logpal = NULL; - init_palette(); - - term_set_focus(term, GetForegroundWindow() == hwnd); - UpdateWindow(hwnd); + term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd); + UpdateWindow(wgs.term_hwnd); while (1) { HANDLE *handles; @@ -926,7 +917,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } else { timeout = INFINITE; /* The messages seem unreliable; especially if we're being tricky */ - term_set_focus(term, GetForegroundWindow() == hwnd); + term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd); } handles = handle_get_events(&nhandles); @@ -944,6 +935,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (msg.message == WM_QUIT) goto finished; /* two-level break */ + HWND logbox = event_log_window(); if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) DispatchMessageW(&msg); @@ -1027,32 +1019,6 @@ void cleanup_exit(int code) exit(code); } -/* - * Set up, or shut down, an AsyncSelect. Called from winnet.c. - */ -char *do_select(SOCKET skt, bool startup) -{ - int msg, events; - if (startup) { - msg = WM_NETEVENT; - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - msg = events = 0; - } - if (!hwnd) - return "do_select(): internal error (hwnd==NULL)"; - if (p_WSAAsyncSelect(skt, hwnd, msg, events) == SOCKET_ERROR) { - switch (p_WSAGetLastError()) { - case WSAENETDOWN: - return "Network is down"; - default: - return "WSAAsyncSelect(): unknown error"; - } - } - return NULL; -} - /* * Refresh the saved-session submenu from `sesslist'. */ @@ -1150,7 +1116,7 @@ static void update_mouse_pointer(void) static bool forced_visible = false; switch (busy_status) { case BUSY_NOT: - if (send_raw_mouse) + if (pointer_indicates_raw_mouse) curstype = IDC_ARROW; else curstype = IDC_IBEAM; @@ -1168,7 +1134,7 @@ static void update_mouse_pointer(void) } { HCURSOR cursor = LoadCursor(NULL, curstype); - SetClassLongPtr(hwnd, GCLP_HCURSOR, (LONG_PTR)cursor); + SetClassLongPtr(wgs.term_hwnd, GCLP_HCURSOR, (LONG_PTR)cursor); SetCursor(cursor); /* force redraw of cursor at current posn */ } if (force_visible != forced_visible) { @@ -1187,13 +1153,14 @@ static void win_seat_set_busy_status(Seat *seat, BusyStatus status) update_mouse_pointer(); } -/* - * set or clear the "raw mouse message" mode - */ static void wintw_set_raw_mouse_mode(TermWin *tw, bool activate) { - activate = activate && !conf_get_bool(conf, CONF_no_mouse_rep); send_raw_mouse = activate; +} + +static void wintw_set_raw_mouse_mode_pointer(TermWin *tw, bool activate) +{ + pointer_indicates_raw_mouse = activate; update_mouse_pointer(); } @@ -1203,7 +1170,8 @@ static void wintw_set_raw_mouse_mode(TermWin *tw, bool activate) static void win_seat_connection_fatal(Seat *seat, const char *msg) { char *title = dupprintf("%s Fatal Error", appname); - MessageBox(hwnd, msg, title, MB_ICONERROR | MB_OK); + show_mouseptr(true); + MessageBox(wgs.term_hwnd, msg, title, MB_ICONERROR | MB_OK); sfree(title); if (conf_get_int(conf, CONF_close_on_exit) == FORCE_ON) @@ -1225,7 +1193,7 @@ void cmdline_error(const char *fmt, ...) message = dupvprintf(fmt, ap); va_end(ap); title = dupprintf("%s Command Line Error", appname); - MessageBox(hwnd, message, title, MB_ICONERROR | MB_OK); + MessageBox(wgs.term_hwnd, message, title, MB_ICONERROR | MB_OK); sfree(message); sfree(title); exit(1); @@ -1241,122 +1209,34 @@ static void wm_netevent_callback(void *vctx) sfree(vctx); } -/* - * Copy the colour palette from the configuration data into defpal. - * This is non-trivial because the colour indices are different. - */ -static void conftopalette(void) +static inline rgb rgb_from_colorref(COLORREF cr) { - int i; - static const int ww[] = { - 256, 257, 258, 259, 260, 261, - 0, 8, 1, 9, 2, 10, 3, 11, - 4, 12, 5, 13, 6, 14, 7, 15 - }; - - for (i = 0; i < 22; i++) { - int w = ww[i]; - defpal[w].rgbtRed = conf_get_int_int(conf, CONF_colours, i*3+0); - defpal[w].rgbtGreen = conf_get_int_int(conf, CONF_colours, i*3+1); - defpal[w].rgbtBlue = conf_get_int_int(conf, CONF_colours, i*3+2); - } - for (i = 0; i < NEXTCOLOURS; i++) { - if (i < 216) { - int r = i / 36, g = (i / 6) % 6, b = i % 6; - defpal[i+16].rgbtRed = r ? r * 40 + 55 : 0; - defpal[i+16].rgbtGreen = g ? g * 40 + 55 : 0; - defpal[i+16].rgbtBlue = b ? b * 40 + 55 : 0; - } else { - int shade = i - 216; - shade = shade * 10 + 8; - defpal[i+16].rgbtRed = defpal[i+16].rgbtGreen = - defpal[i+16].rgbtBlue = shade; - } - } - - /* Override with system colours if appropriate */ - if (conf_get_bool(conf, CONF_system_colour)) - systopalette(); + rgb toret; + toret.r = GetRValue(cr); + toret.g = GetGValue(cr); + toret.b = GetBValue(cr); + return toret; } -/* - * Override bit of defpal with colours from the system. - * (NB that this takes a copy the system colours at the time this is called, - * so subsequent colour scheme changes don't take effect. To fix that we'd - * probably want to be using GetSysColorBrush() and the like.) - */ -static void systopalette(void) +static void wintw_palette_get_overrides(TermWin *tw, Terminal *term) { - int i; - static const struct { int nIndex; int norm; int bold; } or[] = - { - { COLOR_WINDOWTEXT, 256, 257 }, /* Default Foreground */ - { COLOR_WINDOW, 258, 259 }, /* Default Background */ - { COLOR_HIGHLIGHTTEXT, 260, 260 }, /* Cursor Text */ - { COLOR_HIGHLIGHT, 261, 261 }, /* Cursor Colour */ - }; - - for (i = 0; i < (sizeof(or)/sizeof(or[0])); i++) { - COLORREF colour = GetSysColor(or[i].nIndex); - defpal[or[i].norm].rgbtRed = - defpal[or[i].bold].rgbtRed = GetRValue(colour); - defpal[or[i].norm].rgbtGreen = - defpal[or[i].bold].rgbtGreen = GetGValue(colour); - defpal[or[i].norm].rgbtBlue = - defpal[or[i].bold].rgbtBlue = GetBValue(colour); - } -} + if (conf_get_bool(conf, CONF_system_colour)) { + rgb rgb; -static void internal_set_colour(int i, int r, int g, int b) -{ - assert(i >= 0); - assert(i < NALLCOLOURS); - if (pal) - colours[i] = PALETTERGB(r, g, b); - else - colours[i] = RGB(r, g, b); - colours_rgb[i].r = r; - colours_rgb[i].g = g; - colours_rgb[i].b = b; -} + rgb = rgb_from_colorref(GetSysColor(COLOR_WINDOWTEXT)); + term_palette_override(term, OSC4_COLOUR_fg, rgb); + term_palette_override(term, OSC4_COLOUR_fg_bold, rgb); -/* - * Set up the colour palette. - */ -static void init_palette(void) -{ - int i; - HDC hdc = GetDC(hwnd); - if (hdc) { - if (conf_get_bool(conf, CONF_try_palette) && - GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { - /* - * This is a genuine case where we must use smalloc - * because the snew macros can't cope. - */ - logpal = smalloc(sizeof(*logpal) - - sizeof(logpal->palPalEntry) - + NALLCOLOURS * sizeof(PALETTEENTRY)); - logpal->palVersion = 0x300; - logpal->palNumEntries = NALLCOLOURS; - for (i = 0; i < NALLCOLOURS; i++) { - logpal->palPalEntry[i].peRed = defpal[i].rgbtRed; - logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen; - logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue; - logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; - } - pal = CreatePalette(logpal); - if (pal) { - SelectPalette(hdc, pal, false); - RealizePalette(hdc); - SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); - } - } - ReleaseDC(hwnd, hdc); + rgb = rgb_from_colorref(GetSysColor(COLOR_WINDOW)); + term_palette_override(term, OSC4_COLOUR_bg, rgb); + term_palette_override(term, OSC4_COLOUR_bg_bold, rgb); + + rgb = rgb_from_colorref(GetSysColor(COLOR_HIGHLIGHTTEXT)); + term_palette_override(term, OSC4_COLOUR_cursor_fg, rgb); + + rgb = rgb_from_colorref(GetSysColor(COLOR_HIGHLIGHT)); + term_palette_override(term, OSC4_COLOUR_cursor_bg, rgb); } - for (i = 0; i < NALLCOLOURS; i++) - internal_set_colour(i, defpal[i].rgbtRed, - defpal[i].rgbtGreen, defpal[i].rgbtBlue); } /* @@ -1486,6 +1366,30 @@ static int get_font_width(HDC hdc, const TEXTMETRIC *tm) return ret; } +static void init_dpi_info(void) +{ + if (dpi_info.cur_dpi.x == 0 || dpi_info.cur_dpi.y == 0) { + if (p_GetDpiForMonitor) { + UINT dpiX, dpiY; + HMONITOR currentMonitor = MonitorFromWindow( + wgs.term_hwnd, MONITOR_DEFAULTTOPRIMARY); + if (p_GetDpiForMonitor(currentMonitor, MDT_EFFECTIVE_DPI, + &dpiX, &dpiY) == S_OK) { + dpi_info.cur_dpi.x = (int)dpiX; + dpi_info.cur_dpi.y = (int)dpiY; + } + } + + /* Fall back to system DPI */ + if (dpi_info.cur_dpi.x == 0 || dpi_info.cur_dpi.y == 0) { + HDC hdc = GetDC(wgs.term_hwnd); + dpi_info.cur_dpi.x = GetDeviceCaps(hdc, LOGPIXELSX); + dpi_info.cur_dpi.y = GetDeviceCaps(hdc, LOGPIXELSY); + ReleaseDC(wgs.term_hwnd, hdc); + } + } +} + /* * Initialise all the fonts we will need initially. There may be as many as * three or as few as one. The other (potentially) twenty-one fonts are done @@ -1508,6 +1412,7 @@ static int get_font_width(HDC hdc, const TEXTMETRIC *tm) static void init_fonts(int pick_width, int pick_height) { TEXTMETRIC tm; + OUTLINETEXTMETRIC otm; CPINFO cpinfo; FontSpec *font; int fontsize[3]; @@ -1533,7 +1438,7 @@ static void init_fonts(int pick_width, int pick_height) fw_bold = FW_BOLD; } - hdc = GetDC(hwnd); + hdc = GetDC(wgs.term_hwnd); if (pick_height) font_height = pick_height; @@ -1541,7 +1446,7 @@ static void init_fonts(int pick_width, int pick_height) font_height = font->height; if (font_height > 0) { font_height = - -MulDiv(font_height, GetDeviceCaps(hdc, LOGPIXELSY), 72); + -MulDiv(font_height, dpi_info.cur_dpi.y, 72); } } font_width = pick_width; @@ -1557,6 +1462,10 @@ static void init_fonts(int pick_width, int pick_height) SelectObject(hdc, fonts[FONT_NORMAL]); GetTextMetrics(hdc, &tm); + if (GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) + font_strikethrough_y = tm.tmAscent - otm.otmsStrikeoutPosition; + else + font_strikethrough_y = tm.tmAscent - (tm.tmAscent * 3 / 8); GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont); @@ -1667,7 +1576,7 @@ static void init_fonts(int pick_width, int pick_height) fontsize[i] = -i; } - ReleaseDC(hwnd, hdc); + ReleaseDC(wgs.term_hwnd, hdc); if (trust_icon != INVALID_HANDLE_VALUE) { DestroyIcon(trust_icon); @@ -1767,15 +1676,19 @@ static void deinit_fonts(void) static void wintw_request_resize(TermWin *tw, int w, int h) { + const struct BackendVtable *vt; int width, height; /* If the window is maximized suppress resizing attempts */ - if (IsZoomed(hwnd)) { + if (IsZoomed(wgs.term_hwnd)) { if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) return; } if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return; + vt = backend_vt_from_proto(be_default_protocol); + if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) + return; if (h == term->rows && w == term->cols) return; /* Sanity checks ... */ @@ -1809,17 +1722,36 @@ static void wintw_request_resize(TermWin *tw, int w, int h) term_size(term, h, w, conf_get_int(conf, CONF_savelines)); if (conf_get_int(conf, CONF_resize_action) != RESIZE_FONT && - !IsZoomed(hwnd)) { + !IsZoomed(wgs.term_hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; - SetWindowPos(hwnd, NULL, 0, 0, width, height, + SetWindowPos(wgs.term_hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER); } else reset_window(0); - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); +} + +static void recompute_window_offset(void) +{ + RECT cr; + GetClientRect(wgs.term_hwnd, &cr); + + int win_width = cr.right - cr.left; + int win_height = cr.bottom - cr.top; + + int new_offset_width = (win_width-font_width*term->cols)/2; + int new_offset_height = (win_height-font_height*term->rows)/2; + + if (offset_width != new_offset_width || + offset_height != new_offset_height) { + offset_width = new_offset_width; + offset_height = new_offset_height; + InvalidateRect(wgs.term_hwnd, NULL, true); + } } static void reset_window(int reinit) { @@ -1838,8 +1770,8 @@ static void reset_window(int reinit) { #endif /* Current window sizes ... */ - GetWindowRect(hwnd, &wr); - GetClientRect(hwnd, &cr); + GetWindowRect(wgs.term_hwnd, &wr); + GetClientRect(wgs.term_hwnd, &cr); win_width = cr.right - cr.left; win_height = cr.bottom - cr.top; @@ -1864,18 +1796,14 @@ static void reset_window(int reinit) { return; /* Is the window out of position ? */ - if ( !reinit && - (offset_width != (win_width-font_width*term->cols)/2 || - offset_height != (win_height-font_height*term->rows)/2) ){ - offset_width = (win_width-font_width*term->cols)/2; - offset_height = (win_height-font_height*term->rows)/2; - InvalidateRect(hwnd, NULL, true); + if (!reinit) { + recompute_window_offset(); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> Reposition terminal\n"); #endif } - if (IsZoomed(hwnd)) { + if (IsZoomed(wgs.term_hwnd)) { /* We're fullscreen, this means we must not change the size of * the window so it's the font size or the terminal itself. */ @@ -1890,7 +1818,7 @@ static void reset_window(int reinit) { init_fonts(win_width/term->cols, win_height/term->rows); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> Z font resize to (%d, %d)\n", font_width, font_height); @@ -1906,7 +1834,7 @@ static void reset_window(int reinit) { conf_get_int(conf, CONF_savelines)); offset_width = (win_width-font_width*term->cols)/2; offset_height = (win_height-font_height*term->rows)/2; - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> Zoomed term_size\n"); #endif @@ -1915,6 +1843,35 @@ static void reset_window(int reinit) { return; } + /* Resize window after DPI change */ + if (reinit == 3 && p_GetSystemMetricsForDpi && p_AdjustWindowRectExForDpi) { + RECT rect; + rect.left = rect.top = 0; + rect.right = (font_width * term->cols); + if (conf_get_bool(conf, CONF_scrollbar)) + rect.right += p_GetSystemMetricsForDpi(SM_CXVSCROLL, + dpi_info.cur_dpi.x); + rect.bottom = (font_height * term->rows); + p_AdjustWindowRectExForDpi( + &rect, GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE), + FALSE, GetWindowLongPtr(wgs.term_hwnd, GWL_EXSTYLE), + dpi_info.cur_dpi.x); + rect.right += (window_border * 2); + rect.bottom += (window_border * 2); + OffsetRect(&dpi_info.new_wnd_rect, + ((dpi_info.new_wnd_rect.right - dpi_info.new_wnd_rect.left) - + (rect.right - rect.left)) / 2, + ((dpi_info.new_wnd_rect.bottom - dpi_info.new_wnd_rect.top) - + (rect.bottom - rect.top)) / 2); + SetWindowPos(wgs.term_hwnd, NULL, + dpi_info.new_wnd_rect.left, dpi_info.new_wnd_rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOZORDER); + + InvalidateRect(wgs.term_hwnd, NULL, true); + return; + } + /* Hmm, a force re-init means we should ignore the current window * so we resize to the default font size. */ @@ -1934,13 +1891,13 @@ static void reset_window(int reinit) { * allowed window size, we will then be back in here and resize * the font or terminal to fit. */ - SetWindowPos(hwnd, NULL, 0, 0, + SetWindowPos(wgs.term_hwnd, NULL, 0, 0, font_width*term->cols + extra_width, font_height*term->rows + extra_height, SWP_NOMOVE | SWP_NOZORDER); } - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); return; } @@ -1994,12 +1951,12 @@ static void reset_window(int reinit) { } } - SetWindowPos(hwnd, NULL, 0, 0, + SetWindowPos(wgs.term_hwnd, NULL, 0, 0, font_width*term->cols + extra_width, font_height*term->rows + extra_height, SWP_NOMOVE | SWP_NOZORDER); - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> window resize to (%d,%d)\n", font_width*term->cols + extra_width, @@ -2023,7 +1980,7 @@ static void reset_window(int reinit) { extra_width = wr.right - wr.left - cr.right + cr.left +offset_width*2; extra_height = wr.bottom - wr.top - cr.bottom + cr.top+offset_height*2; - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> font resize to (%d,%d)\n", font_width, font_height); @@ -2132,9 +2089,11 @@ static void win_seat_notify_remote_exit(Seat *seat) /* exitcode == INT_MAX indicates that the connection was closed * by a fatal error, so an error box will be coming our way and * we should not generate this informational one. */ - if (exitcode != INT_MAX) - MessageBox(hwnd, "Connection closed by remote host", + if (exitcode != INT_MAX) { + show_mouseptr(true); + MessageBox(wgs.term_hwnd, "Connection closed by remote host", appname, MB_OK | MB_ICONINFORMATION); + } } } } @@ -2147,8 +2106,8 @@ void timer_change_notify(unsigned long next) ticks = 0; else ticks = next - now; - KillTimer(hwnd, TIMING_TIMER_ID); - SetTimer(hwnd, TIMING_TIMER_ID, ticks, NULL); + KillTimer(wgs.term_hwnd, TIMING_TIMER_ID); + SetTimer(wgs.term_hwnd, TIMING_TIMER_ID, ticks, NULL); timing_next_time = next; } @@ -2165,10 +2124,10 @@ static HDC make_hdc(void) { HDC hdc; - if (!hwnd) + if (!wgs.term_hwnd) return NULL; - hdc = GetDC(hwnd); + hdc = GetDC(wgs.term_hwnd); if (!hdc) return NULL; @@ -2178,9 +2137,42 @@ static HDC make_hdc(void) static void free_hdc(HDC hdc) { - assert(hwnd); + assert(wgs.term_hwnd); SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); - ReleaseDC(hwnd, hdc); + ReleaseDC(wgs.term_hwnd, hdc); +} + +static bool need_backend_resize = false; + +static void wm_size_resize_term(LPARAM lParam, bool border) +{ + int width = LOWORD(lParam); + int height = HIWORD(lParam); + int border_size = border ? conf_get_int(conf, CONF_window_border) : 0; + + int w = (width - border_size*2) / font_width; + int h = (height - border_size*2) / font_height; + + if (w < 1) w = 1; + if (h < 1) h = 1; + + if (resizing) { + /* + * If we're in the middle of an interactive resize, we don't + * call term_size. This means that, firstly, the user can drag + * the size back and forth indecisively without wiping out any + * actual terminal contents, and secondly, the Terminal + * doesn't call back->size in turn for each increment of the + * resizing drag, so we don't spam the server with huge + * numbers of resize events. + */ + need_backend_resize = true; + conf_set_int(conf, CONF_height, h); + conf_set_int(conf, CONF_width, w); + } else { + term_size(term, h, w, + conf_get_int(conf, CONF_savelines)); + } } static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, @@ -2188,9 +2180,9 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, { HDC hdc; static bool ignore_clip = false; - static bool need_backend_resize = false; static bool fullscr_on_max = false; static bool processed_resize = false; + static bool in_scrollbar_loop = false; static UINT last_mousemove = 0; int resize_action; @@ -2208,20 +2200,26 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, return 0; case WM_CREATE: break; - case WM_CLOSE: - { - char *str; - show_mouseptr(true); - str = dupprintf("%s Exit Confirmation", appname); - if (session_closed || !conf_get_bool(conf, CONF_warn_on_close) || - MessageBox(hwnd, - "Are you sure you want to close this session?", - str, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) - == IDOK) - DestroyWindow(hwnd); - sfree(str); + case WM_CLOSE: { + char *title, *msg, *additional = NULL; + show_mouseptr(true); + title = dupprintf("%s Exit Confirmation", appname); + if (backend && backend->vt->close_warn_text) { + additional = backend->vt->close_warn_text(backend); } + msg = dupprintf("Are you sure you want to close this session?%s%s", + additional ? "\n" : "", + additional ? additional : ""); + if (session_closed || !conf_get_bool(conf, CONF_warn_on_close) || + MessageBox(hwnd, msg, title, + MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) + == IDOK) + DestroyWindow(hwnd); + sfree(title); + sfree(msg); + sfree(additional); return 0; + } case WM_DESTROY: show_mouseptr(true); PostQuitMessage(0); @@ -2239,289 +2237,293 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_COMMAND: case WM_SYSCOMMAND: switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ + case SC_VSCROLL: + case SC_HSCROLL: + if (message == WM_SYSCOMMAND) { + /* As per the long comment in WM_VSCROLL handler: give + * this message the default handling, which starts a + * subsidiary message loop, but set a flag so that + * when we're re-entered from that loop, scroll events + * within an interactive scrollbar-drag can be handled + * differently. */ + in_scrollbar_loop = true; + LRESULT result = DefWindowProcW(hwnd, message, wParam, lParam); + in_scrollbar_loop = false; + return result; + } + break; case IDM_SHOWLOG: showeventlog(hwnd); break; case IDM_NEWSESS: case IDM_DUPSESS: - case IDM_SAVEDSESS: - { - char b[2048]; - char *cl; - const char *argprefix; - bool inherit_handles; - STARTUPINFO si; - PROCESS_INFORMATION pi; - HANDLE filemap = NULL; - - if (restricted_acl) - argprefix = "&R"; - else - argprefix = ""; - - if (wParam == IDM_DUPSESS) { - /* - * Allocate a file-mapping memory chunk for the - * config structure. - */ - SECURITY_ATTRIBUTES sa; - strbuf *serbuf; - void *p; - int size; - - serbuf = strbuf_new(); - conf_serialise(BinarySink_UPCAST(serbuf), conf); - size = serbuf->len; - - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = true; - filemap = CreateFileMapping(INVALID_HANDLE_VALUE, - &sa, - PAGE_READWRITE, - 0, size, NULL); - if (filemap && filemap != INVALID_HANDLE_VALUE) { - p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size); - if (p) { - memcpy(p, serbuf->s, size); - UnmapViewOfFile(p); - } - } - - strbuf_free(serbuf); - inherit_handles = true; - cl = dupprintf("putty %s&%p:%u", argprefix, - filemap, (unsigned)size); - } else if (wParam == IDM_SAVEDSESS) { - unsigned int sessno = ((lParam - IDM_SAVED_MIN) - / MENU_SAVED_STEP) + 1; - if (sessno < (unsigned)sesslist.nsessions) { - const char *session = sesslist.sessions[sessno]; - cl = dupprintf("putty %s@%s", argprefix, session); - inherit_handles = false; - } else - break; - } else /* IDM_NEWSESS */ { - cl = dupprintf("putty%s%s", - *argprefix ? " " : "", - argprefix); - inherit_handles = false; + case IDM_SAVEDSESS: { + char b[2048]; + char *cl; + const char *argprefix; + bool inherit_handles; + STARTUPINFO si; + PROCESS_INFORMATION pi; + HANDLE filemap = NULL; + + if (restricted_acl()) + argprefix = "&R"; + else + argprefix = ""; + + if (wParam == IDM_DUPSESS) { + /* + * Allocate a file-mapping memory chunk for the + * config structure. + */ + SECURITY_ATTRIBUTES sa; + strbuf *serbuf; + void *p; + int size; + + serbuf = strbuf_new(); + conf_serialise(BinarySink_UPCAST(serbuf), conf); + size = serbuf->len; + + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = true; + filemap = CreateFileMapping(INVALID_HANDLE_VALUE, + &sa, + PAGE_READWRITE, + 0, size, NULL); + if (filemap && filemap != INVALID_HANDLE_VALUE) { + p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, size); + if (p) { + memcpy(p, serbuf->s, size); + UnmapViewOfFile(p); } - - GetModuleFileName(NULL, b, sizeof(b) - 1); - si.cb = sizeof(si); - si.lpReserved = NULL; - si.lpDesktop = NULL; - si.lpTitle = NULL; - si.dwFlags = 0; - si.cbReserved2 = 0; - si.lpReserved2 = NULL; - CreateProcess(b, cl, NULL, NULL, inherit_handles, - NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); - - if (filemap) - CloseHandle(filemap); - sfree(cl); + } + + strbuf_free(serbuf); + inherit_handles = true; + cl = dupprintf("putty %s&%p:%u", argprefix, + filemap, (unsigned)size); + } else if (wParam == IDM_SAVEDSESS) { + unsigned int sessno = ((lParam - IDM_SAVED_MIN) + / MENU_SAVED_STEP) + 1; + if (sessno < (unsigned)sesslist.nsessions) { + const char *session = sesslist.sessions[sessno]; + cl = dupprintf("putty %s@%s", argprefix, session); + inherit_handles = false; + } else + break; + } else /* IDM_NEWSESS */ { + cl = dupprintf("putty%s%s", + *argprefix ? " " : "", + argprefix); + inherit_handles = false; } + + GetModuleFileName(NULL, b, sizeof(b) - 1); + si.cb = sizeof(si); + si.lpReserved = NULL; + si.lpDesktop = NULL; + si.lpTitle = NULL; + si.dwFlags = 0; + si.cbReserved2 = 0; + si.lpReserved2 = NULL; + CreateProcess(b, cl, NULL, NULL, inherit_handles, + NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + if (filemap) + CloseHandle(filemap); + sfree(cl); break; + } case IDM_RESTART: if (!backend) { - lp_eventlog(default_logpolicy, - "----- Session restarted -----"); + lp_eventlog(&wgs.logpolicy, "----- Session restarted -----"); term_pwron(term, false); start_backend(); } break; - case IDM_RECONF: - { - Conf *prev_conf; - int init_lvl = 1; - bool reconfig_result; + case IDM_RECONF: { + Conf *prev_conf; + int init_lvl = 1; + bool reconfig_result; - if (reconfiguring) - break; - else - reconfiguring = true; + if (reconfiguring) + break; + else + reconfiguring = true; - /* - * Copy the current window title into the stored - * previous configuration, so that doing nothing to - * the window title field in the config box doesn't - * reset the title to its startup state. - */ - conf_set_str(conf, CONF_wintitle, window_name); + term_pre_reconfig(term, conf); + prev_conf = conf_copy(conf); - prev_conf = conf_copy(conf); + reconfig_result = do_reconfig( + hwnd, conf, backend ? backend_cfg_info(backend) : 0); + reconfiguring = false; + if (!reconfig_result) { + conf_free(prev_conf); + break; + } - reconfig_result = - do_reconfig(hwnd, backend ? backend_cfg_info(backend) : 0); - reconfiguring = false; - if (!reconfig_result) { - conf_free(prev_conf); - break; - } + conf_cache_data(); - conf_cache_data(); + resize_action = conf_get_int(conf, CONF_resize_action); + { + /* Disable full-screen if resizing forbidden */ + int i; + for (i = 0; i < lenof(popup_menus); i++) + EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, + MF_BYCOMMAND | + (resize_action == RESIZE_DISABLED + ? MF_GRAYED : MF_ENABLED)); + /* Gracefully unzoom if necessary */ + if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED)) + ShowWindow(hwnd, SW_RESTORE); + } - resize_action = conf_get_int(conf, CONF_resize_action); - { - /* Disable full-screen if resizing forbidden */ - int i; - for (i = 0; i < lenof(popup_menus); i++) - EnableMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, - MF_BYCOMMAND | - (resize_action == RESIZE_DISABLED) - ? MF_GRAYED : MF_ENABLED); - /* Gracefully unzoom if necessary */ - if (IsZoomed(hwnd) && (resize_action == RESIZE_DISABLED)) - ShowWindow(hwnd, SW_RESTORE); - } + /* Pass new config data to the logging module */ + log_reconfig(logctx, conf); - /* Pass new config data to the logging module */ - log_reconfig(logctx, conf); + sfree(logpal); + /* + * Flush the line discipline's edit buffer in the + * case where local editing has just been disabled. + */ + if (ldisc) { + ldisc_configure(ldisc, conf); + ldisc_echoedit_update(ldisc); + } - sfree(logpal); - /* - * Flush the line discipline's edit buffer in the - * case where local editing has just been disabled. - */ - if (ldisc) { - ldisc_configure(ldisc, conf); - ldisc_echoedit_update(ldisc); - } - if (pal) - DeleteObject(pal); - logpal = NULL; - pal = NULL; - conftopalette(); - init_palette(); - - /* Pass new config data to the terminal */ - term_reconfig(term, conf); - setup_clipboards(term, conf); - - /* Pass new config data to the back end */ - if (backend) - backend_reconfig(backend, conf); - - /* Screen size changed ? */ - if (conf_get_int(conf, CONF_height) != - conf_get_int(prev_conf, CONF_height) || - conf_get_int(conf, CONF_width) != - conf_get_int(prev_conf, CONF_width) || - conf_get_int(conf, CONF_savelines) != - conf_get_int(prev_conf, CONF_savelines) || - resize_action == RESIZE_FONT || - (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || - resize_action == RESIZE_DISABLED) - term_size(term, conf_get_int(conf, CONF_height), - conf_get_int(conf, CONF_width), - conf_get_int(conf, CONF_savelines)); + if (conf_get_bool(conf, CONF_system_colour) != + conf_get_bool(prev_conf, CONF_system_colour)) + term_notify_palette_changed(term); - /* Enable or disable the scroll bar, etc */ - { - LONG nflg, flag = GetWindowLongPtr(hwnd, GWL_STYLE); - LONG nexflag, exflag = - GetWindowLongPtr(hwnd, GWL_EXSTYLE); - - nexflag = exflag; - if (conf_get_bool(conf, CONF_alwaysontop) != - conf_get_bool(prev_conf, CONF_alwaysontop)) { - if (conf_get_bool(conf, CONF_alwaysontop)) { - nexflag |= WS_EX_TOPMOST; - SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE); - } else { - nexflag &= ~(WS_EX_TOPMOST); - SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE); - } - } - if (conf_get_bool(conf, CONF_sunken_edge)) - nexflag |= WS_EX_CLIENTEDGE; - else - nexflag &= ~(WS_EX_CLIENTEDGE); - - nflg = flag; - if (conf_get_bool(conf, is_full_screen() ? - CONF_scrollbar_in_fullscreen : - CONF_scrollbar)) - nflg |= WS_VSCROLL; - else - nflg &= ~WS_VSCROLL; - - if (resize_action == RESIZE_DISABLED || - is_full_screen()) - nflg &= ~WS_THICKFRAME; - else - nflg |= WS_THICKFRAME; - - if (resize_action == RESIZE_DISABLED) - nflg &= ~WS_MAXIMIZEBOX; - else - nflg |= WS_MAXIMIZEBOX; - - if (nflg != flag || nexflag != exflag) { - if (nflg != flag) - SetWindowLongPtr(hwnd, GWL_STYLE, nflg); - if (nexflag != exflag) - SetWindowLongPtr(hwnd, GWL_EXSTYLE, nexflag); - - SetWindowPos(hwnd, NULL, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOCOPYBITS | - SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | - SWP_FRAMECHANGED); - - init_lvl = 2; - } - } + /* Pass new config data to the terminal */ + term_reconfig(term, conf); + setup_clipboards(term, conf); - /* Oops */ - if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { - force_normal(hwnd); - init_lvl = 2; - } + /* Reinitialise the colour palette, in case the terminal + * just read new settings out of Conf */ + if (pal) + DeleteObject(pal); + logpal = NULL; + pal = NULL; + init_palette(); - win_set_title(wintw, conf_get_str(conf, CONF_wintitle)); - if (IsIconic(hwnd)) { - SetWindowText(hwnd, - conf_get_bool(conf, CONF_win_name_always) ? - window_name : icon_name); - } + /* Pass new config data to the back end */ + if (backend) + backend_reconfig(backend, conf); + + /* Screen size changed ? */ + if (conf_get_int(conf, CONF_height) != + conf_get_int(prev_conf, CONF_height) || + conf_get_int(conf, CONF_width) != + conf_get_int(prev_conf, CONF_width) || + conf_get_int(conf, CONF_savelines) != + conf_get_int(prev_conf, CONF_savelines) || + resize_action == RESIZE_FONT || + (resize_action == RESIZE_EITHER && IsZoomed(hwnd)) || + resize_action == RESIZE_DISABLED) + term_size(term, conf_get_int(conf, CONF_height), + conf_get_int(conf, CONF_width), + conf_get_int(conf, CONF_savelines)); - { - FontSpec *font = conf_get_fontspec(conf, CONF_font); - FontSpec *prev_font = conf_get_fontspec(prev_conf, - CONF_font); - - if (!strcmp(font->name, prev_font->name) || - !strcmp(conf_get_str(conf, CONF_line_codepage), - conf_get_str(prev_conf, CONF_line_codepage)) || - font->isbold != prev_font->isbold || - font->height != prev_font->height || - font->charset != prev_font->charset || - conf_get_int(conf, CONF_font_quality) != - conf_get_int(prev_conf, CONF_font_quality) || - conf_get_int(conf, CONF_vtmode) != - conf_get_int(prev_conf, CONF_vtmode) || - conf_get_int(conf, CONF_bold_style) != - conf_get_int(prev_conf, CONF_bold_style) || - resize_action == RESIZE_DISABLED || - resize_action == RESIZE_EITHER || - resize_action != conf_get_int(prev_conf, - CONF_resize_action)) - init_lvl = 2; + /* Enable or disable the scroll bar, etc */ + { + LONG nflg, flag = GetWindowLongPtr(hwnd, GWL_STYLE); + LONG nexflag, exflag = + GetWindowLongPtr(hwnd, GWL_EXSTYLE); + + nexflag = exflag; + if (conf_get_bool(conf, CONF_alwaysontop) != + conf_get_bool(prev_conf, CONF_alwaysontop)) { + if (conf_get_bool(conf, CONF_alwaysontop)) { + nexflag |= WS_EX_TOPMOST; + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); + } else { + nexflag &= ~(WS_EX_TOPMOST); + SetWindowPos(hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE); } + } + if (conf_get_bool(conf, CONF_sunken_edge)) + nexflag |= WS_EX_CLIENTEDGE; + else + nexflag &= ~(WS_EX_CLIENTEDGE); + + nflg = flag; + if (conf_get_bool(conf, is_full_screen() ? + CONF_scrollbar_in_fullscreen : + CONF_scrollbar)) + nflg |= WS_VSCROLL; + else + nflg &= ~WS_VSCROLL; + + if (resize_action == RESIZE_DISABLED || + is_full_screen()) + nflg &= ~WS_THICKFRAME; + else + nflg |= WS_THICKFRAME; + + if (resize_action == RESIZE_DISABLED) + nflg &= ~WS_MAXIMIZEBOX; + else + nflg |= WS_MAXIMIZEBOX; + + if (nflg != flag || nexflag != exflag) { + if (nflg != flag) + SetWindowLongPtr(hwnd, GWL_STYLE, nflg); + if (nexflag != exflag) + SetWindowLongPtr(hwnd, GWL_EXSTYLE, nexflag); + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOCOPYBITS | + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_FRAMECHANGED); + + init_lvl = 2; + } + } - InvalidateRect(hwnd, NULL, true); - reset_window(init_lvl); + /* Oops */ + if (resize_action == RESIZE_DISABLED && IsZoomed(hwnd)) { + force_normal(hwnd); + init_lvl = 2; + } - conf_free(prev_conf); + { + FontSpec *font = conf_get_fontspec(conf, CONF_font); + FontSpec *prev_font = conf_get_fontspec(prev_conf, + CONF_font); + + if (!strcmp(font->name, prev_font->name) || + !strcmp(conf_get_str(conf, CONF_line_codepage), + conf_get_str(prev_conf, CONF_line_codepage)) || + font->isbold != prev_font->isbold || + font->height != prev_font->height || + font->charset != prev_font->charset || + conf_get_int(conf, CONF_font_quality) != + conf_get_int(prev_conf, CONF_font_quality) || + conf_get_int(conf, CONF_vtmode) != + conf_get_int(prev_conf, CONF_vtmode) || + conf_get_int(conf, CONF_bold_style) != + conf_get_int(prev_conf, CONF_bold_style) || + resize_action == RESIZE_DISABLED || + resize_action == RESIZE_EITHER || + resize_action != conf_get_int(prev_conf, + CONF_resize_action)) + init_lvl = 2; } + + InvalidateRect(hwnd, NULL, true); + reset_window(init_lvl); + + conf_free(prev_conf); break; + } case IDM_COPYALL: term_copyall(term, clips_system, lenof(clips_system)); break; @@ -2709,21 +2711,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } } return 0; - case WM_MOUSEMOVE: - { - /* - * Windows seems to like to occasionally send MOUSEMOVE - * events even if the mouse hasn't moved. Don't unhide - * the mouse pointer in this case. - */ - static WPARAM wp = 0; - static LPARAM lp = 0; - if (wParam != wp || lParam != lp || - last_mousemove != WM_MOUSEMOVE) { - show_mouseptr(true); - wp = wParam; lp = lParam; - last_mousemove = WM_MOUSEMOVE; - } + case WM_MOUSEMOVE: { + /* + * Windows seems to like to occasionally send MOUSEMOVE + * events even if the mouse hasn't moved. Don't unhide + * the mouse pointer in this case. + */ + static WPARAM wp = 0; + static LPARAM lp = 0; + if (wParam != wp || lParam != lp || + last_mousemove != WM_MOUSEMOVE) { + show_mouseptr(true); + wp = wParam; lp = lParam; + last_mousemove = WM_MOUSEMOVE; } /* * Add the mouse position and message time to the random @@ -2746,19 +2746,19 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, wParam & MK_CONTROL, is_alt_pressed()); } return 0; - case WM_NCMOUSEMOVE: - { - static WPARAM wp = 0; - static LPARAM lp = 0; - if (wParam != wp || lParam != lp || - last_mousemove != WM_NCMOUSEMOVE) { - show_mouseptr(true); - wp = wParam; lp = lParam; - last_mousemove = WM_NCMOUSEMOVE; - } + } + case WM_NCMOUSEMOVE: { + static WPARAM wp = 0; + static LPARAM lp = 0; + if (wParam != wp || lParam != lp || + last_mousemove != WM_NCMOUSEMOVE) { + show_mouseptr(true); + wp = wParam; lp = lParam; + last_mousemove = WM_NCMOUSEMOVE; } noise_ultralight(NOISE_SOURCE_MOUSEPOS, lParam); break; + } case WM_IGNORE_CLIP: ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */ break; @@ -2771,123 +2771,122 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, term_lost_clipboard_ownership(term, CLIP_SYSTEM); ignore_clip = false; } - return 0; - case WM_PAINT: - { - PAINTSTRUCT p; - HideCaret(hwnd); - hdc = BeginPaint(hwnd, &p); - if (pal) { - SelectPalette(hdc, pal, true); - RealizePalette(hdc); - } - - /* - * We have to be careful about term_paint(). It will - * set a bunch of character cells to INVALID and then - * call do_paint(), which will redraw those cells and - * _then mark them as done_. This may not be accurate: - * when painting in WM_PAINT context we are restricted - * to the rectangle which has just been exposed - so if - * that only covers _part_ of a character cell and the - * rest of it was already visible, that remainder will - * not be redrawn at all. Accordingly, we must not - * paint any character cell in a WM_PAINT context which - * already has a pending update due to terminal output. - * The simplest solution to this - and many, many - * thanks to Hung-Te Lin for working all this out - is - * not to do any actual painting at _all_ if there's a - * pending terminal update: just mark the relevant - * character cells as INVALID and wait for the - * scheduled full update to sort it out. - * - * I have a suspicion this isn't the _right_ solution. - * An alternative approach would be to have terminal.c - * separately track what _should_ be on the terminal - * screen and what _is_ on the terminal screen, and - * have two completely different types of redraw (one - * for full updates, which syncs the former with the - * terminal itself, and one for WM_PAINT which syncs - * the latter with the former); yet another possibility - * would be to have the Windows front end do what the - * GTK one already does, and maintain a bitmap of the - * current terminal appearance so that WM_PAINT becomes - * completely trivial. However, this should do for now. - */ - assert(!wintw_hdc); - wintw_hdc = hdc; - term_paint(term, - (p.rcPaint.left-offset_width)/font_width, - (p.rcPaint.top-offset_height)/font_height, - (p.rcPaint.right-offset_width-1)/font_width, - (p.rcPaint.bottom-offset_height-1)/font_height, - !term->window_update_pending); - wintw_hdc = NULL; - - if (p.fErase || - p.rcPaint.left < offset_width || - p.rcPaint.top < offset_height || - p.rcPaint.right >= offset_width + font_width*term->cols || - p.rcPaint.bottom>= offset_height + font_height*term->rows) - { - HBRUSH fillcolour, oldbrush; - HPEN edge, oldpen; - fillcolour = CreateSolidBrush ( - colours[ATTR_DEFBG>>ATTR_BGSHIFT]); - oldbrush = SelectObject(hdc, fillcolour); - edge = CreatePen(PS_SOLID, 0, - colours[ATTR_DEFBG>>ATTR_BGSHIFT]); - oldpen = SelectObject(hdc, edge); - - /* - * Jordan Russell reports that this apparently - * ineffectual IntersectClipRect() call masks a - * Windows NT/2K bug causing strange display - * problems when the PuTTY window is taller than - * the primary monitor. It seems harmless enough... - */ - IntersectClipRect(hdc, - p.rcPaint.left, p.rcPaint.top, - p.rcPaint.right, p.rcPaint.bottom); - - ExcludeClipRect(hdc, - offset_width, offset_height, - offset_width+font_width*term->cols, - offset_height+font_height*term->rows); - - Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, - p.rcPaint.right, p.rcPaint.bottom); - - /* SelectClipRgn(hdc, NULL); */ + return 0; + case WM_PAINT: { + PAINTSTRUCT p; - SelectObject(hdc, oldbrush); - DeleteObject(fillcolour); - SelectObject(hdc, oldpen); - DeleteObject(edge); - } - SelectObject(hdc, GetStockObject(SYSTEM_FONT)); - SelectObject(hdc, GetStockObject(WHITE_PEN)); - EndPaint(hwnd, &p); - ShowCaret(hwnd); + HideCaret(hwnd); + hdc = BeginPaint(hwnd, &p); + if (pal) { + SelectPalette(hdc, pal, true); + RealizePalette(hdc); } - return 0; - case WM_NETEVENT: + + /* + * We have to be careful about term_paint(). It will + * set a bunch of character cells to INVALID and then + * call do_paint(), which will redraw those cells and + * _then mark them as done_. This may not be accurate: + * when painting in WM_PAINT context we are restricted + * to the rectangle which has just been exposed - so if + * that only covers _part_ of a character cell and the + * rest of it was already visible, that remainder will + * not be redrawn at all. Accordingly, we must not + * paint any character cell in a WM_PAINT context which + * already has a pending update due to terminal output. + * The simplest solution to this - and many, many + * thanks to Hung-Te Lin for working all this out - is + * not to do any actual painting at _all_ if there's a + * pending terminal update: just mark the relevant + * character cells as INVALID and wait for the + * scheduled full update to sort it out. + * + * I have a suspicion this isn't the _right_ solution. + * An alternative approach would be to have terminal.c + * separately track what _should_ be on the terminal + * screen and what _is_ on the terminal screen, and + * have two completely different types of redraw (one + * for full updates, which syncs the former with the + * terminal itself, and one for WM_PAINT which syncs + * the latter with the former); yet another possibility + * would be to have the Windows front end do what the + * GTK one already does, and maintain a bitmap of the + * current terminal appearance so that WM_PAINT becomes + * completely trivial. However, this should do for now. + */ + assert(!wintw_hdc); + wintw_hdc = hdc; + term_paint(term, + (p.rcPaint.left-offset_width)/font_width, + (p.rcPaint.top-offset_height)/font_height, + (p.rcPaint.right-offset_width-1)/font_width, + (p.rcPaint.bottom-offset_height-1)/font_height, + !term->window_update_pending); + wintw_hdc = NULL; + + if (p.fErase || + p.rcPaint.left < offset_width || + p.rcPaint.top < offset_height || + p.rcPaint.right >= offset_width + font_width*term->cols || + p.rcPaint.bottom>= offset_height + font_height*term->rows) { - /* - * To protect against re-entrancy when Windows's recv() - * immediately triggers a new WSAAsyncSelect window - * message, we don't call select_result directly from this - * handler but instead wait until we're back out at the - * top level of the message loop. - */ - struct wm_netevent_params *params = - snew(struct wm_netevent_params); - params->wParam = wParam; - params->lParam = lParam; - queue_toplevel_callback(wm_netevent_callback, params); + HBRUSH fillcolour, oldbrush; + HPEN edge, oldpen; + fillcolour = CreateSolidBrush ( + colours[ATTR_DEFBG>>ATTR_BGSHIFT]); + oldbrush = SelectObject(hdc, fillcolour); + edge = CreatePen(PS_SOLID, 0, + colours[ATTR_DEFBG>>ATTR_BGSHIFT]); + oldpen = SelectObject(hdc, edge); + + /* + * Jordan Russell reports that this apparently + * ineffectual IntersectClipRect() call masks a + * Windows NT/2K bug causing strange display + * problems when the PuTTY window is taller than + * the primary monitor. It seems harmless enough... + */ + IntersectClipRect(hdc, + p.rcPaint.left, p.rcPaint.top, + p.rcPaint.right, p.rcPaint.bottom); + + ExcludeClipRect(hdc, + offset_width, offset_height, + offset_width+font_width*term->cols, + offset_height+font_height*term->rows); + + Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, + p.rcPaint.right, p.rcPaint.bottom); + + /* SelectClipRgn(hdc, NULL); */ + + SelectObject(hdc, oldbrush); + DeleteObject(fillcolour); + SelectObject(hdc, oldpen); + DeleteObject(edge); } + SelectObject(hdc, GetStockObject(SYSTEM_FONT)); + SelectObject(hdc, GetStockObject(WHITE_PEN)); + EndPaint(hwnd, &p); + ShowCaret(hwnd); + return 0; + } + case WM_NETEVENT: { + /* + * To protect against re-entrancy when Windows's recv() + * immediately triggers a new WSAAsyncSelect window + * message, we don't call select_result directly from this + * handler but instead wait until we're back out at the + * top level of the message loop. + */ + struct wm_netevent_params *params = + snew(struct wm_netevent_params); + params->wParam = wParam; + params->lParam = lParam; + queue_toplevel_callback(wm_netevent_callback, params); return 0; + } case WM_SETFOCUS: term_set_focus(term, true); CreateCaret(hwnd, caretbm, font_width, font_height); @@ -2923,6 +2922,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, conf_get_int(conf, CONF_savelines)); InvalidateRect(hwnd, NULL, true); } + recompute_window_offset(); break; case WM_SIZING: /* @@ -3021,6 +3021,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, fullscr_on_max = true; break; case WM_MOVE: + term_notify_window_pos(term, LOWORD(lParam), HIWORD(lParam)); sys_cursor_update(); break; case WM_SIZE: @@ -3034,6 +3035,18 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, "...", LOWORD(lParam), HIWORD(lParam)); #endif + term_notify_minimised(term, wParam == SIZE_MINIMIZED); + { + /* + * WM_SIZE's lParam tells us the size of the client area. + * But historic PuTTY practice is that we want to tell the + * terminal the size of the overall window. + */ + RECT r; + GetWindowRect(hwnd, &r); + term_notify_window_size_pixels( + term, r.right - r.left, r.bottom - r.top); + } if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, conf_get_bool(conf, CONF_win_name_always) ? @@ -3074,48 +3087,17 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, /* A resize, well it better be a minimize. */ reset_window(-1); } else { - - int width, height, w, h; - int window_border = conf_get_int(conf, CONF_window_border); - - width = LOWORD(lParam); - height = HIWORD(lParam); - if (wParam == SIZE_MAXIMIZED) { was_zoomed = true; prev_rows = term->rows; prev_cols = term->cols; - if (resize_action == RESIZE_TERM) { - w = width / font_width; - if (w < 1) w = 1; - h = height / font_height; - if (h < 1) h = 1; - - if (resizing) { - /* - * As below, if we're in the middle of an - * interactive resize we don't call - * back->size. In Windows 7, this case can - * arise in maximisation as well via the Aero - * snap UI. - */ - need_backend_resize = true; - conf_set_int(conf, CONF_height, h); - conf_set_int(conf, CONF_width, w); - } else { - term_size(term, h, w, - conf_get_int(conf, CONF_savelines)); - } - } + if (resize_action == RESIZE_TERM) + wm_size_resize_term(lParam, false); reset_window(0); } else if (wParam == SIZE_RESTORED && was_zoomed) { was_zoomed = false; if (resize_action == RESIZE_TERM) { - w = (width-window_border*2) / font_width; - if (w < 1) w = 1; - h = (height-window_border*2) / font_height; - if (h < 1) h = 1; - term_size(term, h, w, conf_get_int(conf, CONF_savelines)); + wm_size_resize_term(lParam, true); reset_window(2); } else if (resize_action != RESIZE_FONT) reset_window(2); @@ -3126,30 +3108,35 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } else if (resize_action == RESIZE_TERM || (resize_action == RESIZE_EITHER && !is_alt_pressed())) { - w = (width-window_border*2) / font_width; - if (w < 1) w = 1; - h = (height-window_border*2) / font_height; - if (h < 1) h = 1; + wm_size_resize_term(lParam, true); - if (resizing) { - /* - * Don't call back->size in mid-resize. (To - * prevent massive numbers of resize events - * getting sent down the connection during an NT - * opaque drag.) - */ - need_backend_resize = true; - conf_set_int(conf, CONF_height, h); - conf_set_int(conf, CONF_width, w); - } else { - term_size(term, h, w, conf_get_int(conf, CONF_savelines)); - } + /* + * Sometimes, we can get a spontaneous resize event + * outside a WM_SIZING interactive drag which wants to + * set us to a new specific SIZE_RESTORED size. An + * example is what happens if you press Windows+Right + * and then Windows+Up: the first operation fits the + * window to the right-hand half of the screen, and + * the second one changes that for the top right + * quadrant. In that situation, if we've responded + * here by resizing the terminal, we may still need to + * recompute the border around the window and do a + * full redraw to clear the new border. + */ + if (!resizing) + recompute_window_offset(); } else { reset_window(0); } } sys_cursor_update(); return 0; + case WM_DPICHANGED: + dpi_info.cur_dpi.x = LOWORD(wParam); + dpi_info.cur_dpi.y = HIWORD(wParam); + dpi_info.new_wnd_rect = *(RECT*)(lParam); + reset_window(3); + return 0; case WM_VSCROLL: switch (LOWORD(wParam)) { case SB_BOTTOM: @@ -3171,21 +3158,65 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, term_scroll(term, 0, -term->rows / 2); break; case SB_THUMBPOSITION: - case SB_THUMBTRACK: + case SB_THUMBTRACK: { /* * Use GetScrollInfo instead of HIWORD(wParam) to get * 32-bit scroll position. */ - { - SCROLLINFO si; + SCROLLINFO si; - si.cbSize = sizeof(si); - si.fMask = SIF_TRACKPOS; - if (GetScrollInfo(hwnd, SB_VERT, &si) == 0) - si.nTrackPos = HIWORD(wParam); - term_scroll(term, 1, si.nTrackPos); - } + si.cbSize = sizeof(si); + si.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_VERT, &si) == 0) + si.nTrackPos = HIWORD(wParam); + term_scroll(term, 1, si.nTrackPos); break; + } + } + + if (in_scrollbar_loop) { + /* + * Allow window updates to happen during interactive + * scroll. + * + * When the user takes hold of our window's scrollbar and + * wobbles it interactively back and forth, or presses on + * one of the arrow buttons at the ends, the first thing + * that happens is that this window procedure receives + * WM_SYSCOMMAND / SC_VSCROLL. [1] The default handler for + * that window message starts a subsidiary message loop, + * which continues to run until the user lets go of the + * scrollbar again. All WM_VSCROLL / SB_THUMBTRACK + * messages are generated by the handlers within that + * subsidiary message loop. + * + * So, during that time, _our_ message loop is not + * running, which means toplevel callbacks and timers and + * so forth are not happening, which means that when we + * redraw the window and set a timer to clear the cooldown + * flag 20ms later, that timer never fires, and we aren't + * able to keep redrawing the window. + * + * The 'obvious' answer would be to seize that SYSCOMMAND + * ourselves and inhibit the default handler, so that our + * message loop carries on running. But that would mean + * we'd have to reimplement the whole of the scrollbar + * handler! + * + * So instead we apply a bodge: set a static variable that + * indicates that we're _in_ that sub-loop, and if so, + * decide it's OK to manually call term_update() proper, + * bypassing the timer and cooldown and rate-limiting + * systems completely, whenever we see an SB_THUMBTRACK. + * This shouldn't cause a rate overload, because we're + * only doing it once per UI event! + * + * [1] Actually, there's an extra oddity where SC_HSCROLL + * and SC_VSCROLL have their documented values the wrong + * way round. Many people on the Internet have noticed + * this, e.g. https://stackoverflow.com/q/55528397 + */ + term_update(term); } break; case WM_PALETTECHANGED: @@ -3395,61 +3426,59 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, set_input_locale((HKL)lParam); sys_cursor_update(); break; - case WM_IME_STARTCOMPOSITION: - { - HIMC hImc = ImmGetContext(hwnd); - ImmSetCompositionFont(hImc, &lfont); - ImmReleaseContext(hwnd, hImc); - } + case WM_IME_STARTCOMPOSITION: { + HIMC hImc = ImmGetContext(hwnd); + ImmSetCompositionFont(hImc, &lfont); + ImmReleaseContext(hwnd, hImc); break; - case WM_IME_COMPOSITION: - { - HIMC hIMC; - int n; - char *buff; - - if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS || - osPlatformId == VER_PLATFORM_WIN32s) - break; /* no Unicode */ - - if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */ - break; /* fall back to DefWindowProc */ - - hIMC = ImmGetContext(hwnd); - n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); - - if (n > 0) { - int i; - buff = snewn(n, char); - ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n); - /* - * Jaeyoun Chung reports that Korean character - * input doesn't work correctly if we do a single - * term_keyinputw covering the whole of buff. So - * instead we send the characters one by one. - */ - /* don't divide SURROGATE PAIR */ - if (ldisc) { - for (i = 0; i < n; i += 2) { - WCHAR hs = *(unsigned short *)(buff+i); - if (IS_HIGH_SURROGATE(hs) && i+2 < n) { - WCHAR ls = *(unsigned short *)(buff+i+2); - if (IS_LOW_SURROGATE(ls)) { - term_keyinputw( - term, (unsigned short *)(buff+i), 2); - i += 2; - continue; - } - } - term_keyinputw( - term, (unsigned short *)(buff+i), 1); - } + } + case WM_IME_COMPOSITION: { + HIMC hIMC; + int n; + char *buff; + + if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS || + osPlatformId == VER_PLATFORM_WIN32s) + break; /* no Unicode */ + + if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */ + break; /* fall back to DefWindowProc */ + + hIMC = ImmGetContext(hwnd); + n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); + + if (n > 0) { + int i; + buff = snewn(n, char); + ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n); + /* + * Jaeyoun Chung reports that Korean character + * input doesn't work correctly if we do a single + * term_keyinputw covering the whole of buff. So + * instead we send the characters one by one. + */ + /* don't divide SURROGATE PAIR */ + if (ldisc) { + for (i = 0; i < n; i += 2) { + WCHAR hs = *(unsigned short *)(buff+i); + if (IS_HIGH_SURROGATE(hs) && i+2 < n) { + WCHAR ls = *(unsigned short *)(buff+i+2); + if (IS_LOW_SURROGATE(ls)) { + term_keyinputw( + term, (unsigned short *)(buff+i), 2); + i += 2; + continue; } - free(buff); + } + term_keyinputw( + term, (unsigned short *)(buff+i), 1); } - ImmReleaseContext(hwnd, hIMC); - return 1; + } + free(buff); } + ImmReleaseContext(hwnd, hIMC); + return 1; + } case WM_IME_CHAR: if (wParam & 0xFF00) { @@ -3491,20 +3520,12 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case WM_SYSCOLORCHANGE: if (conf_get_bool(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ - /* XXX actually this zaps the entire palette. */ - systopalette(); + term_notify_palette_changed(term); init_palette(); /* Force a repaint of the terminal window. */ term_invalidate(term); } break; - case WM_AGENT_CALLBACK: - { - struct agent_callback *c = (struct agent_callback *)lParam; - c->callback(c->callback_ctx, c->data, c->len); - sfree(c); - } - return 0; case WM_GOT_CLIPDATA: process_clipdata((HGLOBAL)lParam, wParam); return 0; @@ -3617,13 +3638,32 @@ static void sys_cursor_update(void) osMinorVersion == 0) return; /* 95 */ /* we should have the IMM functions */ - hIMC = ImmGetContext(hwnd); + hIMC = ImmGetContext(wgs.term_hwnd); cf.dwStyle = CFS_POINT; cf.ptCurrentPos.x = caret_x; cf.ptCurrentPos.y = caret_y; ImmSetCompositionWindow(hIMC, &cf); - ImmReleaseContext(hwnd, hIMC); + ImmReleaseContext(wgs.term_hwnd, hIMC); +} + +static void draw_horizontal_line_on_text(int y, int lattr, RECT line_box, + COLORREF colour) +{ + if (lattr == LATTR_TOP || lattr == LATTR_BOT) { + y *= 2; + if (lattr == LATTR_BOT) + y -= font_height; + } + + if (!(0 <= y && y < font_height)) + return; + + HPEN oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, colour)); + MoveToEx(wintw_hdc, line_box.left, line_box.top + y, NULL); + LineTo(wintw_hdc, line_box.right, line_box.top + y); + oldpen = SelectObject(wintw_hdc, oldpen); + DeleteObject(oldpen); } /* @@ -4002,20 +4042,13 @@ static void do_text_internal( SetBkMode(wintw_hdc, TRANSPARENT); opaque = false; } + if (lattr != LATTR_TOP && (force_manual_underline || - (und_mode == UND_LINE - && (attr & ATTR_UNDER)))) { - HPEN oldpen; - int dec = descent; - if (lattr == LATTR_BOT) - dec = dec * 2 - font_height; + (und_mode == UND_LINE && (attr & ATTR_UNDER)))) + draw_horizontal_line_on_text(descent, lattr, line_box, fg); - oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, fg)); - MoveToEx(wintw_hdc, line_box.left, line_box.top + dec, NULL); - LineTo(wintw_hdc, line_box.right, line_box.top + dec); - oldpen = SelectObject(wintw_hdc, oldpen); - DeleteObject(oldpen); - } + if (attr & ATTR_STRIKE) + draw_horizontal_line_on_text(font_strikethrough_y, lattr, line_box, fg); } /* @@ -4221,9 +4254,13 @@ static void init_winfuncs(void) { HMODULE user32_module = load_system32_dll("user32.dll"); HMODULE winmm_module = load_system32_dll("winmm.dll"); + HMODULE shcore_module = load_system32_dll("shcore.dll"); GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx); GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx); GET_WINDOWS_FUNCTION_PP(winmm_module, PlaySound); + GET_WINDOWS_FUNCTION_NO_TYPECHECK(shcore_module, GetDpiForMonitor); + GET_WINDOWS_FUNCTION_NO_TYPECHECK(user32_module, GetSystemMetricsForDpi); + GET_WINDOWS_FUNCTION_NO_TYPECHECK(user32_module, AdjustWindowRectExForDpi); } /* @@ -4460,28 +4497,28 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, /* Lets see if it's a pattern we know all about ... */ if (wParam == VK_PRIOR && shift_state == 1) { - SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0); + SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_PAGEUP, 0); return 0; } if (wParam == VK_PRIOR && shift_state == 3) { /* ctrl-shift-pageup */ - SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0); + SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_TOP, 0); return 0; } if (wParam == VK_NEXT && shift_state == 3) { /* ctrl-shift-pagedown */ - SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0); + SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_BOTTOM, 0); return 0; } if (wParam == VK_PRIOR && shift_state == 2) { - SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0); + SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_LINEUP, 0); return 0; } if (wParam == VK_NEXT && shift_state == 1) { - SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); + SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); return 0; } if (wParam == VK_NEXT && shift_state == 2) { - SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0); + SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_LINEDOWN, 0); return 0; } if ((wParam == VK_PRIOR || wParam == VK_NEXT) && shift_state == 3) { @@ -4543,7 +4580,7 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, } if (left_alt && wParam == VK_SPACE && conf_get_bool(conf, CONF_alt_space)) { - SendMessage(hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); + SendMessage(wgs.term_hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } if (left_alt && wParam == VK_RETURN && @@ -4895,19 +4932,17 @@ static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, static void wintw_set_title(TermWin *tw, const char *title) { sfree(window_name); - window_name = snewn(1 + strlen(title), char); - strcpy(window_name, title); - if (conf_get_bool(conf, CONF_win_name_always) || !IsIconic(hwnd)) - SetWindowText(hwnd, title); + window_name = dupstr(title); + if (conf_get_bool(conf, CONF_win_name_always) || !IsIconic(wgs.term_hwnd)) + SetWindowText(wgs.term_hwnd, title); } static void wintw_set_icon_title(TermWin *tw, const char *title) { sfree(icon_name); - icon_name = snewn(1 + strlen(title), char); - strcpy(icon_name, title); - if (!conf_get_bool(conf, CONF_win_name_always) && IsIconic(hwnd)) - SetWindowText(hwnd, title); + icon_name = dupstr(title); + if (!conf_get_bool(conf, CONF_win_name_always) && IsIconic(wgs.term_hwnd)) + SetWindowText(wgs.term_hwnd, title); } static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page) @@ -4924,8 +4959,8 @@ static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page) si.nMax = total - 1; si.nPage = page; si.nPos = start; - if (hwnd) - SetScrollInfo(hwnd, SB_VERT, &si, true); + if (wgs.term_hwnd) + SetScrollInfo(wgs.term_hwnd, SB_VERT, &si, true); } static bool wintw_setup_draw_ctx(TermWin *tw) @@ -4942,75 +4977,74 @@ static void wintw_free_draw_ctx(TermWin *tw) wintw_hdc = NULL; } -static void real_palette_set(int n, int r, int g, int b) -{ - internal_set_colour(n, r, g, b); - if (pal) { - logpal->palPalEntry[n].peRed = r; - logpal->palPalEntry[n].peGreen = g; - logpal->palPalEntry[n].peBlue = b; - logpal->palPalEntry[n].peFlags = PC_NOCOLLAPSE; - SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry); - } -} - -static bool wintw_palette_get(TermWin *tw, int n, int *r, int *g, int *b) +/* + * Set up the colour palette. + */ +static void init_palette(void) { - if (n < 0 || n >= NALLCOLOURS) - return false; - *r = colours_rgb[n].r; - *g = colours_rgb[n].g; - *b = colours_rgb[n].b; - return true; + pal = NULL; + logpal = snew_plus(LOGPALETTE, (OSC4_NCOLOURS - 1) * sizeof(PALETTEENTRY)); + logpal->palVersion = 0x300; + logpal->palNumEntries = OSC4_NCOLOURS; + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; } -static void wintw_palette_set(TermWin *tw, int n, int r, int g, int b) +static void wintw_palette_set(TermWin *win, unsigned start, + unsigned ncolours, const rgb *colours_in) { - if (n >= 16) - n += 256 - 16; - if (n >= NALLCOLOURS) - return; - real_palette_set(n, r, g, b); - if (pal) { - HDC hdc = make_hdc(); - UnrealizeObject(pal); - RealizePalette(hdc); - free_hdc(hdc); - } else { - if (n == (ATTR_DEFBG>>ATTR_BGSHIFT)) - /* If Default Background changes, we need to ensure any - * space between the text area and the window border is - * redrawn. */ - InvalidateRect(hwnd, NULL, true); + assert(start <= OSC4_NCOLOURS); + assert(ncolours <= OSC4_NCOLOURS - start); + + for (unsigned i = 0; i < ncolours; i++) { + const rgb *in = &colours_in[i]; + PALETTEENTRY *out = &logpal->palPalEntry[i + start]; + out->peRed = in->r; + out->peGreen = in->g; + out->peBlue = in->b; + colours[i + start] = RGB(in->r, in->g, in->b) ^ colorref_modifier; } -} -static void wintw_palette_reset(TermWin *tw) -{ - int i; + bool got_new_palette = false; - /* And this */ - for (i = 0; i < NALLCOLOURS; i++) { - internal_set_colour(i, defpal[i].rgbtRed, - defpal[i].rgbtGreen, defpal[i].rgbtBlue); - if (pal) { - logpal->palPalEntry[i].peRed = defpal[i].rgbtRed; - logpal->palPalEntry[i].peGreen = defpal[i].rgbtGreen; - logpal->palPalEntry[i].peBlue = defpal[i].rgbtBlue; - logpal->palPalEntry[i].peFlags = 0; + if (!tried_pal && conf_get_bool(conf, CONF_try_palette)) { + HDC hdc = GetDC(wgs.term_hwnd); + if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { + pal = CreatePalette(logpal); + if (pal) { + SelectPalette(hdc, pal, false); + RealizePalette(hdc); + SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); + + /* Convert all RGB() values in colours[] into PALETTERGB(), + * and ensure we stick to that later */ + colorref_modifier = PALETTERGB(0, 0, 0) ^ RGB(0, 0, 0); + for (unsigned i = 0; i < OSC4_NCOLOURS; i++) + colours[i] ^= colorref_modifier; + + /* Inhibit the SetPaletteEntries call below */ + got_new_palette = true; + } } + ReleaseDC(wgs.term_hwnd, hdc); + tried_pal = true; } - if (pal) { - HDC hdc; - SetPaletteEntries(pal, 0, NALLCOLOURS, logpal->palPalEntry); - hdc = make_hdc(); + if (pal && !got_new_palette) { + /* We already had a palette, so replace the changed colours in the + * existing one. */ + SetPaletteEntries(pal, start, ncolours, logpal->palPalEntry + start); + + HDC hdc = make_hdc(); + UnrealizeObject(pal); RealizePalette(hdc); free_hdc(hdc); - } else { - /* Default Background may have changed. Ensure any space between - * text area and window border is redrawn. */ - InvalidateRect(hwnd, NULL, true); + } + + if (start <= OSC4_COLOUR_bg && OSC4_COLOUR_bg < start + ncolours) { + /* If Default Background changes, we need to ensure any space between + * the text area and the window border is redrawn. */ + InvalidateRect(wgs.term_hwnd, NULL, true); } } @@ -5033,9 +5067,9 @@ void write_aclip(int clipboard, char *data, int len, bool must_deselect) GlobalUnlock(clipdata); if (!must_deselect) - SendMessage(hwnd, WM_IGNORE_CLIP, true, 0); + SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, true, 0); - if (OpenClipboard(hwnd)) { + if (OpenClipboard(wgs.term_hwnd)) { EmptyClipboard(); SetClipboardData(CF_TEXT, clipdata); CloseClipboard(); @@ -5043,7 +5077,7 @@ void write_aclip(int clipboard, char *data, int len, bool must_deselect) GlobalFree(clipdata); if (!must_deselect) - SendMessage(hwnd, WM_IGNORE_CLIP, false, 0); + SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, false, 0); } typedef struct _rgbindex { @@ -5114,7 +5148,7 @@ static void wintw_clip_write( COLORREF bg, lastbg = -1; int attrBold, lastAttrBold = 0; int attrUnder, lastAttrUnder = 0; - int palette[NALLCOLOURS]; + int palette[OSC4_NCOLOURS]; int numcolours; tree234 *rgbtree = NULL; FontSpec *font = conf_get_fontspec(conf, CONF_font); @@ -5190,7 +5224,7 @@ static void wintw_clip_write( * Next - Create a reduced palette */ numcolours = 0; - for (i = 0; i < NALLCOLOURS; i++) { + for (i = 0; i < OSC4_NCOLOURS; i++) { if (palette[i] != 0) palette[i] = ++numcolours; } @@ -5206,11 +5240,11 @@ static void wintw_clip_write( */ put_datapl(rtf, PTRLEN_LITERAL("{\\colortbl ;")); - for (i = 0; i < NALLCOLOURS; i++) { + for (i = 0; i < OSC4_NCOLOURS; i++) { if (palette[i] != 0) { + const PALETTEENTRY *pe = &logpal->palPalEntry[i]; strbuf_catf(rtf, "\\red%d\\green%d\\blue%d;", - defpal[i].rgbtRed, defpal[i].rgbtGreen, - defpal[i].rgbtBlue); + pe->peRed, pe->peGreen, pe->peBlue); } } if (rgbtree) { @@ -5389,7 +5423,7 @@ static void wintw_clip_write( (int)udata[uindex]); alen = 1; strcpy(after, "}"); } else { - blen = sprintf(before, "\\u%d", udata[uindex]); + blen = sprintf(before, "\\u%d", (int)udata[uindex]); alen = 0; after[0] = '\0'; } } @@ -5451,9 +5485,9 @@ static void wintw_clip_write( GlobalUnlock(clipdata2); if (!must_deselect) - SendMessage(hwnd, WM_IGNORE_CLIP, true, 0); + SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, true, 0); - if (OpenClipboard(hwnd)) { + if (OpenClipboard(wgs.term_hwnd)) { EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, clipdata); SetClipboardData(CF_TEXT, clipdata2); @@ -5466,7 +5500,7 @@ static void wintw_clip_write( } if (!must_deselect) - SendMessage(hwnd, WM_IGNORE_CLIP, false, 0); + SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, false, 0); } static DWORD WINAPI clipboard_read_threadfunc(void *param) @@ -5545,8 +5579,10 @@ static void wintw_clip_request_paste(TermWin *tw, int clipboard) * that tells us it's OK to paste. */ DWORD in_threadid; /* required for Win9x */ - CreateThread(NULL, 0, clipboard_read_threadfunc, - hwnd, 0, &in_threadid); + HANDLE hThread = CreateThread(NULL, 0, clipboard_read_threadfunc, + wgs.term_hwnd, 0, &in_threadid); + if (hThread) + CloseHandle(hThread); /* we don't need the thread handle */ } /* @@ -5560,8 +5596,10 @@ void modalfatalbox(const char *fmt, ...) va_start(ap, fmt); message = dupvprintf(fmt, ap); va_end(ap); + show_mouseptr(true); title = dupprintf("%s Fatal Error", appname); - MessageBox(hwnd, message, title, MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); + MessageBox(wgs.term_hwnd, message, title, + MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(message); sfree(title); cleanup_exit(1); @@ -5578,8 +5616,9 @@ void nonfatal(const char *fmt, ...) va_start(ap, fmt); message = dupvprintf(fmt, ap); va_end(ap); + show_mouseptr(true); title = dupprintf("%s Error", appname); - MessageBox(hwnd, message, title, MB_ICONERROR | MB_OK); + MessageBox(wgs.term_hwnd, message, title, MB_ICONERROR | MB_OK); sfree(message); sfree(title); } @@ -5589,7 +5628,7 @@ static bool flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout) if (p_FlashWindowEx) { FLASHWINFO fi; fi.cbSize = sizeof(fi); - fi.hwnd = hwnd; + fi.hwnd = wgs.term_hwnd; fi.dwFlags = dwFlags; fi.uCount = uCount; fi.dwTimeout = dwTimeout; @@ -5628,7 +5667,7 @@ static void flash_window(int mode) if (p_FlashWindowEx) flash_window_ex(FLASHW_STOP, 0, 0); else - FlashWindow(hwnd, false); + FlashWindow(wgs.term_hwnd, false); } } else if (mode == 2) { @@ -5647,16 +5686,18 @@ static void flash_window(int mode) 0 /* system cursor blink rate */); /* No need to schedule timer */ } else { - FlashWindow(hwnd, true); - next_flash = schedule_timer(450, flash_window_timer, hwnd); + FlashWindow(wgs.term_hwnd, true); + next_flash = schedule_timer(450, flash_window_timer, + wgs.term_hwnd); } } } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) { /* maintain */ if (flashing && !p_FlashWindowEx) { - FlashWindow(hwnd, true); /* toggle */ - next_flash = schedule_timer(450, flash_window_timer, hwnd); + FlashWindow(wgs.term_hwnd, true); /* toggle */ + next_flash = schedule_timer(450, flash_window_timer, + wgs.term_hwnd); } } } @@ -5690,11 +5731,13 @@ static void wintw_bell(TermWin *tw, int mode) if (!p_PlaySound || !p_PlaySound(bell_wavefile->path, NULL, SND_ASYNC | SND_FILENAME)) { char *buf, *otherbuf; + show_mouseptr(true); buf = dupprintf( "Unable to play sound file\n%s\nUsing default sound instead", bell_wavefile->path); otherbuf = dupprintf("%s Sound Error", appname); - MessageBox(hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); + MessageBox(wgs.term_hwnd, buf, otherbuf, + MB_OK | MB_ICONEXCLAMATION); sfree(buf); sfree(otherbuf); conf_set_int(conf, CONF_beep, BELL_DEFAULT); @@ -5729,12 +5772,12 @@ static void wintw_bell(TermWin *tw, int mode) */ static void wintw_set_minimised(TermWin *tw, bool minimised) { - if (IsIconic(hwnd)) { + if (IsIconic(wgs.term_hwnd)) { if (!minimised) - ShowWindow(hwnd, SW_RESTORE); + ShowWindow(wgs.term_hwnd, SW_RESTORE); } else { if (minimised) - ShowWindow(hwnd, SW_MINIMIZE); + ShowWindow(wgs.term_hwnd, SW_MINIMIZE); } } @@ -5746,10 +5789,10 @@ static void wintw_move(TermWin *tw, int x, int y) int resize_action = conf_get_int(conf, CONF_resize_action); if (resize_action == RESIZE_DISABLED || resize_action == RESIZE_FONT || - IsZoomed(hwnd)) + IsZoomed(wgs.term_hwnd)) return; - SetWindowPos(hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); + SetWindowPos(wgs.term_hwnd, NULL, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } /* @@ -5760,7 +5803,7 @@ static void wintw_set_zorder(TermWin *tw, bool top) { if (conf_get_bool(conf, CONF_alwaysontop)) return; /* ignore */ - SetWindowPos(hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, + SetWindowPos(wgs.term_hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } @@ -5769,7 +5812,7 @@ static void wintw_set_zorder(TermWin *tw, bool top) */ static void wintw_refresh(TermWin *tw) { - InvalidateRect(hwnd, NULL, true); + InvalidateRect(wgs.term_hwnd, NULL, true); } /* @@ -5778,61 +5821,23 @@ static void wintw_refresh(TermWin *tw) */ static void wintw_set_maximised(TermWin *tw, bool maximised) { - if (IsZoomed(hwnd)) { + if (IsZoomed(wgs.term_hwnd)) { if (!maximised) - ShowWindow(hwnd, SW_RESTORE); + ShowWindow(wgs.term_hwnd, SW_RESTORE); } else { if (maximised) - ShowWindow(hwnd, SW_MAXIMIZE); + ShowWindow(wgs.term_hwnd, SW_MAXIMIZE); } } -/* - * Report whether the window is iconic, for terminal reports. - */ -static bool wintw_is_minimised(TermWin *tw) -{ - return IsIconic(hwnd); -} - -/* - * Report the window's position, for terminal reports. - */ -static void wintw_get_pos(TermWin *tw, int *x, int *y) -{ - RECT r; - GetWindowRect(hwnd, &r); - *x = r.left; - *y = r.top; -} - -/* - * Report the window's pixel size, for terminal reports. - */ -static void wintw_get_pixels(TermWin *tw, int *x, int *y) -{ - RECT r; - GetWindowRect(hwnd, &r); - *x = r.right - r.left; - *y = r.bottom - r.top; -} - -/* - * Return the window or icon title. - */ -static const char *wintw_get_title(TermWin *tw, bool icon) -{ - return icon ? icon_name : window_name; -} - /* * See if we're in full-screen mode. */ static bool is_full_screen() { - if (!IsZoomed(hwnd)) + if (!IsZoomed(wgs.term_hwnd)) return false; - if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CAPTION) + if (GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE) & WS_CAPTION) return false; return true; } @@ -5845,7 +5850,7 @@ static bool get_fullscreen_rect(RECT * ss) #if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON) HMONITOR mon; MONITORINFO mi; - mon = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + mon = MonitorFromWindow(wgs.term_hwnd, MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof(mi); GetMonitorInfo(mon, &mi); @@ -5872,26 +5877,24 @@ static void make_full_screen() DWORD style; RECT ss; - assert(IsZoomed(hwnd)); + assert(IsZoomed(wgs.term_hwnd)); if (is_full_screen()) return; /* Remove the window furniture. */ - style = GetWindowLongPtr(hwnd, GWL_STYLE); + style = GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); if (conf_get_bool(conf, CONF_scrollbar_in_fullscreen)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; - SetWindowLongPtr(hwnd, GWL_STYLE, style); + SetWindowLongPtr(wgs.term_hwnd, GWL_STYLE, style); /* Resize ourselves to exactly cover the nearest monitor. */ get_fullscreen_rect(&ss); - SetWindowPos(hwnd, HWND_TOP, ss.left, ss.top, - ss.right - ss.left, - ss.bottom - ss.top, - SWP_FRAMECHANGED); + SetWindowPos(wgs.term_hwnd, HWND_TOP, ss.left, ss.top, + ss.right - ss.left, ss.bottom - ss.top, SWP_FRAMECHANGED); /* We may have changed size as a result */ @@ -5913,7 +5916,7 @@ static void clear_full_screen() DWORD oldstyle, style; /* Reinstate the window furniture. */ - style = oldstyle = GetWindowLongPtr(hwnd, GWL_STYLE); + style = oldstyle = GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE); style |= WS_CAPTION | WS_BORDER; if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) style &= ~WS_THICKFRAME; @@ -5924,8 +5927,8 @@ static void clear_full_screen() else style &= ~WS_VSCROLL; if (style != oldstyle) { - SetWindowLongPtr(hwnd, GWL_STYLE, style); - SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SetWindowLongPtr(wgs.term_hwnd, GWL_STYLE, style); + SetWindowPos(wgs.term_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } @@ -5944,12 +5947,12 @@ static void clear_full_screen() static void flip_full_screen() { if (is_full_screen()) { - ShowWindow(hwnd, SW_RESTORE); - } else if (IsZoomed(hwnd)) { + ShowWindow(wgs.term_hwnd, SW_RESTORE); + } else if (IsZoomed(wgs.term_hwnd)) { make_full_screen(); } else { - SendMessage(hwnd, WM_FULLSCR_ON_MAX, 0, 0); - ShowWindow(hwnd, SW_MAXIMIZE); + SendMessage(wgs.term_hwnd, WM_FULLSCR_ON_MAX, 0, 0); + ShowWindow(wgs.term_hwnd, SW_MAXIMIZE); } } @@ -5974,19 +5977,23 @@ static int win_seat_get_userpass_input( return ret; } -void agent_schedule_callback(void (*callback)(void *, void *, int), - void *callback_ctx, void *data, int len) +static bool win_seat_set_trust_status(Seat *seat, bool trusted) +{ + term_set_trust_status(term, trusted); + return true; +} + +static bool win_seat_get_cursor_position(Seat *seat, int *x, int *y) { - struct agent_callback *c = snew(struct agent_callback); - c->callback = callback; - c->callback_ctx = callback_ctx; - c->data = data; - c->len = len; - PostMessage(hwnd, WM_AGENT_CALLBACK, 0, (LPARAM)c); + term_get_cursor_position(term, x, y); + return true; } -static bool win_seat_set_trust_status(Seat *seat, bool trusted) +static bool win_seat_get_window_pixel_size(Seat *seat, int *x, int *y) { - term_set_trust_status(term, trusted); + RECT r; + GetWindowRect(wgs.term_hwnd, &r); + *x = r.right - r.left; + *y = r.bottom - r.top; return true; } diff --git a/windows/wingss.c b/windows/wingss.c index 6744475..79c4921 100644 --- a/windows/wingss.c +++ b/windows/wingss.c @@ -95,6 +95,28 @@ const char *gsslogmsg = NULL; static void ssh_sspi_bind_fns(struct ssh_gss_library *lib); +static tree234 *libraries_to_never_unload; +static int library_to_never_unload_cmp(void *av, void *bv) +{ + uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv; + return a < b ? -1 : a > b ? +1 : 0; +} +static void ensure_library_tree_exists(void) +{ + if (!libraries_to_never_unload) + libraries_to_never_unload = newtree234(library_to_never_unload_cmp); +} +static bool library_is_in_never_unload_tree(HMODULE module) +{ + ensure_library_tree_exists(); + return find234(libraries_to_never_unload, module, NULL); +} +static void add_library_to_never_unload_tree(HMODULE module) +{ + ensure_library_tree_exists(); + add234(libraries_to_never_unload, module); +} + struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) { HMODULE module; @@ -145,6 +167,23 @@ struct ssh_gss_liblist *ssh_gss_setup(Conf *conf) LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_USER_DIRS); + + /* + * The MIT Kerberos DLL suffers an internal segfault + * for some reason if you unload and reload one within + * the same process. So, make sure that after we load + * this library, we never free it. + * + * Or rather: after we've loaded it once, if any + * _further_ load returns the same module handle, we + * immediately free it again (to prevent the Windows + * API's internal reference count growing without + * bound). But on the other hand we never free it in + * ssh_gss_cleanup. + */ + if (library_is_in_never_unload_tree(module)) + FreeLibrary(module); + add_library_to_never_unload_tree(module); } sfree(buffer); } @@ -280,7 +319,11 @@ void ssh_gss_cleanup(struct ssh_gss_liblist *list) * another SSH instance still using it. */ for (i = 0; i < list->nlibraries; i++) { - FreeLibrary((HMODULE)list->libraries[i].handle); + if (list->libraries[i].id != 0) { + HMODULE module = (HMODULE)list->libraries[i].handle; + if (!library_is_in_never_unload_tree(module)) + FreeLibrary(module); + } if (list->libraries[i].id == 2) { /* The 'custom' id involves a dynamically allocated message. * Note that we must cast away the 'const' to free it. */ @@ -308,7 +351,7 @@ static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib, if (host == NULL) return SSH_GSS_FAILURE; /* copy it into form host/FQDN */ - pStr = dupcat("host/", host, NULL); + pStr = dupcat("host/", host); *srv_name = (Ssh_gss_name) pStr; diff --git a/windows/winhandl.c b/windows/winhandl.c index b41cac5..82d2ade 100644 --- a/windows/winhandl.c +++ b/windows/winhandl.c @@ -201,7 +201,7 @@ static DWORD WINAPI handle_input_threadfunc(void *param) } /* - * This is called after a succcessful read, or from the + * This is called after a successful read, or from the * `unthrottle' function. It decides whether or not to begin a new * read operation. */ @@ -451,8 +451,10 @@ struct handle *handle_input_new(HANDLE handle, handle_inputfn_t gotdata, handles_by_evtomain = newtree234(handle_cmp_evtomain); add234(handles_by_evtomain, h); - CreateThread(NULL, 0, handle_input_threadfunc, - &h->u.i, 0, &in_threadid); + HANDLE hThread = CreateThread(NULL, 0, handle_input_threadfunc, + &h->u.i, 0, &in_threadid); + if (hThread) + CloseHandle(hThread); /* we don't need the thread handle */ h->u.i.busy = true; return h; @@ -482,8 +484,10 @@ struct handle *handle_output_new(HANDLE handle, handle_outputfn_t sentdata, handles_by_evtomain = newtree234(handle_cmp_evtomain); add234(handles_by_evtomain, h); - CreateThread(NULL, 0, handle_output_threadfunc, - &h->u.o, 0, &out_threadid); + HANDLE hThread = CreateThread(NULL, 0, handle_output_threadfunc, + &h->u.o, 0, &out_threadid); + if (hThread) + CloseHandle(hThread); /* we don't need the thread handle */ return h; } diff --git a/windows/winhelp.h b/windows/winhelp.h index 823a242..9011df4 100644 --- a/windows/winhelp.h +++ b/windows/winhelp.h @@ -52,6 +52,10 @@ #define WINHELP_CTX_terminal_localecho "config-localecho" #define WINHELP_CTX_terminal_localedit "config-localedit" #define WINHELP_CTX_terminal_printing "config-printing" +#define WINHELP_CTX_supdup_location "supdup-location" +#define WINHELP_CTX_supdup_ascii "supdup-ascii" +#define WINHELP_CTX_supdup_more "supdup-more" +#define WINHELP_CTX_supdup_scroll "supdup-scroll" #define WINHELP_CTX_bell_style "config-bellstyle" #define WINHELP_CTX_bell_taskbar "config-belltaskbar" #define WINHELP_CTX_bell_overload "config-bellovl" @@ -102,10 +106,12 @@ #define WINHELP_CTX_ssh_share "config-ssh-sharing" #define WINHELP_CTX_ssh_kexlist "config-ssh-kex-order" #define WINHELP_CTX_ssh_hklist "config-ssh-hostkey-order" +#define WINHELP_CTX_ssh_hk_known "config-ssh-prefer-known-hostkeys" #define WINHELP_CTX_ssh_gssapi_kex_delegation "config-ssh-kex-gssapi-delegation" #define WINHELP_CTX_ssh_kex_repeat "config-ssh-kex-rekey" #define WINHELP_CTX_ssh_kex_manual_hostkeys "config-ssh-kex-manual-hostkeys" #define WINHELP_CTX_ssh_auth_bypass "config-ssh-noauth" +#define WINHELP_CTX_ssh_no_trivial_userauth "config-ssh-notrivialauth" #define WINHELP_CTX_ssh_auth_banner "config-ssh-banner" #define WINHELP_CTX_ssh_auth_privkey "config-ssh-privkey" #define WINHELP_CTX_ssh_auth_agentfwd "config-ssh-agentfwd" @@ -167,6 +173,7 @@ #define WINHELP_CTX_pageant_keylist "pageant-mainwin-keylist" #define WINHELP_CTX_pageant_addkey "pageant-mainwin-addkey" #define WINHELP_CTX_pageant_remkey "pageant-mainwin-remkey" +#define WINHELP_CTX_pageant_deferred "pageant-deferred-decryption" #define WINHELP_CTX_pgpfingerprints "pgpkeys" #define WINHELP_CTX_puttygen_general "pubkey-puttygen" #define WINHELP_CTX_puttygen_keytype "puttygen-keytype" @@ -180,6 +187,8 @@ #define WINHELP_CTX_puttygen_pastekey "puttygen-pastekey" #define WINHELP_CTX_puttygen_load "puttygen-load" #define WINHELP_CTX_puttygen_conversions "puttygen-conversions" +#define WINHELP_CTX_puttygen_ppkver "puttygen-save-ppk-version" +#define WINHELP_CTX_puttygen_kdfparam "puttygen-save-passphrase-hashing" /* These are used in Windows-specific bits of the frontend. * We (ab)use "help context identifiers" (dwContextId) to identify them. */ diff --git a/windows/winhsock.c b/windows/winhsock.c index 4952944..543b77b 100644 --- a/windows/winhsock.c +++ b/windows/winhsock.c @@ -272,7 +272,7 @@ static SocketPeerInfo *sk_handle_peer_info(Socket *s) if (!kernel32_module) { kernel32_module = load_system32_dll("kernel32.dll"); -#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__ || defined COVERITY +#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__ /* For older Visual Studio, and MinGW too (at least as of * Ubuntu 16.04), this function isn't available in the header * files to type-check. Ditto the toolchain I use for @@ -304,14 +304,14 @@ static SocketPeerInfo *sk_handle_peer_info(Socket *s) } static const SocketVtable HandleSocket_sockvt = { - sk_handle_plug, - sk_handle_close, - sk_handle_write, - sk_handle_write_oob, - sk_handle_write_eof, - sk_handle_set_frozen, - sk_handle_socket_error, - sk_handle_peer_info, + .plug = sk_handle_plug, + .close = sk_handle_close, + .write = sk_handle_write, + .write_oob = sk_handle_write_oob, + .write_eof = sk_handle_write_eof, + .set_frozen = sk_handle_set_frozen, + .socket_error = sk_handle_socket_error, + .peer_info = sk_handle_peer_info, }; Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, diff --git a/windows/winjump.c b/windows/winjump.c index 595bbe0..358504f 100644 --- a/windows/winjump.c +++ b/windows/winjump.c @@ -432,7 +432,7 @@ static IShellLink *make_shell_link(const char *appname, * behaviour change in which an argument string starting with * '@' causes the SetArguments method to silently do the wrong * thing. */ - param_string = dupcat(" @", sessionname, NULL); + param_string = dupcat(" @", sessionname); } else { param_string = dupstr(""); } @@ -440,8 +440,7 @@ static IShellLink *make_shell_link(const char *appname, sfree(param_string); if (sessionname) { - desc_string = dupcat("Connect to PuTTY session '", - sessionname, "'", NULL); + desc_string = dupcat("Connect to PuTTY session '", sessionname, "'"); } else { assert(appname); desc_string = dupprintf("Run %.*s", diff --git a/windows/winmisc.c b/windows/winmisc.c index 9ccb65f..759df01 100644 --- a/windows/winmisc.c +++ b/windows/winmisc.c @@ -157,11 +157,9 @@ void dll_hijacking_protection(void) if (!kernel32_module) { kernel32_module = load_system32_dll("kernel32.dll"); -#if (defined _MSC_VER && _MSC_VER < 1900) || defined COVERITY - /* For older Visual Studio, and also for the system I - * currently use for Coveritying the Windows code, this - * function isn't available in the header files to - * type-check */ +#if (defined _MSC_VER && _MSC_VER < 1900) + /* For older Visual Studio, this function isn't available in + * the header files to type-check */ GET_WINDOWS_FUNCTION_NO_TYPECHECK( kernel32_module, SetDefaultDllDirectories); #else @@ -233,7 +231,7 @@ HMODULE load_system32_dll(const char *libname) sgrowarray(sysdir, sysdirsize, len); } - fullpath = dupcat(sysdir, "\\", libname, NULL); + fullpath = dupcat(sysdir, "\\", libname); ret = LoadLibrary(fullpath); sfree(fullpath); return ret; diff --git a/windows/winmiscs.c b/windows/winmiscs.c index 73e4f86..571a912 100644 --- a/windows/winmiscs.c +++ b/windows/winmiscs.c @@ -266,6 +266,14 @@ bool platform_sha1_hw_available(void) return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); } +bool platform_sha512_hw_available(void) +{ + /* As of 2020-12-24, as far as I can tell from docs.microsoft.com, + * Windows on Arm does not yet provide a PF_ARM_V8_* flag for the + * SHA-512 architecture extension. */ + return false; +} + #endif bool is_console_handle(HANDLE handle) diff --git a/windows/winnet.c b/windows/winnet.c index ab38e54..3b4da3c 100644 --- a/windows/winnet.c +++ b/windows/winnet.c @@ -16,6 +16,7 @@ #include "putty.h" #include "network.h" #include "tree234.h" +#include "ssh.h" #include @@ -223,12 +224,11 @@ static bool sk_startup(int hi, int lo) return true; } -/* Actually define this function pointer, which won't have been - * defined alongside all the others by PUTTY_DO_GLOBALS because of the - * annoying winelib header-ordering issue. (See comment in winstuff.h.) */ -DECL_WINDOWS_FUNCTION(/* empty */, int, select, - (int, fd_set FAR *, fd_set FAR *, - fd_set FAR *, const struct timeval FAR *)); +DEF_WINDOWS_FUNCTION(WSAAsyncSelect); +DEF_WINDOWS_FUNCTION(WSAEventSelect); +DEF_WINDOWS_FUNCTION(WSAGetLastError); +DEF_WINDOWS_FUNCTION(WSAEnumNetworkEvents); +DEF_WINDOWS_FUNCTION(select); void sk_init(void) { @@ -279,32 +279,21 @@ void sk_init(void) GET_WINDOWS_FUNCTION(winsock_module, WSAStartup); GET_WINDOWS_FUNCTION(winsock_module, WSACleanup); GET_WINDOWS_FUNCTION(winsock_module, closesocket); -#ifndef COVERITY GET_WINDOWS_FUNCTION(winsock_module, ntohl); GET_WINDOWS_FUNCTION(winsock_module, htonl); GET_WINDOWS_FUNCTION(winsock_module, htons); GET_WINDOWS_FUNCTION(winsock_module, ntohs); GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname); -#else - /* The toolchain I use for Windows Coverity builds doesn't know - * the type signatures of these */ - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, ntohl); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, htonl); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, htons); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, ntohs); - GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname); -#endif GET_WINDOWS_FUNCTION(winsock_module, gethostbyname); GET_WINDOWS_FUNCTION(winsock_module, getservbyname); GET_WINDOWS_FUNCTION(winsock_module, inet_addr); GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa); -#if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__ /* Older Visual Studio, and MinGW as of Ubuntu 16.04, don't know - * about this function at all, so can't type-check it */ + * about this function at all, so can't type-check it. Also there + * seems to be some disagreement in the VS headers about whether + * the second argument is void * or const void *, so I omit the + * type check. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, inet_ntop); -#else - GET_WINDOWS_FUNCTION(winsock_module, inet_ntop); -#endif GET_WINDOWS_FUNCTION(winsock_module, connect); GET_WINDOWS_FUNCTION(winsock_module, bind); GET_WINDOWS_FUNCTION(winsock_module, setsockopt); @@ -551,8 +540,7 @@ SockAddr *sk_namelookup(const char *host, char **canonicalname, strncpy(realhost, host, sizeof(realhost)); } realhost[lenof(realhost)-1] = '\0'; - *canonicalname = snewn(1+strlen(realhost), char); - strcpy(*canonicalname, realhost); + *canonicalname = dupstr(realhost); return ret; } @@ -832,20 +820,20 @@ static const char *sk_net_socket_error(Socket *s); static SocketPeerInfo *sk_net_peer_info(Socket *s); static const SocketVtable NetSocket_sockvt = { - sk_net_plug, - sk_net_close, - sk_net_write, - sk_net_write_oob, - sk_net_write_eof, - sk_net_set_frozen, - sk_net_socket_error, - sk_net_peer_info, + .plug = sk_net_plug, + .close = sk_net_close, + .write = sk_net_write, + .write_oob = sk_net_write_oob, + .write_eof = sk_net_write_eof, + .set_frozen = sk_net_set_frozen, + .socket_error = sk_net_socket_error, + .peer_info = sk_net_peer_info, }; static Socket *sk_net_accept(accept_ctx_t ctx, Plug *plug) { DWORD err; - char *errstr; + const char *errstr; NetSocket *ret; /* @@ -897,7 +885,7 @@ static DWORD try_connect(NetSocket *sock) #endif SOCKADDR_IN a; DWORD err; - char *errstr; + const char *errstr; short localport; int family; @@ -909,7 +897,8 @@ static DWORD try_connect(NetSocket *sock) { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); - plug_log(sock->plug, 0, &thisaddr, sock->port, NULL, 0); + plug_log(sock->plug, PLUGLOG_CONNECT_TRYING, + &thisaddr, sock->port, NULL, 0); } /* @@ -1068,6 +1057,9 @@ static DWORD try_connect(NetSocket *sock) * and we should set the socket as writable. */ sock->writable = true; + SockAddr thisaddr = sk_extractaddr_tmp(sock->addr, &sock->step); + plug_log(sock->plug, PLUGLOG_CONNECT_SUCCESS, + &thisaddr, sock->port, NULL, 0); } err = 0; @@ -1082,7 +1074,8 @@ static DWORD try_connect(NetSocket *sock) if (err) { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); - plug_log(sock->plug, 1, &thisaddr, sock->port, sock->error, err); + plug_log(sock->plug, PLUGLOG_CONNECT_FAILED, + &thisaddr, sock->port, sock->error, err); } return err; } @@ -1137,7 +1130,7 @@ Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug, SOCKADDR_IN a; DWORD err; - char *errstr; + const char *errstr; NetSocket *ret; int retcode; @@ -1528,7 +1521,7 @@ void select_result(WPARAM wParam, LPARAM lParam) if (s->addr) { SockAddr thisaddr = sk_extractaddr_tmp( s->addr, &s->step); - plug_log(s->plug, 1, &thisaddr, s->port, + plug_log(s->plug, PLUGLOG_CONNECT_FAILED, &thisaddr, s->port, winsock_error_string(err), err); while (err && s->addr && sk_nextaddr(s->addr, &s->step)) { err = try_connect(s); @@ -1545,12 +1538,18 @@ void select_result(WPARAM wParam, LPARAM lParam) case FD_CONNECT: s->connected = true; s->writable = true; + /* - * Once a socket is connected, we can stop falling - * back through the candidate addresses to connect - * to. + * Once a socket is connected, we can stop falling back + * through the candidate addresses to connect to. But first, + * let the plug know we were successful. */ if (s->addr) { + SockAddr thisaddr = sk_extractaddr_tmp( + s->addr, &s->step); + plug_log(s->plug, PLUGLOG_CONNECT_SUCCESS, + &thisaddr, s->port, NULL, 0); + sk_addr_free(s->addr); s->addr = NULL; } @@ -1614,17 +1613,16 @@ void select_result(WPARAM wParam, LPARAM lParam) plug_receive(s->plug, 2, buf, ret); } break; - case FD_WRITE: - { - int bufsize_before, bufsize_after; - s->writable = true; - bufsize_before = s->sending_oob + bufchain_size(&s->output_data); - try_send(s); - bufsize_after = s->sending_oob + bufchain_size(&s->output_data); - if (bufsize_after < bufsize_before) - plug_sent(s->plug, bufsize_after); - } + case FD_WRITE: { + int bufsize_before, bufsize_after; + s->writable = true; + bufsize_before = s->sending_oob + bufchain_size(&s->output_data); + try_send(s); + bufsize_after = s->sending_oob + bufchain_size(&s->output_data); + if (bufsize_after < bufsize_before) + plug_sent(s->plug, bufsize_after); break; + } case FD_CLOSE: /* Signal a close on the socket. First read any outstanding data. */ do { @@ -1642,42 +1640,42 @@ void select_result(WPARAM wParam, LPARAM lParam) } } while (ret > 0); return; - case FD_ACCEPT: - { + case FD_ACCEPT: { #ifdef NO_IPV6 - struct sockaddr_in isa; + struct sockaddr_in isa; #else - struct sockaddr_storage isa; + struct sockaddr_storage isa; #endif - int addrlen = sizeof(isa); - SOCKET t; /* socket of connection */ - accept_ctx_t actx; - - memset(&isa, 0, sizeof(isa)); - err = 0; - t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen); - if (t == INVALID_SOCKET) - { - err = p_WSAGetLastError(); - if (err == WSATRY_AGAIN) - break; - } + int addrlen = sizeof(isa); + SOCKET t; /* socket of connection */ + accept_ctx_t actx; + + memset(&isa, 0, sizeof(isa)); + err = 0; + t = p_accept(s->s,(struct sockaddr *)&isa,&addrlen); + if (t == INVALID_SOCKET) + { + err = p_WSAGetLastError(); + if (err == WSATRY_AGAIN) + break; + } - actx.p = (void *)t; + actx.p = (void *)t; #ifndef NO_IPV6 - if (isa.ss_family == AF_INET && - s->localhost_only && - !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) + if (isa.ss_family == AF_INET && + s->localhost_only && + !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) #else - if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) + if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) #endif - { - p_closesocket(t); /* dodgy WinSock let nonlocal through */ - } else if (plug_accepting(s->plug, sk_net_accept, actx)) { - p_closesocket(t); /* denied or error */ - } + { + p_closesocket(t); /* dodgy WinSock let nonlocal through */ + } else if (plug_accepting(s->plug, sk_net_accept, actx)) { + p_closesocket(t); /* denied or error */ } + break; + } } } @@ -1817,8 +1815,7 @@ char *get_hostname(void) return dupstr(hostbuf); } -SockAddr *platform_get_x11_unix_address(const char *display, int displaynum, - char **canonicalname) +SockAddr *platform_get_x11_unix_address(const char *display, int displaynum) { SockAddr *ret = snew(SockAddr); memset(ret, 0, sizeof(SockAddr)); diff --git a/windows/winnpc.c b/windows/winnpc.c index 4fcb9e2..eabfb4b 100644 --- a/windows/winnpc.c +++ b/windows/winnpc.c @@ -15,7 +15,7 @@ #include "winsecur.h" -Socket *new_named_pipe_client(const char *pipename, Plug *plug) +HANDLE connect_to_named_pipe(const char *pipename, char **err) { HANDLE pipehandle; PSID usersid, pipeowner; @@ -33,9 +33,10 @@ Socket *new_named_pipe_client(const char *pipename, Plug *plug) break; if (GetLastError() != ERROR_PIPE_BUSY) { - return new_error_socket_fmt( - plug, "Unable to open named pipe '%s': %s", + *err = dupprintf( + "Unable to open named pipe '%s': %s", pipename, win_strerror(GetLastError())); + return INVALID_HANDLE_VALUE; } /* @@ -46,16 +47,18 @@ Socket *new_named_pipe_client(const char *pipename, Plug *plug) * take excessively long.) */ if (!WaitNamedPipe(pipename, NMPWAIT_USE_DEFAULT_WAIT)) { - return new_error_socket_fmt( - plug, "Error waiting for named pipe '%s': %s", + *err = dupprintf( + "Error waiting for named pipe '%s': %s", pipename, win_strerror(GetLastError())); + return INVALID_HANDLE_VALUE; } } if ((usersid = get_user_sid()) == NULL) { CloseHandle(pipehandle); - return new_error_socket_fmt( - plug, "Unable to get user SID: %s", win_strerror(GetLastError())); + *err = dupprintf( + "Unable to get user SID: %s", win_strerror(GetLastError())); + return INVALID_HANDLE_VALUE; } if (p_GetSecurityInfo(pipehandle, SE_KERNEL_OBJECT, @@ -63,21 +66,33 @@ Socket *new_named_pipe_client(const char *pipename, Plug *plug) &pipeowner, NULL, NULL, NULL, &psd) != ERROR_SUCCESS) { CloseHandle(pipehandle); - return new_error_socket_fmt( - plug, "Unable to get named pipe security information: %s", + *err = dupprintf( + "Unable to get named pipe security information: %s", win_strerror(GetLastError())); + return INVALID_HANDLE_VALUE; } if (!EqualSid(pipeowner, usersid)) { CloseHandle(pipehandle); LocalFree(psd); - return new_error_socket_fmt( - plug, "Owner of named pipe '%s' is not us", pipename); + *err = dupprintf( + "Owner of named pipe '%s' is not us", pipename); + return INVALID_HANDLE_VALUE; } LocalFree(psd); - return make_handle_socket(pipehandle, pipehandle, NULL, plug, true); + return pipehandle; +} + +Socket *new_named_pipe_client(const char *pipename, Plug *plug) +{ + char *err = NULL; + HANDLE pipehandle = connect_to_named_pipe(pipename, &err); + if (pipehandle == INVALID_HANDLE_VALUE) + return new_error_socket_consume_string(plug, err); + else + return make_handle_socket(pipehandle, pipehandle, NULL, plug, true); } #endif /* !defined NO_SECURITY */ diff --git a/windows/winnps.c b/windows/winnps.c index 36e24d2..1757cdb 100644 --- a/windows/winnps.c +++ b/windows/winnps.c @@ -195,14 +195,10 @@ static void named_pipe_connect_callback(void *vps) * be asked to write or set_frozen. */ static const SocketVtable NamedPipeServerSocket_sockvt = { - sk_namedpipeserver_plug, - sk_namedpipeserver_close, - NULL /* write */, - NULL /* write_oob */, - NULL /* write_eof */, - NULL /* set_frozen */, - sk_namedpipeserver_socket_error, - sk_namedpipeserver_peer_info, + .plug = sk_namedpipeserver_plug, + .close = sk_namedpipeserver_close, + .socket_error = sk_namedpipeserver_socket_error, + .peer_info = sk_namedpipeserver_peer_info, }; Socket *new_named_pipe_listener(const char *pipename, Plug *plug) diff --git a/windows/winpgen.c b/windows/winpgen.c index 353caa0..56c6a8d 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -7,12 +7,12 @@ #include #include -#define PUTTY_DO_GLOBALS - #include "putty.h" #include "ssh.h" +#include "sshkeygen.h" #include "licence.h" #include "winsecur.h" +#include "puttygen-rc.h" #include @@ -23,7 +23,8 @@ #define WM_DONEKEY (WM_APP + 1) #define DEFAULT_KEY_BITS 2048 -#define DEFAULT_CURVE_INDEX 0 +#define DEFAULT_ECCURVE_INDEX 0 +#define DEFAULT_EDCURVE_INDEX 0 static char *cmdline_keyfile = NULL; @@ -61,77 +62,134 @@ void nonfatal(const char *fmt, ...) } /* ---------------------------------------------------------------------- - * Progress report code. This is really horrible :-) + * ProgressReceiver implementation. */ + #define PROGRESSRANGE 65535 -#define MAXPHASE 5 + +struct progressphase { + double startpoint, total; + /* For exponential phases */ + double exp_probability, exp_current_value; +}; + struct progress { - int nphases; - struct { - bool exponential; - unsigned startpoint, total; - unsigned param, current, n; /* if exponential */ - unsigned mult; /* if linear */ - } phases[MAXPHASE]; - unsigned total, divisor, range; + size_t nphases, phasessize; + struct progressphase *phases, *currphase; + + double scale; HWND progbar; + + ProgressReceiver rec; }; -static void progress_update(void *param, int action, int phase, int iprogress) +static ProgressPhase win_progress_add_linear( + ProgressReceiver *prog, double overall_cost) { + struct progress *p = container_of(prog, struct progress, rec); + + sgrowarray(p->phases, p->phasessize, p->nphases); + int phase = p->nphases++; + + p->phases[phase].total = overall_cost; + + ProgressPhase ph = { .n = phase }; + return ph; +} + +static ProgressPhase win_progress_add_probabilistic( + ProgressReceiver *prog, double cost_per_attempt, double probability) { + struct progress *p = container_of(prog, struct progress, rec); + + sgrowarray(p->phases, p->phasessize, p->nphases); + int phase = p->nphases++; + + p->phases[phase].exp_probability = 1.0 - probability; + p->phases[phase].exp_current_value = 1.0; + /* Expected number of attempts = 1 / probability of attempt succeeding */ + p->phases[phase].total = cost_per_attempt / probability; + + ProgressPhase ph = { .n = phase }; + return ph; +} + +static void win_progress_ready(ProgressReceiver *prog) { - struct progress *p = (struct progress *) param; - unsigned progress = iprogress; - int position; - - if (action < PROGFN_READY && p->nphases < phase) - p->nphases = phase; - switch (action) { - case PROGFN_INITIALISE: - p->nphases = 0; - break; - case PROGFN_LIN_PHASE: - p->phases[phase-1].exponential = false; - p->phases[phase-1].mult = p->phases[phase].total / progress; - break; - case PROGFN_EXP_PHASE: - p->phases[phase-1].exponential = true; - p->phases[phase-1].param = 0x10000 + progress; - p->phases[phase-1].current = p->phases[phase-1].total; - p->phases[phase-1].n = 0; - break; - case PROGFN_PHASE_EXTENT: - p->phases[phase-1].total = progress; - break; - case PROGFN_READY: - { - unsigned total = 0; - int i; - for (i = 0; i < p->nphases; i++) { - p->phases[i].startpoint = total; - total += p->phases[i].total; - } - p->total = total; - p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE); - p->range = p->total / p->divisor; - SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range)); - } - break; - case PROGFN_PROGRESS: - if (p->phases[phase-1].exponential) { - while (p->phases[phase-1].n < progress) { - p->phases[phase-1].n++; - p->phases[phase-1].current *= p->phases[phase-1].param; - p->phases[phase-1].current /= 0x10000; - } - position = (p->phases[phase-1].startpoint + - p->phases[phase-1].total - p->phases[phase-1].current); - } else { - position = (p->phases[phase-1].startpoint + - progress * p->phases[phase-1].mult); - } - SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0); - break; + struct progress *p = container_of(prog, struct progress, rec); + + double total = 0; + for (int i = 0; i < p->nphases; i++) { + p->phases[i].startpoint = total; + total += p->phases[i].total; } + p->scale = PROGRESSRANGE / total; + + SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); +} + +static void win_progress_start_phase(ProgressReceiver *prog, + ProgressPhase phase) +{ + struct progress *p = container_of(prog, struct progress, rec); + + assert(phase.n < p->nphases); + p->currphase = &p->phases[phase.n]; +} + +static void win_progress_update(struct progress *p, double phasepos) +{ + double position = (p->currphase->startpoint + + p->currphase->total * phasepos); + position *= p->scale; + if (position < 0) + position = 0; + if (position > PROGRESSRANGE) + position = PROGRESSRANGE; + + SendMessage(p->progbar, PBM_SETPOS, (WPARAM)position, 0); +} + +static void win_progress_report(ProgressReceiver *prog, double progress) +{ + struct progress *p = container_of(prog, struct progress, rec); + + win_progress_update(p, progress); +} + +static void win_progress_report_attempt(ProgressReceiver *prog) +{ + struct progress *p = container_of(prog, struct progress, rec); + + p->currphase->exp_current_value *= p->currphase->exp_probability; + win_progress_update(p, 1.0 - p->currphase->exp_current_value); +} + +static void win_progress_report_phase_complete(ProgressReceiver *prog) +{ + struct progress *p = container_of(prog, struct progress, rec); + + win_progress_update(p, 1.0); +} + +static const ProgressReceiverVtable win_progress_vt = { + .add_linear = win_progress_add_linear, + .add_probabilistic = win_progress_add_probabilistic, + .ready = win_progress_ready, + .start_phase = win_progress_start_phase, + .report = win_progress_report, + .report_attempt = win_progress_report_attempt, + .report_phase_complete = win_progress_report_phase_complete, +}; + +static void win_progress_initialise(struct progress *p) +{ + p->nphases = p->phasessize = 0; + p->phases = p->currphase = NULL; + p->rec.vt = &win_progress_vt; +} + +static void win_progress_cleanup(struct progress *p) +{ + sfree(p->phases); } struct PassphraseProcStruct { @@ -203,6 +261,194 @@ static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg, return 0; } +static void try_get_dlg_item_uint32(HWND hwnd, int id, uint32_t *out) +{ + char buf[128]; + if (!GetDlgItemText(hwnd, id, buf, sizeof(buf))) + return; + + if (!*buf) + return; + + char *end; + unsigned long val = strtoul(buf, &end, 10); + if (*end) + return; + + if ((val >> 16) >> 16) + return; + + *out = val; +} + +static ppk_save_parameters save_params; + +struct PPKParams { + ppk_save_parameters params; + uint32_t time_passes, time_ms; +}; + +/* + * Dialog-box function for the passphrase box. + */ +static INT_PTR CALLBACK PPKParamsProc(HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + struct PPKParams *pp; + char *buf; + + if (msg == WM_INITDIALOG) { + pp = (struct PPKParams *)lParam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pp); + } else { + pp = (struct PPKParams *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + } + + switch (msg) { + case WM_INITDIALOG: + SetForegroundWindow(hwnd); + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); + + if (has_help()) + SetWindowLongPtr(hwnd, GWL_EXSTYLE, + GetWindowLongPtr(hwnd, GWL_EXSTYLE) | + WS_EX_CONTEXTHELP); + + /* + * Centre the window. + */ + { /* centre the window */ + RECT rs, rd; + HWND hw; + + hw = GetDesktopWindow(); + if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) + MoveWindow(hwnd, + (rs.right + rs.left + rd.left - rd.right) / 2, + (rs.bottom + rs.top + rd.top - rd.bottom) / 2, + rd.right - rd.left, rd.bottom - rd.top, true); + } + + CheckRadioButton(hwnd, IDC_PPKVER_2, IDC_PPKVER_3, + IDC_PPKVER_2 + (pp->params.fmt_version - 2)); + + CheckRadioButton( + hwnd, IDC_KDF_ARGON2ID, IDC_KDF_ARGON2D, + (pp->params.argon2_flavour == Argon2id ? IDC_KDF_ARGON2ID : + pp->params.argon2_flavour == Argon2i ? IDC_KDF_ARGON2I : + /* pp->params.argon2_flavour == Argon2d ? */ IDC_KDF_ARGON2D)); + + buf = dupprintf("%"PRIu32, pp->params.argon2_mem); + SetDlgItemText(hwnd, IDC_ARGON2_MEM, buf); + sfree(buf); + + if (pp->params.argon2_passes_auto) { + CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO, + IDC_PPK_AUTO_YES); + buf = dupprintf("%"PRIu32, pp->time_ms); + SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); + sfree(buf); + } else { + CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO, + IDC_PPK_AUTO_NO); + buf = dupprintf("%"PRIu32, pp->time_passes); + SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); + sfree(buf); + } + + buf = dupprintf("%"PRIu32, pp->params.argon2_parallelism); + SetDlgItemText(hwnd, IDC_ARGON2_PARALLEL, buf); + sfree(buf); + + return 0; + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hwnd, 1); + return 0; + case IDCANCEL: + EndDialog(hwnd, 0); + return 0; + case IDC_PPKVER_2: + pp->params.fmt_version = 2; + return 0; + case IDC_PPKVER_3: + pp->params.fmt_version = 3; + return 0; + case IDC_KDF_ARGON2ID: + pp->params.argon2_flavour = Argon2id; + return 0; + case IDC_KDF_ARGON2I: + pp->params.argon2_flavour = Argon2i; + return 0; + case IDC_KDF_ARGON2D: + pp->params.argon2_flavour = Argon2d; + return 0; + case IDC_ARGON2_MEM: + try_get_dlg_item_uint32(hwnd, IDC_ARGON2_MEM, + &pp->params.argon2_mem); + return 0; + case IDC_PPK_AUTO_YES: + pp->params.argon2_passes_auto = true; + buf = dupprintf("%"PRIu32, pp->time_ms); + SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); + sfree(buf); + return 0; + case IDC_PPK_AUTO_NO: + pp->params.argon2_passes_auto = false; + buf = dupprintf("%"PRIu32, pp->time_passes); + SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); + sfree(buf); + return 0; + case IDC_ARGON2_TIME: + try_get_dlg_item_uint32(hwnd, IDC_ARGON2_TIME, + pp->params.argon2_passes_auto ? + &pp->time_ms : &pp->time_passes); + return 0; + case IDC_ARGON2_PARALLEL: + try_get_dlg_item_uint32(hwnd, IDC_ARGON2_PARALLEL, + &pp->params.argon2_parallelism); + return 0; + } + return 0; + case WM_HELP: { + int id = ((LPHELPINFO)lParam)->iCtrlId; + const char *topic = NULL; + switch (id) { + case IDC_PPKVER_STATIC: + case IDC_PPKVER_2: + case IDC_PPKVER_3: + topic = WINHELP_CTX_puttygen_ppkver; break; + case IDC_KDF_STATIC: + case IDC_KDF_ARGON2ID: + case IDC_KDF_ARGON2I: + case IDC_KDF_ARGON2D: + case IDC_ARGON2_MEM_STATIC: + case IDC_ARGON2_MEM: + case IDC_ARGON2_MEM_STATIC2: + case IDC_ARGON2_TIME_STATIC: + case IDC_ARGON2_TIME: + case IDC_PPK_AUTO_YES: + case IDC_PPK_AUTO_NO: + case IDC_ARGON2_PARALLEL_STATIC: + case IDC_ARGON2_PARALLEL: + topic = WINHELP_CTX_puttygen_kdfparam; break; + } + if (topic) { + launch_help(hwnd, topic); + } else { + MessageBeep(0); + } + break; + } + case WM_CLOSE: + EndDialog(hwnd, 0); + return 0; + } + return 0; +} + /* * Prompt for a key file. Assumes the filename buffer is of size * FILENAME_MAX. @@ -238,24 +484,23 @@ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - case WM_INITDIALOG: + case WM_INITDIALOG: { /* * Centre the window. */ - { /* centre the window */ - RECT rs, rd; - HWND hw; + RECT rs, rd; + HWND hw; - hw = GetDesktopWindow(); - if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) - MoveWindow(hwnd, - (rs.right + rs.left + rd.left - rd.right) / 2, - (rs.bottom + rs.top + rd.top - rd.bottom) / 2, - rd.right - rd.left, rd.bottom - rd.top, true); - } + hw = GetDesktopWindow(); + if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) + MoveWindow(hwnd, + (rs.right + rs.left + rd.left - rd.right) / 2, + (rs.bottom + rs.top + rd.top - rd.bottom) / 2, + rd.right - rd.left, rd.bottom - rd.top, true); SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n")); return 1; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: @@ -302,6 +547,7 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); sfree(buildinfo_text); SetDlgItemText(hwnd, 1000, text); + MakeDlgItemBorderless(hwnd, 1000); sfree(text); } return 1; @@ -332,7 +578,7 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, return 0; } -typedef enum {RSA, DSA, ECDSA, ED25519} keytype; +typedef enum {RSA, DSA, ECDSA, EDDSA} keytype; /* * Thread to generate a key. @@ -343,6 +589,8 @@ struct rsa_key_thread_params { int key_bits; /* bits in key modulus (RSA, DSA) */ int curve_bits; /* bits in elliptic curve (ECDSA) */ keytype keytype; + const PrimeGenerationPolicy *primepolicy; + bool rsa_strong; union { RSAKey *key; struct dss_key *dsskey; @@ -357,20 +605,26 @@ static DWORD WINAPI generate_key_thread(void *param) struct progress prog; prog.progbar = params->progressbar; - progress_update(&prog, PROGFN_INITIALISE, 0, 0); + win_progress_initialise(&prog); + + PrimeGenerationContext *pgc = primegen_new_context(params->primepolicy); if (params->keytype == DSA) - dsa_generate(params->dsskey, params->key_bits, progress_update, &prog); + dsa_generate(params->dsskey, params->key_bits, pgc, &prog.rec); else if (params->keytype == ECDSA) - ecdsa_generate(params->eckey, params->curve_bits, - progress_update, &prog); - else if (params->keytype == ED25519) - eddsa_generate(params->edkey, 256, progress_update, &prog); + ecdsa_generate(params->eckey, params->curve_bits); + else if (params->keytype == EDDSA) + eddsa_generate(params->edkey, params->curve_bits); else - rsa_generate(params->key, params->key_bits, progress_update, &prog); + rsa_generate(params->key, params->key_bits, params->rsa_strong, + pgc, &prog.rec); + + primegen_free_context(pgc); PostMessage(params->dialog, WM_DONEKEY, 0, 0); + win_progress_cleanup(&prog); + sfree(params); return 0; } @@ -383,6 +637,9 @@ struct MainDlgState { int key_bits, curve_bits; bool ssh2; keytype keytype; + const PrimeGenerationPolicy *primepolicy; + bool rsa_strong; + FingerprintType fptype; char **commentptr; /* points to key.comment or ssh2key.comment */ ssh2_userkey ssh2key; unsigned *entropy; @@ -460,9 +717,14 @@ enum { IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB, IDC_BOX_PARAMS, IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA, - IDC_KEYSSH2ECDSA, IDC_KEYSSH2ED25519, + IDC_KEYSSH2ECDSA, IDC_KEYSSH2EDDSA, + IDC_PRIMEGEN_PROB, IDC_PRIMEGEN_MAURER_SIMPLE, IDC_PRIMEGEN_MAURER_COMPLEX, + IDC_RSA_STRONG, + IDC_FPTYPE_SHA256, IDC_FPTYPE_MD5, + IDC_PPK_PARAMS, IDC_BITSSTATIC, IDC_BITS, - IDC_CURVESTATIC, IDC_CURVE, + IDC_ECCURVESTATIC, IDC_ECCURVE, + IDC_EDCURVESTATIC, IDC_EDCURVE, IDC_NOTHINGSTATIC, IDC_ABOUT, IDC_GIVEHELP, @@ -502,7 +764,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1); + EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); @@ -513,7 +775,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519, + EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO, @@ -535,7 +797,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 0); + EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0); EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); @@ -546,7 +808,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, MF_GRAYED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519, + EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO, @@ -568,7 +830,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); - EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ED25519), 1); + EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND); @@ -579,7 +841,7 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, MF_ENABLED|MF_BYCOMMAND); - EnableMenuItem(state->keymenu, IDC_KEYSSH2ED25519, + EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); /* @@ -605,12 +867,15 @@ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) */ void ui_update_key_type_ctrls(HWND hwnd) { - enum { BITS, CURVE, NOTHING } which; + enum { BITS, ECCURVE, EDCURVE, NOTHING } which; static const int bits_ids[] = { IDC_BITSSTATIC, IDC_BITS, 0 }; - static const int curve_ids[] = { - IDC_CURVESTATIC, IDC_CURVE, 0 + static const int eccurve_ids[] = { + IDC_ECCURVESTATIC, IDC_ECCURVE, 0 + }; + static const int edcurve_ids[] = { + IDC_EDCURVESTATIC, IDC_EDCURVE, 0 }; static const int nothing_ids[] = { IDC_NOTHINGSTATIC, 0 @@ -621,23 +886,85 @@ void ui_update_key_type_ctrls(HWND hwnd) IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { which = BITS; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { - which = CURVE; + which = ECCURVE; + } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) { + which = EDCURVE; } else { - /* ED25519 implicitly only supports one curve */ + /* Currently not used since Ed25519 stopped being the only + * thing in its class, but I'll keep it here in case it comes + * in useful again */ which = NOTHING; } hidemany(hwnd, bits_ids, which != BITS); - hidemany(hwnd, curve_ids, which != CURVE); + hidemany(hwnd, eccurve_ids, which != ECCURVE); + hidemany(hwnd, edcurve_ids, which != EDCURVE); hidemany(hwnd, nothing_ids, which != NOTHING); } void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button) { - CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2ED25519, button); - CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2ED25519, + CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, button); + CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, button, MF_BYCOMMAND); ui_update_key_type_ctrls(hwnd); } +void ui_set_primepolicy(HWND hwnd, struct MainDlgState *state, int option) +{ + CheckMenuRadioItem(state->keymenu, IDC_PRIMEGEN_PROB, + IDC_PRIMEGEN_MAURER_COMPLEX, option, MF_BYCOMMAND); + switch (option) { + case IDC_PRIMEGEN_PROB: + state->primepolicy = &primegen_probabilistic; + break; + case IDC_PRIMEGEN_MAURER_SIMPLE: + state->primepolicy = &primegen_provable_maurer_simple; + break; + case IDC_PRIMEGEN_MAURER_COMPLEX: + state->primepolicy = &primegen_provable_maurer_complex; + break; + } +} +void ui_set_rsa_strong(HWND hwnd, struct MainDlgState *state, bool enable) +{ + state->rsa_strong = enable; + CheckMenuItem(state->keymenu, IDC_RSA_STRONG, + (enable ? MF_CHECKED : 0) | MF_BYCOMMAND); +} +static FingerprintType idc_to_fptype(int option) +{ + switch (option) { + case IDC_FPTYPE_SHA256: + return SSH_FPTYPE_SHA256; + case IDC_FPTYPE_MD5: + return SSH_FPTYPE_MD5; + default: + unreachable("bad control id in idc_to_fptype"); + } +} +static int fptype_to_idc(FingerprintType fptype) +{ + switch (fptype) { + case SSH_FPTYPE_SHA256: + return IDC_FPTYPE_SHA256; + case SSH_FPTYPE_MD5: + return IDC_FPTYPE_MD5; + default: + unreachable("bad fptype in fptype_to_idc"); + } +} +void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option) +{ + CheckMenuRadioItem(state->keymenu, IDC_FPTYPE_SHA256, + IDC_FPTYPE_MD5, option, MF_BYCOMMAND); + + state->fptype = idc_to_fptype(option); + + if (state->key_exists && state->ssh2) { + char *fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); + SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); + sfree(fp); + } +} void load_key_file(HWND hwnd, struct MainDlgState *state, Filename *filename, bool was_import_cmd) @@ -657,7 +984,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, !import_possible(type)) { char *msg = dupprintf("Couldn't load private key (%s)", key_type_to_str(type)); - message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, + message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); return; @@ -672,9 +999,9 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, comment = NULL; passphrase = NULL; if (realtype == SSH_KEYTYPE_SSH1) - needs_pass = rsa_ssh1_encrypted(filename, &comment); + needs_pass = rsa1_encrypted_f(filename, &comment); else if (realtype == SSH_KEYTYPE_SSH2) - needs_pass = ssh2_userkey_encrypted(filename, &comment); + needs_pass = ppk_encrypted_f(filename, &comment); else needs_pass = import_encrypted(filename, realtype, &comment); do { @@ -699,14 +1026,13 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, passphrase = dupstr(""); if (type == SSH_KEYTYPE_SSH1) { if (realtype == type) - ret = rsa_ssh1_loadkey( - filename, &newkey1, passphrase, &errmsg); + ret = rsa1_load_f(filename, &newkey1, passphrase, &errmsg); else ret = import_ssh1(filename, realtype, &newkey1, passphrase, &errmsg); } else { if (realtype == type) - newkey2 = ssh2_load_userkey(filename, passphrase, &errmsg); + newkey2 = ppk_load_f(filename, passphrase, &errmsg); else newkey2 = import_ssh2(filename, realtype, passphrase, &errmsg); if (newkey2 == SSH2_WRONG_PASSPHRASE) @@ -721,7 +1047,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, sfree(comment); if (ret == 0) { char *msg = dupprintf("Couldn't load private key (%s)", errmsg); - message_box(msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, + message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); } else if (ret == 1) { @@ -770,7 +1096,7 @@ void load_key_file(HWND hwnd, struct MainDlgState *state, savecomment = state->ssh2key.comment; state->ssh2key.comment = NULL; - fp = ssh2_fingerprint(state->ssh2key.key); + fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); state->ssh2key.comment = savecomment; SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); @@ -829,16 +1155,20 @@ static void start_generating_key(HWND hwnd, struct MainDlgState *state) params->key_bits = state->key_bits; params->curve_bits = state->curve_bits; params->keytype = state->keytype; + params->primepolicy = state->primepolicy; + params->rsa_strong = state->rsa_strong; params->key = &state->key; params->dsskey = &state->dsskey; - if (!CreateThread(NULL, 0, generate_key_thread, - params, 0, &threadid)) { + HANDLE hThread = CreateThread(NULL, 0, generate_key_thread, + params, 0, &threadid); + if (!hThread) { MessageBox(hwnd, "Out of thread resources", "Key generation error", MB_OK | MB_ICONERROR); sfree(params); } else { + CloseHandle(hThread); /* we don't need the thread handle */ state->generation_thread_exists = true; } } @@ -895,7 +1225,25 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key"); - AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ED25519, "SSH-2 Ed&25519 key"); + AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2EDDSA, "SSH-2 EdD&SA key"); + AppendMenu(menu1, MF_SEPARATOR, 0, 0); + AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_PROB, + "Use probable primes (fast)"); + AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_SIMPLE, + "Use proven primes (slower)"); + AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_COMPLEX, + "Use proven primes with even distribution (slowest)"); + AppendMenu(menu1, MF_SEPARATOR, 0, 0); + AppendMenu(menu1, MF_ENABLED, IDC_RSA_STRONG, + "Use \"strong\" primes as RSA key factors"); + AppendMenu(menu1, MF_SEPARATOR, 0, 0); + AppendMenu(menu1, MF_ENABLED, IDC_PPK_PARAMS, + "Parameters for saving key files..."); + AppendMenu(menu1, MF_SEPARATOR, 0, 0); + AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_SHA256, + "Show fingerprint as SHA256"); + AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_MD5, + "Show fingerprint as MD5"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key"); state->keymenu = menu1; @@ -954,15 +1302,15 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, IDC_PKSTATIC, IDC_KEYDISPLAY, 5); SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC, - IDC_FINGERPRINT, 75); + IDC_FINGERPRINT, 82); SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC, - IDC_COMMENTEDIT, 75); + IDC_COMMENTEDIT, 82); staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC, - IDC_PASSPHRASE1EDIT, 75); + IDC_PASSPHRASE1EDIT, 82); staticpassedit(&cp, "C&onfirm passphrase:", - IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 75); + IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 82); endbox(&cp); beginbox(&cp, "Actions", IDC_BOX_ACTIONS); staticbtn(&cp, "Generate a public/private key pair", @@ -978,7 +1326,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, "&RSA", IDC_KEYSSH2RSA, "&DSA", IDC_KEYSSH2DSA, "&ECDSA", IDC_KEYSSH2ECDSA, - "Ed&25519", IDC_KEYSSH2ED25519, + "EdD&SA", IDC_KEYSSH2EDDSA, "SSH-&1 (RSA)", IDC_KEYSSH1, NULL); cp2 = cp; @@ -987,8 +1335,8 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, ymax = cp2.ypos; cp2 = cp; staticddl(&cp2, "Cur&ve to use for generating this key:", - IDC_CURVESTATIC, IDC_CURVE, 20); - SendDlgItemMessage(hwnd, IDC_CURVE, CB_RESETCONTENT, 0, 0); + IDC_ECCURVESTATIC, IDC_ECCURVE, 30); + SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_RESETCONTENT, 0, 0); { int i, bits; const struct ec_curve *curve; @@ -997,12 +1345,32 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, for (i = 0; i < n_ec_nist_curve_lengths; i++) { bits = ec_nist_curve_lengths[i]; ec_nist_alg_and_curve_by_bits(bits, &curve, &alg); - SendDlgItemMessage(hwnd, IDC_CURVE, CB_ADDSTRING, 0, + SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_ADDSTRING, 0, (LPARAM)curve->textname); } } ymax = ymax > cp2.ypos ? ymax : cp2.ypos; cp2 = cp; + staticddl(&cp2, "Cur&ve to use for generating this key:", + IDC_EDCURVESTATIC, IDC_EDCURVE, 30); + SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_RESETCONTENT, 0, 0); + { + int i, bits; + const struct ec_curve *curve; + const ssh_keyalg *alg; + + for (i = 0; i < n_ec_ed_curve_lengths; i++) { + bits = ec_ed_curve_lengths[i]; + ec_ed_alg_and_curve_by_bits(bits, &curve, &alg); + char *desc = dupprintf("%s (%d bits)", + curve->textname, bits); + SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_ADDSTRING, 0, + (LPARAM)desc); + sfree(desc); + } + } + ymax = ymax > cp2.ypos ? ymax : cp2.ypos; + cp2 = cp; statictext(&cp2, "(nothing to configure for this key type)", 1, IDC_NOTHINGSTATIC); ymax = ymax > cp2.ypos ? ymax : cp2.ypos; @@ -1010,9 +1378,14 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, endbox(&cp); } ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA); + ui_set_primepolicy(hwnd, state, IDC_PRIMEGEN_PROB); + ui_set_rsa_strong(hwnd, state, false); + ui_set_fptype(hwnd, state, fptype_to_idc(SSH_FPTYPE_DEFAULT)); SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); - SendDlgItemMessage(hwnd, IDC_CURVE, CB_SETCURSEL, - DEFAULT_CURVE_INDEX, 0); + SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL, + DEFAULT_ECCURVE_INDEX, 0); + SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_SETCURSEL, + DEFAULT_EDCURVE_INDEX, 0); /* * Initially, hide the progress bar and the key display, @@ -1060,13 +1433,55 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: case IDC_KEYSSH2ECDSA: - case IDC_KEYSSH2ED25519: - { - state = (struct MainDlgState *) - GetWindowLongPtr(hwnd, GWLP_USERDATA); - ui_set_key_type(hwnd, state, LOWORD(wParam)); + case IDC_KEYSSH2EDDSA: { + state = (struct MainDlgState *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + ui_set_key_type(hwnd, state, LOWORD(wParam)); + break; + } + case IDC_PRIMEGEN_PROB: + case IDC_PRIMEGEN_MAURER_SIMPLE: + case IDC_PRIMEGEN_MAURER_COMPLEX: { + state = (struct MainDlgState *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + ui_set_primepolicy(hwnd, state, LOWORD(wParam)); + break; + } + case IDC_FPTYPE_SHA256: + case IDC_FPTYPE_MD5: { + state = (struct MainDlgState *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + ui_set_fptype(hwnd, state, LOWORD(wParam)); + break; + } + case IDC_RSA_STRONG: { + state = (struct MainDlgState *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + ui_set_rsa_strong(hwnd, state, !state->rsa_strong); + break; + } + case IDC_PPK_PARAMS: { + struct PPKParams pp[1]; + pp->params = save_params; + if (pp->params.argon2_passes_auto) { + pp->time_ms = pp->params.argon2_milliseconds; + pp->time_passes = 13; + } else { + pp->time_ms = 100; + pp->time_passes = pp->params.argon2_passes; + } + int dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(215), + NULL, PPKParamsProc, (LPARAM)pp); + if (dlgret) { + if (pp->params.argon2_passes_auto) { + pp->params.argon2_milliseconds = pp->time_ms; + } else { + pp->params.argon2_passes = pp->time_passes; + } + save_params = pp->params; } break; + } case IDC_QUIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; @@ -1113,25 +1528,36 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, unsigned raw_entropy_required; unsigned char *raw_entropy_buf; BOOL ok; + state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, false); if (!ok) state->key_bits = DEFAULT_KEY_BITS; - { - int curveindex = SendDlgItemMessage(hwnd, IDC_CURVE, + state->ssh2 = true; + + if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1)) { + state->ssh2 = false; + state->keytype = RSA; + } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA)) { + state->keytype = RSA; + } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { + state->keytype = DSA; + } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { + state->keytype = ECDSA; + int curveindex = SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_GETCURSEL, 0, 0); assert(curveindex >= 0); assert(curveindex < n_ec_nist_curve_lengths); state->curve_bits = ec_nist_curve_lengths[curveindex]; - } - /* If we ever introduce a new key type, check it here! */ - state->ssh2 = !IsDlgButtonChecked(hwnd, IDC_KEYSSH1); - state->keytype = RSA; - if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { - state->keytype = DSA; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { - state->keytype = ECDSA; - } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ED25519)) { - state->keytype = ED25519; + } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) { + state->keytype = EDDSA; + int curveindex = SendDlgItemMessage(hwnd, IDC_EDCURVE, + CB_GETCURSEL, 0, 0); + assert(curveindex >= 0); + assert(curveindex < n_ec_ed_curve_lengths); + state->curve_bits = ec_ed_curve_lengths[curveindex]; + } else { + /* Somehow, no button was checked */ + break; } if ((state->keytype == RSA || state->keytype == DSA) && @@ -1161,10 +1587,10 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, if (state->keytype == RSA || state->keytype == DSA) raw_entropy_required = (state->key_bits / 2) * 2; - else if (state->keytype == ECDSA) + else if (state->keytype == ECDSA || state->keytype == EDDSA) raw_entropy_required = (state->curve_bits / 2) * 2; else - raw_entropy_required = 256; + unreachable("we must have initialised keytype by now"); /* Bound the entropy collection above by the amount of * data we can actually fit into the PRNG. Any more @@ -1309,9 +1735,9 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, ret = export_ssh2(fn, type, &state->ssh2key, *passphrase ? passphrase : NULL); else - ret = ssh2_save_userkey(fn, &state->ssh2key, - *passphrase ? passphrase : - NULL); + ret = ppk_save_f(fn, &state->ssh2key, + *passphrase ? passphrase : NULL, + &save_params); filename_free(fn); } else { Filename *fn = filename_from_str(filename); @@ -1319,9 +1745,8 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, ret = export_ssh1(fn, type, &state->key, *passphrase ? passphrase : NULL); else - ret = rsa_ssh1_savekey( - fn, &state->key, - *passphrase ? passphrase : NULL); + ret = rsa1_save_f(fn, &state->key, + *passphrase ? passphrase : NULL); filename_free(fn); } if (ret <= 0) { @@ -1408,7 +1833,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, state->ssh2key.key = &state->dsskey.sshk; } else if (state->keytype == ECDSA) { state->ssh2key.key = &state->eckey.sshk; - } else if (state->keytype == ED25519) { + } else if (state->keytype == EDDSA) { state->ssh2key.key = &state->edkey.sshk; } else { state->ssh2key.key = &state->key.sshk; @@ -1431,8 +1856,8 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm); else if (state->keytype == ECDSA) strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm); - else if (state->keytype == ED25519) - strftime(*state->commentptr, 30, "ed25519-key-%Y%m%d", &tm); + else if (state->keytype == EDDSA) + strftime(*state->commentptr, 30, "eddsa-key-%Y%m%d", &tm); else strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm); } @@ -1459,7 +1884,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, savecomment = *state->commentptr; *state->commentptr = NULL; if (state->ssh2) - fp = ssh2_fingerprint(state->ssh2key.key); + fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); else fp = rsa_ssh1_fingerprint(&state->key); SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); @@ -1483,61 +1908,60 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, */ ui_set_state(hwnd, state, 2); break; - case WM_HELP: - { - int id = ((LPHELPINFO)lParam)->iCtrlId; - const char *topic = NULL; - switch (id) { - case IDC_GENERATING: - case IDC_PROGRESS: - case IDC_GENSTATIC: - case IDC_GENERATE: - topic = WINHELP_CTX_puttygen_generate; break; - case IDC_PKSTATIC: - case IDC_KEYDISPLAY: - topic = WINHELP_CTX_puttygen_pastekey; break; - case IDC_FPSTATIC: - case IDC_FINGERPRINT: - topic = WINHELP_CTX_puttygen_fingerprint; break; - case IDC_COMMENTSTATIC: - case IDC_COMMENTEDIT: - topic = WINHELP_CTX_puttygen_comment; break; - case IDC_PASSPHRASE1STATIC: - case IDC_PASSPHRASE1EDIT: - case IDC_PASSPHRASE2STATIC: - case IDC_PASSPHRASE2EDIT: - topic = WINHELP_CTX_puttygen_passphrase; break; - case IDC_LOADSTATIC: - case IDC_LOAD: - topic = WINHELP_CTX_puttygen_load; break; - case IDC_SAVESTATIC: - case IDC_SAVE: - topic = WINHELP_CTX_puttygen_savepriv; break; - case IDC_SAVEPUB: - topic = WINHELP_CTX_puttygen_savepub; break; - case IDC_TYPESTATIC: - case IDC_KEYSSH1: - case IDC_KEYSSH2RSA: - case IDC_KEYSSH2DSA: - case IDC_KEYSSH2ECDSA: - case IDC_KEYSSH2ED25519: - topic = WINHELP_CTX_puttygen_keytype; break; - case IDC_BITSSTATIC: - case IDC_BITS: - topic = WINHELP_CTX_puttygen_bits; break; - case IDC_IMPORT: - case IDC_EXPORT_OPENSSH_AUTO: - case IDC_EXPORT_OPENSSH_NEW: - case IDC_EXPORT_SSHCOM: - topic = WINHELP_CTX_puttygen_conversions; break; - } - if (topic) { - launch_help(hwnd, topic); - } else { - MessageBeep(0); - } + case WM_HELP: { + int id = ((LPHELPINFO)lParam)->iCtrlId; + const char *topic = NULL; + switch (id) { + case IDC_GENERATING: + case IDC_PROGRESS: + case IDC_GENSTATIC: + case IDC_GENERATE: + topic = WINHELP_CTX_puttygen_generate; break; + case IDC_PKSTATIC: + case IDC_KEYDISPLAY: + topic = WINHELP_CTX_puttygen_pastekey; break; + case IDC_FPSTATIC: + case IDC_FINGERPRINT: + topic = WINHELP_CTX_puttygen_fingerprint; break; + case IDC_COMMENTSTATIC: + case IDC_COMMENTEDIT: + topic = WINHELP_CTX_puttygen_comment; break; + case IDC_PASSPHRASE1STATIC: + case IDC_PASSPHRASE1EDIT: + case IDC_PASSPHRASE2STATIC: + case IDC_PASSPHRASE2EDIT: + topic = WINHELP_CTX_puttygen_passphrase; break; + case IDC_LOADSTATIC: + case IDC_LOAD: + topic = WINHELP_CTX_puttygen_load; break; + case IDC_SAVESTATIC: + case IDC_SAVE: + topic = WINHELP_CTX_puttygen_savepriv; break; + case IDC_SAVEPUB: + topic = WINHELP_CTX_puttygen_savepub; break; + case IDC_TYPESTATIC: + case IDC_KEYSSH1: + case IDC_KEYSSH2RSA: + case IDC_KEYSSH2DSA: + case IDC_KEYSSH2ECDSA: + case IDC_KEYSSH2EDDSA: + topic = WINHELP_CTX_puttygen_keytype; break; + case IDC_BITSSTATIC: + case IDC_BITS: + topic = WINHELP_CTX_puttygen_bits; break; + case IDC_IMPORT: + case IDC_EXPORT_OPENSSH_AUTO: + case IDC_EXPORT_OPENSSH_NEW: + case IDC_EXPORT_SSHCOM: + topic = WINHELP_CTX_puttygen_conversions; break; + } + if (topic) { + launch_help(hwnd, topic); + } else { + MessageBeep(0); } break; + } case WM_CLOSE: state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); sfree(state); @@ -1554,6 +1978,8 @@ void cleanup_exit(int code) exit(code); } +HINSTANCE hinst; + int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { int argc, i; @@ -1564,7 +1990,6 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) init_common_controls(); hinst = inst; - hwnd = NULL; /* * See if we can find our Help file. @@ -1575,7 +2000,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-pgpfp")) { - pgp_fingerprints(); + pgp_fingerprints_msgbox(NULL); return 1; } else if (!strcmp(argv[i], "-restrict-acl") || !strcmp(argv[i], "-restrict_acl") || @@ -1591,6 +2016,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) } } + save_params = ppk_save_default_parameters; + random_setup_special(); ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK; diff --git a/windows/winpgnt.c b/windows/winpgnt.c index 718feb9..d6e960b 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -9,15 +9,15 @@ #include #include -#define PUTTY_DO_GLOBALS - #include "putty.h" #include "ssh.h" #include "misc.h" #include "tree234.h" #include "winsecur.h" +#include "wincapi.h" #include "pageant.h" #include "licence.h" +#include "pageant-rc.h" #include @@ -29,31 +29,28 @@ #endif #endif -#define IDI_MAINICON 200 -#define IDI_TRAYICON 201 - #define WM_SYSTRAY (WM_APP + 6) #define WM_SYSTRAY2 (WM_APP + 7) #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ -/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of - * wParam are used by Windows, and should be masked off, so we shouldn't - * attempt to store information in them. Hence all these identifiers have - * the low 4 bits clear. Also, identifiers should < 0xF000. */ - -#define IDM_CLOSE 0x0010 -#define IDM_VIEWKEYS 0x0020 -#define IDM_ADDKEY 0x0030 -#define IDM_HELP 0x0040 -#define IDM_ABOUT 0x0050 - #define APPNAME "Pageant" +/* Titles and class names for invisible windows. IPCWINTITLE and + * IPCCLASSNAME are critical to backwards compatibility: WM_COPYDATA + * based Pageant clients will call FindWindow with those parameters + * and expect to find the Pageant IPC receiver. */ +#define TRAYWINTITLE "Pageant" +#define TRAYCLASSNAME "PageantSysTray" +#define IPCWINTITLE "Pageant" +#define IPCCLASSNAME "Pageant" + +static HWND traywindow; static HWND keylist; static HWND aboutbox; static HMENU systray_menu, session_menu; static bool already_running; +static FingerprintType fptype = SSH_FPTYPE_DEFAULT; static char *putty_path; static bool restrict_putty_acl = false; @@ -61,9 +58,22 @@ static bool restrict_putty_acl = false; /* CWD for "add key" file requester. */ static filereq *keypath = NULL; -#define IDM_PUTTY 0x0060 -#define IDM_SESSIONS_BASE 0x1000 -#define IDM_SESSIONS_MAX 0x2000 +/* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of + * wParam are used by Windows, and should be masked off, so we shouldn't + * attempt to store information in them. Hence all these identifiers have + * the low 4 bits clear. Also, identifiers should < 0xF000. */ + +#define IDM_CLOSE 0x0010 +#define IDM_VIEWKEYS 0x0020 +#define IDM_ADDKEY 0x0030 +#define IDM_ADDKEY_ENCRYPTED 0x0040 +#define IDM_REMOVE_ALL 0x0050 +#define IDM_REENCRYPT_ALL 0x0060 +#define IDM_HELP 0x0070 +#define IDM_ABOUT 0x0080 +#define IDM_PUTTY 0x0090 +#define IDM_SESSIONS_BASE 0x1000 +#define IDM_SESSIONS_MAX 0x2000 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions" #define PUTTY_DEFAULT "Default%20Settings" static int initial_menuitems_count; @@ -79,7 +89,7 @@ void modalfatalbox(const char *fmt, ...) va_start(ap, fmt); buf = dupvprintf(fmt, ap); va_end(ap); - MessageBox(hwnd, buf, "Pageant Fatal Error", + MessageBox(traywindow, buf, "Pageant Fatal Error", MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(buf); exit(1); @@ -88,8 +98,11 @@ void modalfatalbox(const char *fmt, ...) static bool has_security; struct PassphraseProcStruct { - char **passphrase; - char *comment; + bool modal; + const char *help_topic; + PageantClientDialogId *dlgid; + char *passphrase; + const char *comment; }; /* @@ -100,7 +113,7 @@ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, { switch (msg) { case WM_INITDIALOG: - SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n")); + SetDlgItemText(hwnd, IDC_LICENCE_TEXTBOX, LICENCE_TEXT("\r\n\r\n")); return 1; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -124,18 +137,18 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { - case WM_INITDIALOG: - { - char *buildinfo_text = buildinfo("\r\n"); - char *text = dupprintf - ("Pageant\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", - ver, buildinfo_text, - "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); - sfree(buildinfo_text); - SetDlgItemText(hwnd, 1000, text); - sfree(text); - } + case WM_INITDIALOG: { + char *buildinfo_text = buildinfo("\r\n"); + char *text = dupprintf + ("Pageant\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", + ver, buildinfo_text, + "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); + sfree(buildinfo_text); + SetDlgItemText(hwnd, IDC_ABOUT_TEXTBOX, text); + MakeDlgItemBorderless(hwnd, IDC_ABOUT_TEXTBOX); + sfree(text); return 1; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: @@ -143,13 +156,13 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, aboutbox = NULL; DestroyWindow(hwnd); return 0; - case 101: + case IDC_ABOUT_LICENCE: EnableWindow(hwnd, 0); - DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc); + DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCE), hwnd, LicenceProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; - case 102: + case IDC_ABOUT_WEBSITE: /* Load web browser */ ShellExecute(hwnd, "open", "https://www.chiark.greenend.org.uk/~sgtatham/putty/", @@ -165,7 +178,42 @@ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, return 0; } -static HWND passphrase_box; +static HWND modal_passphrase_hwnd = NULL; +static HWND nonmodal_passphrase_hwnd = NULL; + +static void end_passphrase_dialog(HWND hwnd, INT_PTR result) +{ + struct PassphraseProcStruct *p = (struct PassphraseProcStruct *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + + if (p->modal) { + EndDialog(hwnd, result); + } else { + /* + * Destroy this passphrase dialog box before passing the + * results back to pageant.c, to avoid re-entrancy issues. + * + * If we successfully got a passphrase from the user, but it + * was _wrong_, then pageant_passphrase_request_success will + * respond by calling back - synchronously - to our + * ask_passphrase() implementation, which will expect the + * previous value of nonmodal_passphrase_hwnd to have already + * been cleaned up. + */ + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) NULL); + DestroyWindow(hwnd); + nonmodal_passphrase_hwnd = NULL; + + if (result) + pageant_passphrase_request_success( + p->dlgid, ptrlen_from_asciz(p->passphrase)); + else + pageant_passphrase_request_refused(p->dlgid); + + burnstr(p->passphrase); + sfree(p); + } +} /* * Dialog-box function for the passphrase box. @@ -173,59 +221,77 @@ static HWND passphrase_box; static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - static char **passphrase = NULL; struct PassphraseProcStruct *p; + if (msg == WM_INITDIALOG) { + p = (struct PassphraseProcStruct *) lParam; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) p); + } else { + p = (struct PassphraseProcStruct *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + } + switch (msg) { - case WM_INITDIALOG: - passphrase_box = hwnd; + case WM_INITDIALOG: { + if (p->modal) + modal_passphrase_hwnd = hwnd; + /* * Centre the window. */ - { /* centre the window */ - RECT rs, rd; - HWND hw; - - hw = GetDesktopWindow(); - if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) - MoveWindow(hwnd, - (rs.right + rs.left + rd.left - rd.right) / 2, - (rs.bottom + rs.top + rd.top - rd.bottom) / 2, - rd.right - rd.left, rd.bottom - rd.top, true); - } + RECT rs, rd; + HWND hw; + + hw = GetDesktopWindow(); + if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) + MoveWindow(hwnd, + (rs.right + rs.left + rd.left - rd.right) / 2, + (rs.bottom + rs.top + rd.top - rd.bottom) / 2, + rd.right - rd.left, rd.bottom - rd.top, true); SetForegroundWindow(hwnd); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); - p = (struct PassphraseProcStruct *) lParam; - passphrase = p->passphrase; + if (!p->modal) + SetActiveWindow(hwnd); /* this won't have happened automatically */ if (p->comment) - SetDlgItemText(hwnd, 101, p->comment); - burnstr(*passphrase); - *passphrase = dupstr(""); - SetDlgItemText(hwnd, 102, *passphrase); + SetDlgItemText(hwnd, IDC_PASSPHRASE_FINGERPRINT, p->comment); + burnstr(p->passphrase); + p->passphrase = dupstr(""); + SetDlgItemText(hwnd, IDC_PASSPHRASE_EDITBOX, p->passphrase); + if (!p->help_topic || !has_help()) { + HWND item = GetDlgItem(hwnd, IDHELP); + if (item) + DestroyWindow(item); + } return 0; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: - if (*passphrase) - EndDialog(hwnd, 1); + if (p->passphrase) + end_passphrase_dialog(hwnd, 1); else MessageBeep(0); return 0; case IDCANCEL: - EndDialog(hwnd, 0); + end_passphrase_dialog(hwnd, 0); return 0; - case 102: /* edit box */ - if ((HIWORD(wParam) == EN_CHANGE) && passphrase) { - burnstr(*passphrase); - *passphrase = GetDlgItemText_alloc(hwnd, 102); + case IDHELP: + if (p->help_topic) + launch_help(hwnd, p->help_topic); + return 0; + case IDC_PASSPHRASE_EDITBOX: + if ((HIWORD(wParam) == EN_CHANGE) && p->passphrase) { + burnstr(p->passphrase); + p->passphrase = GetDlgItemText_alloc( + hwnd, IDC_PASSPHRASE_EDITBOX); } return 0; } return 0; case WM_CLOSE: - EndDialog(hwnd, 0); + end_passphrase_dialog(hwnd, 0); return 0; } return 0; @@ -251,120 +317,138 @@ void old_keyfile_warning(void) MessageBox(NULL, message, mbtitle, MB_OK); } -/* - * Update the visible key list. - */ -void keylist_update(void) +struct keylist_update_ctx { + bool enable_remove_controls; + bool enable_reencrypt_controls; +}; + +static void keylist_update_callback( + void *vctx, char **fingerprints, const char *comment, uint32_t ext_flags, + struct pageant_pubkey *key) { - RSAKey *rkey; - ssh2_userkey *skey; - int i; + struct keylist_update_ctx *ctx = (struct keylist_update_ctx *)vctx; + FingerprintType this_type = ssh2_pick_fingerprint(fingerprints, fptype); + const char *fingerprint = fingerprints[this_type]; + strbuf *listentry = strbuf_new(); - if (keylist) { - SendDlgItemMessage(keylist, 100, LB_RESETCONTENT, 0, 0); - for (i = 0; NULL != (rkey = pageant_nth_ssh1_key(i)); i++) { - char *listentry, *fp, *p; + /* There is at least one key, so the controls for removing keys + * should be enabled */ + ctx->enable_remove_controls = true; - fp = rsa_ssh1_fingerprint(rkey); - listentry = dupprintf("ssh1\t%s", fp); - sfree(fp); + switch (key->ssh_version) { + case 1: { + strbuf_catf(listentry, "ssh1\t%s\t%s", fingerprint, comment); - /* - * Replace two spaces in the fingerprint with tabs, for - * nice alignment in the box. - */ - p = strchr(listentry, ' '); - if (p) - *p = '\t'; - p = strchr(listentry, ' '); - if (p) - *p = '\t'; - SendDlgItemMessage(keylist, 100, LB_ADDSTRING, - 0, (LPARAM) listentry); - sfree(listentry); + /* + * Replace the space in the fingerprint (between bit count and + * hash) with a tab, for nice alignment in the box. + */ + char *p = strchr(listentry->s, ' '); + if (p) + *p = '\t'; + break; + } + + case 2: { + /* + * For nice alignment in the list box, we would ideally want + * every entry to align to the tab stop settings, and have a + * column for algorithm name, one for bit count, one for hex + * fingerprint, and one for key comment. + * + * Unfortunately, some of the algorithm names are so long that + * they overflow into the bit-count field. Fortunately, at the + * moment, those are _precisely_ the algorithm names that + * don't need a bit count displayed anyway (because for + * NIST-style ECDSA the bit count is mentioned in the + * algorithm name, and for ssh-ed25519 there is only one + * possible value anyway). So we fudge this by simply omitting + * the bit count field in that situation. + * + * This is fragile not only in the face of further key types + * that don't follow this pattern, but also in the face of + * font metrics changes - the Windows semantics for list box + * tab stops is that \t aligns to the next one you haven't + * already exceeded, so I have to guess when the key type will + * overflow past the bit-count tab stop and leave out a tab + * character. Urgh. + */ + BinarySource src[1]; + BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->blob)); + ptrlen algname = get_string(src); + const ssh_keyalg *alg = find_pubkey_alg_len(algname); + + bool include_bit_count = (alg == &ssh_dss || alg == &ssh_rsa); + + int wordnumber = 0; + for (const char *p = fingerprint; *p; p++) { + char c = *p; + if (c == ' ') { + if (wordnumber < 2) + c = '\t'; + wordnumber++; + } + if (include_bit_count || wordnumber != 1) + put_byte(listentry, c); } - for (i = 0; NULL != (skey = pageant_nth_ssh2_key(i)); i++) { - char *listentry, *p; - int pos; - /* - * For nice alignment in the list box, we would ideally - * want every entry to align to the tab stop settings, and - * have a column for algorithm name, one for bit count, - * one for hex fingerprint, and one for key comment. - * - * Unfortunately, some of the algorithm names are so long - * that they overflow into the bit-count field. - * Fortunately, at the moment, those are _precisely_ the - * algorithm names that don't need a bit count displayed - * anyway (because for NIST-style ECDSA the bit count is - * mentioned in the algorithm name, and for ssh-ed25519 - * there is only one possible value anyway). So we fudge - * this by simply omitting the bit count field in that - * situation. - * - * This is fragile not only in the face of further key - * types that don't follow this pattern, but also in the - * face of font metrics changes - the Windows semantics - * for list box tab stops is that \t aligns to the next - * one you haven't already exceeded, so I have to guess - * when the key type will overflow past the bit-count tab - * stop and leave out a tab character. Urgh. - */ + strbuf_catf(listentry, "\t%s", comment); + break; + } + } - p = ssh2_fingerprint(skey->key); - listentry = dupprintf("%s\t%s", p, skey->comment); - sfree(p); + if (ext_flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY) { + strbuf_catf(listentry, "\t(encrypted)"); + } else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE) { + strbuf_catf(listentry, "\t(re-encryptable)"); - pos = 0; - while (1) { - pos += strcspn(listentry + pos, " :"); - if (listentry[pos] == ':' || !listentry[pos]) - break; - listentry[pos++] = '\t'; - } - if (ssh_key_alg(skey->key) != &ssh_dss && - ssh_key_alg(skey->key) != &ssh_rsa) { - /* - * Remove the bit-count field, which is between the - * first and second \t. - */ - int outpos; - pos = 0; - while (listentry[pos] && listentry[pos] != '\t') - pos++; - outpos = pos; - pos++; - while (listentry[pos] && listentry[pos] != '\t') - pos++; - while (1) { - if ((listentry[outpos] = listentry[pos]) == '\0') - break; - outpos++; - pos++; - } - } + /* At least one key can be re-encrypted */ + ctx->enable_reencrypt_controls = true; + } - SendDlgItemMessage(keylist, 100, LB_ADDSTRING, 0, - (LPARAM) listentry); - sfree(listentry); - } - SendDlgItemMessage(keylist, 100, LB_SETCURSEL, (WPARAM) - 1, 0); + SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, + LB_ADDSTRING, 0, (LPARAM)listentry->s); + strbuf_free(listentry); +} + +/* + * Update the visible key list. + */ +void keylist_update(void) +{ + if (keylist) { + SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, + LB_RESETCONTENT, 0, 0); + + char *errmsg; + struct keylist_update_ctx ctx[1]; + ctx->enable_remove_controls = false; + ctx->enable_reencrypt_controls = false; + int status = pageant_enum_keys(keylist_update_callback, ctx, &errmsg); + assert(status == PAGEANT_ACTION_OK); + assert(!errmsg); + + SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, + LB_SETCURSEL, (WPARAM) - 1, 0); + + EnableWindow(GetDlgItem(keylist, IDC_KEYLIST_REMOVE), + ctx->enable_remove_controls); + EnableWindow(GetDlgItem(keylist, IDC_KEYLIST_REENCRYPT), + ctx->enable_reencrypt_controls); } } -static void win_add_keyfile(Filename *filename) +static void win_add_keyfile(Filename *filename, bool encrypted) { char *err; int ret; - char *passphrase = NULL; /* * Try loading the key without a passphrase. (Or rather, without a * _new_ passphrase; pageant_add_keyfile will take care of trying * all the passphrases we've already stored.) */ - ret = pageant_add_keyfile(filename, NULL, &err); + ret = pageant_add_keyfile(filename, NULL, &err, encrypted); if (ret == PAGEANT_ACTION_OK) { goto done; } else if (ret == PAGEANT_ACTION_FAILURE) { @@ -378,40 +462,39 @@ static void win_add_keyfile(Filename *filename) while (1) { INT_PTR dlgret; struct PassphraseProcStruct pps; - - pps.passphrase = &passphrase; + pps.modal = true; + pps.help_topic = NULL; /* this dialog has no help button */ + pps.dlgid = NULL; + pps.passphrase = NULL; pps.comment = err; - dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(210), - NULL, PassphraseProc, (LPARAM) &pps); - passphrase_box = NULL; + dlgret = DialogBoxParam( + hinst, MAKEINTRESOURCE(IDD_LOAD_PASSPHRASE), + NULL, PassphraseProc, (LPARAM) &pps); + modal_passphrase_hwnd = NULL; - if (!dlgret) + if (!dlgret) { + burnstr(pps.passphrase); goto done; /* operation cancelled */ + } sfree(err); - assert(passphrase != NULL); + assert(pps.passphrase != NULL); + + ret = pageant_add_keyfile(filename, pps.passphrase, &err, false); + burnstr(pps.passphrase); - ret = pageant_add_keyfile(filename, passphrase, &err); if (ret == PAGEANT_ACTION_OK) { goto done; } else if (ret == PAGEANT_ACTION_FAILURE) { goto error; } - - smemclr(passphrase, strlen(passphrase)); - sfree(passphrase); - passphrase = NULL; } error: - message_box(err, APPNAME, MB_OK | MB_ICONERROR, + message_box(traywindow, err, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); done: - if (passphrase) { - smemclr(passphrase, strlen(passphrase)); - sfree(passphrase); - } sfree(err); return; } @@ -419,14 +502,14 @@ static void win_add_keyfile(Filename *filename) /* * Prompt for a key file to add, and add it. */ -static void prompt_add_keyfile(void) +static void prompt_add_keyfile(bool encrypted) { OPENFILENAME of; char *filelist = snewn(8192, char); if (!keypath) keypath = filereq_new(); memset(&of, 0, sizeof(of)); - of.hwndOwner = hwnd; + of.hwndOwner = traywindow; of.lpstrFilter = FILTER_KEY_FILES; of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; @@ -440,7 +523,7 @@ static void prompt_add_keyfile(void) if(strlen(filelist) > of.nFileOffset) { /* Only one filename returned? */ Filename *fn = filename_from_str(filelist); - win_add_keyfile(fn); + win_add_keyfile(fn, encrypted); filename_free(fn); } else { /* we are returned a bunch of strings, end to @@ -451,9 +534,9 @@ static void prompt_add_keyfile(void) char *dir = filelist; char *filewalker = filelist + strlen(dir) + 1; while (*filewalker != '\0') { - char *filename = dupcat(dir, "\\", filewalker, NULL); + char *filename = dupcat(dir, "\\", filewalker); Filename *fn = filename_from_str(filename); - win_add_keyfile(fn); + win_add_keyfile(fn, encrypted); filename_free(fn); sfree(filename); filewalker += strlen(filewalker) + 1; @@ -472,45 +555,60 @@ static void prompt_add_keyfile(void) static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { - RSAKey *rkey; - ssh2_userkey *skey; + static const struct { + const char *name; + FingerprintType value; + } fptypes[] = { + {"SHA256", SSH_FPTYPE_SHA256}, + {"MD5", SSH_FPTYPE_MD5}, + }; switch (msg) { - case WM_INITDIALOG: + case WM_INITDIALOG: { /* * Centre the window. */ - { /* centre the window */ - RECT rs, rd; - HWND hw; - - hw = GetDesktopWindow(); - if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) - MoveWindow(hwnd, - (rs.right + rs.left + rd.left - rd.right) / 2, - (rs.bottom + rs.top + rd.top - rd.bottom) / 2, - rd.right - rd.left, rd.bottom - rd.top, true); - } + RECT rs, rd; + HWND hw; + + hw = GetDesktopWindow(); + if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) + MoveWindow(hwnd, + (rs.right + rs.left + rd.left - rd.right) / 2, + (rs.bottom + rs.top + rd.top - rd.bottom) / 2, + rd.right - rd.left, rd.bottom - rd.top, true); if (has_help()) SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); else { - HWND item = GetDlgItem(hwnd, 103); /* the Help button */ - if (item) - DestroyWindow(item); + HWND item = GetDlgItem(hwnd, IDC_KEYLIST_HELP); + if (item) + DestroyWindow(item); } keylist = hwnd; { - static int tabs[] = { 35, 75, 250 }; - SendDlgItemMessage(hwnd, 100, LB_SETTABSTOPS, - sizeof(tabs) / sizeof(*tabs), - (LPARAM) tabs); + static int tabs[] = { 35, 75, 300 }; + SendDlgItemMessage(hwnd, IDC_KEYLIST_LISTBOX, LB_SETTABSTOPS, + sizeof(tabs) / sizeof(*tabs), + (LPARAM) tabs); } + + int selection = 0; + for (size_t i = 0; i < lenof(fptypes); i++) { + SendDlgItemMessage(hwnd, IDC_KEYLIST_FPTYPE, CB_ADDSTRING, + 0, (LPARAM)fptypes[i].name); + if (fptype == fptypes[i].value) + selection = (int)i; + } + SendDlgItemMessage(hwnd, IDC_KEYLIST_FPTYPE, + CB_SETCURSEL, 0, selection); + keylist_update(); return 0; + } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: @@ -518,18 +616,20 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, keylist = NULL; DestroyWindow(hwnd); return 0; - case 101: /* add key */ + case IDC_KEYLIST_ADDKEY: + case IDC_KEYLIST_ADDKEY_ENC: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { - if (passphrase_box) { + if (modal_passphrase_hwnd) { MessageBeep(MB_ICONERROR); - SetForegroundWindow(passphrase_box); + SetForegroundWindow(modal_passphrase_hwnd); break; } - prompt_add_keyfile(); + prompt_add_keyfile(LOWORD(wParam) == IDC_KEYLIST_ADDKEY_ENC); } return 0; - case 102: /* remove key */ + case IDC_KEYLIST_REMOVE: + case IDC_KEYLIST_REENCRYPT: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int i; @@ -540,8 +640,8 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, int itemNum; /* get the number of items selected in the list */ - int numSelected = - SendDlgItemMessage(hwnd, 100, LB_GETSELCOUNT, 0, 0); + int numSelected = SendDlgItemMessage( + hwnd, IDC_KEYLIST_LISTBOX, LB_GETSELCOUNT, 0, 0); /* none selected? that was silly */ if (numSelected == 0) { @@ -551,8 +651,8 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, /* get item indices in an array */ selectedArray = snewn(numSelected, int); - SendDlgItemMessage(hwnd, 100, LB_GETSELITEMS, - numSelected, (WPARAM)selectedArray); + SendDlgItemMessage(hwnd, IDC_KEYLIST_LISTBOX, LB_GETSELITEMS, + numSelected, (WPARAM)selectedArray); itemNum = numSelected - 1; rCount = pageant_count_ssh1_keys(); @@ -564,24 +664,30 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, * things hence altering the offset of subsequent items */ for (i = sCount - 1; (itemNum >= 0) && (i >= 0); i--) { - skey = pageant_nth_ssh2_key(i); - if (selectedArray[itemNum] == rCount + i) { - pageant_delete_ssh2_key(skey); - ssh_key_free(skey->key); - sfree(skey); + switch (LOWORD(wParam)) { + case IDC_KEYLIST_REMOVE: + pageant_delete_nth_ssh2_key(i); + break; + case IDC_KEYLIST_REENCRYPT: + pageant_reencrypt_nth_ssh2_key(i); + break; + } itemNum--; } } /* do the same for the rsa keys */ for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) { - rkey = pageant_nth_ssh1_key(i); - if(selectedArray[itemNum] == i) { - pageant_delete_ssh1_key(rkey); - freersakey(rkey); - sfree(rkey); + switch (LOWORD(wParam)) { + case IDC_KEYLIST_REMOVE: + pageant_delete_nth_ssh1_key(i); + break; + case IDC_KEYLIST_REENCRYPT: + /* SSH-1 keys can't be re-encrypted */ + break; + } itemNum--; } } @@ -590,30 +696,45 @@ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, keylist_update(); } return 0; - case 103: /* help */ + case IDC_KEYLIST_HELP: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { launch_help(hwnd, WINHELP_CTX_pageant_general); } return 0; + case IDC_KEYLIST_FPTYPE: + if (HIWORD(wParam) == CBN_SELCHANGE) { + int selection = SendDlgItemMessage( + hwnd, IDC_KEYLIST_FPTYPE, CB_GETCURSEL, 0, 0); + if (selection >= 0 && (size_t)selection < lenof(fptypes)) { + fptype = fptypes[selection].value; + keylist_update(); + } + } + return 0; } return 0; - case WM_HELP: - { - int id = ((LPHELPINFO)lParam)->iCtrlId; - const char *topic = NULL; - switch (id) { - case 100: topic = WINHELP_CTX_pageant_keylist; break; - case 101: topic = WINHELP_CTX_pageant_addkey; break; - case 102: topic = WINHELP_CTX_pageant_remkey; break; - } - if (topic) { - launch_help(hwnd, topic); - } else { - MessageBeep(0); - } + case WM_HELP: { + int id = ((LPHELPINFO)lParam)->iCtrlId; + const char *topic = NULL; + switch (id) { + case IDC_KEYLIST_LISTBOX: + case IDC_KEYLIST_FPTYPE: + case IDC_KEYLIST_FPTYPE_STATIC: + topic = WINHELP_CTX_pageant_keylist; break; + case IDC_KEYLIST_ADDKEY: topic = WINHELP_CTX_pageant_addkey; break; + case IDC_KEYLIST_REMOVE: topic = WINHELP_CTX_pageant_remkey; break; + case IDC_KEYLIST_ADDKEY_ENC: + case IDC_KEYLIST_REENCRYPT: + topic = WINHELP_CTX_pageant_deferred; break; + } + if (topic) { + launch_help(hwnd, topic); + } else { + MessageBeep(0); } break; + } case WM_CLOSE: keylist = NULL; DestroyWindow(hwnd); @@ -677,7 +798,7 @@ static void update_sessions(void) sb = strbuf_new(); while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) { if(strcmp(buf, PUTTY_DEFAULT) != 0) { - sb->len = 0; + strbuf_clear(sb); unescape_registry_key(buf, sb); memset(&mii, 0, sizeof(mii)); @@ -753,25 +874,106 @@ PSID get_default_sid(void) } #endif -struct PageantReply { - char *buf; - size_t size, len; - bool overflowed; - BinarySink_IMPLEMENTATION; -}; +struct WmCopydataTransaction { + char *length, *body; + size_t bodysize, bodylen; + HANDLE ev_msg_ready, ev_reply_ready; +} wmct; + +static struct PageantClient wmcpc; -static void pageant_reply_BinarySink_write( - BinarySink *bs, const void *data, size_t len) +static void wm_copydata_got_msg(void *vctx) { - struct PageantReply *rep = BinarySink_DOWNCAST(bs, struct PageantReply); - if (!rep->overflowed && len <= rep->size - rep->len) { - memcpy(rep->buf + rep->len, data, len); - rep->len += len; - } else { - rep->overflowed = true; + pageant_handle_msg(&wmcpc, NULL, make_ptrlen(wmct.body, wmct.bodylen)); +} + +static void wm_copydata_got_response( + PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) +{ + if (response.len > wmct.bodysize) { + /* Output would overflow message buffer. Replace with a + * failure message. */ + static const unsigned char failure[] = { SSH_AGENT_FAILURE }; + response = make_ptrlen(failure, lenof(failure)); + assert(response.len <= wmct.bodysize); } + + PUT_32BIT_MSB_FIRST(wmct.length, response.len); + memcpy(wmct.body, response.ptr, response.len); + + SetEvent(wmct.ev_reply_ready); +} + +static bool ask_passphrase_common(PageantClientDialogId *dlgid, + const char *comment) +{ + /* Pageant core should be serialising requests, so we never expect + * a passphrase prompt to exist already at this point */ + assert(!nonmodal_passphrase_hwnd); + + struct PassphraseProcStruct *pps = snew(struct PassphraseProcStruct); + pps->modal = false; + pps->help_topic = WINHELP_CTX_pageant_deferred; + pps->dlgid = dlgid; + pps->passphrase = NULL; + pps->comment = comment; + + nonmodal_passphrase_hwnd = CreateDialogParam( + hinst, MAKEINTRESOURCE(IDD_ONDEMAND_PASSPHRASE), + NULL, PassphraseProc, (LPARAM)pps); + + /* + * Try to put this passphrase prompt into the foreground. + * + * This will probably not succeed in giving it the actual keyboard + * focus, because Windows is quite opposed to applications being + * able to suddenly steal the focus on their own initiative. + * + * That makes sense in a lot of situations, as a defensive + * measure. If you were about to type a password or other secret + * data into the window you already had focused, and some + * malicious app stole the focus, it might manage to trick you + * into typing your secrets into _it_ instead. + * + * In this case it's possible to regard the same defensive measure + * as counterproductive, because the effect if we _do_ steal focus + * is that you type something into our passphrase prompt that + * isn't the passphrase, and we fail to decrypt the key, and no + * harm is done. Whereas the effect of the user wrongly _assuming_ + * the new passphrase prompt has the focus is much worse: now you + * type your highly secret passphrase into some other window you + * didn't mean to trust with that information - such as the + * agent-forwarded PuTTY in which you just ran an ssh command, + * which the _whole point_ was to avoid telling your passphrase to! + * + * On the other hand, I'm sure _every_ application author can come + * up with an argument for why they think _they_ should be allowed + * to steal the focus. Probably most of them include the claim + * that no harm is done if their application receives data + * intended for something else, and of course that's not always + * true! + * + * In any case, I don't know of anything I can do about it, or + * anything I _should_ do about it if I could. If anyone thinks + * they can improve on all this, patches are welcome. + */ + SetForegroundWindow(nonmodal_passphrase_hwnd); + + return true; } +static bool wm_copydata_ask_passphrase( + PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) +{ + return ask_passphrase_common(dlgid, comment); +} + +static const PageantClientVtable wmcpc_vtable = { + .log = NULL, /* no logging in this client */ + .got_response = wm_copydata_got_response, + .ask_passphrase = wm_copydata_ask_passphrase, +}; + static char *answer_filemapping_message(const char *mapname) { HANDLE maphandle = INVALID_HANDLE_VALUE; @@ -779,7 +981,6 @@ static char *answer_filemapping_message(const char *mapname) char *err = NULL; size_t mapsize; unsigned msglen; - struct PageantReply reply; #ifndef NO_SECURITY PSID mapsid = NULL; @@ -788,7 +989,7 @@ static char *answer_filemapping_message(const char *mapname) PSECURITY_DESCRIPTOR psd = NULL; #endif - reply.buf = NULL; + wmct.length = wmct.body = NULL; #ifdef DEBUG_IPC debug("mapname = \"%s\"\n", mapname); @@ -884,49 +1085,38 @@ static char *answer_filemapping_message(const char *mapname) mapsize = mbi.RegionSize; } #ifdef DEBUG_IPC - debug("region size = %zd\n", mapsize); + debug("region size = %"SIZEu"\n", mapsize); #endif if (mapsize < 5) { err = dupstr("mapping smaller than smallest possible request"); goto cleanup; } - msglen = GET_32BIT_MSB_FIRST((unsigned char *)mapaddr); + wmct.length = (char *)mapaddr; + msglen = GET_32BIT_MSB_FIRST(wmct.length); #ifdef DEBUG_IPC debug("msg length=%08x, msg type=%02x\n", msglen, (unsigned)((unsigned char *) mapaddr)[4]); #endif - reply.buf = (char *)mapaddr + 4; - reply.size = mapsize - 4; - reply.len = 0; - reply.overflowed = false; - BinarySink_INIT(&reply, pageant_reply_BinarySink_write); - - if (msglen > mapsize - 4) { - pageant_failure_msg(BinarySink_UPCAST(&reply), - "incoming length field too large", NULL, NULL); + wmct.body = wmct.length + 4; + wmct.bodysize = mapsize - 4; + + if (msglen > wmct.bodysize) { + /* Incoming length field is too large. Emit a failure response + * without even trying to handle the request. + * + * (We know this must fit, because we checked mapsize >= 5 + * above.) */ + PUT_32BIT_MSB_FIRST(wmct.length, 1); + *wmct.body = SSH_AGENT_FAILURE; } else { - pageant_handle_msg(BinarySink_UPCAST(&reply), - (unsigned char *)mapaddr + 4, msglen, NULL, NULL); - if (reply.overflowed) { - reply.len = 0; - reply.overflowed = false; - pageant_failure_msg(BinarySink_UPCAST(&reply), - "output would overflow message buffer", - NULL, NULL); - } + wmct.bodylen = msglen; + SetEvent(wmct.ev_msg_ready); + WaitForSingleObject(wmct.ev_reply_ready, INFINITE); } - if (reply.overflowed) { - err = dupstr("even failure message overflows buffer"); - goto cleanup; - } - - /* Write in the initial length field, and we're done. */ - PUT_32BIT_MSB_FIRST(((unsigned char *)mapaddr), reply.len); - cleanup: /* expectedsid has the lifetime of the program, so we don't free it */ sfree(expectedsid_bc); @@ -939,8 +1129,18 @@ static char *answer_filemapping_message(const char *mapname) return err; } -static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, - WPARAM wParam, LPARAM lParam) +static void create_keylist_window(void) +{ + if (keylist) + return; + + keylist = CreateDialog(hinst, MAKEINTRESOURCE(IDD_KEYLIST), + NULL, KeyListProc); + ShowWindow(keylist, SW_SHOWNORMAL); +} + +static LRESULT CALLBACK TrayWndProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) { static bool menuinprogress; static UINT msgTaskbarCreated = 0; @@ -984,33 +1184,29 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, } break; case WM_COMMAND: - case WM_SYSCOMMAND: - switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ - case IDM_PUTTY: - { - TCHAR cmdline[10]; - cmdline[0] = '\0'; - if (restrict_putty_acl) - strcat(cmdline, "&R"); - - if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline, - _T(""), SW_SHOW) <= 32) { - MessageBox(NULL, "Unable to execute PuTTY!", - "Error", MB_OK | MB_ICONERROR); - } + case WM_SYSCOMMAND: { + unsigned command = wParam & ~0xF; /* low 4 bits reserved to Windows */ + switch (command) { + case IDM_PUTTY: { + TCHAR cmdline[10]; + cmdline[0] = '\0'; + if (restrict_putty_acl) + strcat(cmdline, "&R"); + + if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline, + _T(""), SW_SHOW) <= 32) { + MessageBox(NULL, "Unable to execute PuTTY!", + "Error", MB_OK | MB_ICONERROR); } break; + } case IDM_CLOSE: - if (passphrase_box) - SendMessage(passphrase_box, WM_CLOSE, 0, 0); + if (modal_passphrase_hwnd) + SendMessage(modal_passphrase_hwnd, WM_CLOSE, 0, 0); SendMessage(hwnd, WM_CLOSE, 0, 0); break; case IDM_VIEWKEYS: - if (!keylist) { - keylist = CreateDialog(hinst, MAKEINTRESOURCE(211), - NULL, KeyListProc); - ShowWindow(keylist, SW_SHOWNORMAL); - } + create_keylist_window(); /* * Sometimes the window comes up minimised / hidden for * no obvious reason. Prevent this. This also brings it @@ -1023,16 +1219,25 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); break; case IDM_ADDKEY: - if (passphrase_box) { + case IDM_ADDKEY_ENCRYPTED: + if (modal_passphrase_hwnd) { MessageBeep(MB_ICONERROR); - SetForegroundWindow(passphrase_box); + SetForegroundWindow(modal_passphrase_hwnd); break; } - prompt_add_keyfile(); + prompt_add_keyfile(command == IDM_ADDKEY_ENCRYPTED); + break; + case IDM_REMOVE_ALL: + pageant_delete_all(); + keylist_update(); + break; + case IDM_REENCRYPT_ALL: + pageant_reencrypt_all(); + keylist_update(); break; case IDM_ABOUT: if (!aboutbox) { - aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(213), + aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUT), NULL, AboutProc); ShowWindow(aboutbox, SW_SHOWNORMAL); /* @@ -1047,63 +1252,90 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, case IDM_HELP: launch_help(hwnd, WINHELP_CTX_pageant_general); break; - default: - { - if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) { - MENUITEMINFO mii; - TCHAR buf[MAX_PATH + 1]; - TCHAR param[MAX_PATH + 1]; - memset(&mii, 0, sizeof(mii)); - mii.cbSize = sizeof(mii); - mii.fMask = MIIM_TYPE; - mii.cch = MAX_PATH; - mii.dwTypeData = buf; - GetMenuItemInfo(session_menu, wParam, false, &mii); - param[0] = '\0'; - if (restrict_putty_acl) - strcat(param, "&R"); - strcat(param, "@"); - strcat(param, mii.dwTypeData); - if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param, - _T(""), SW_SHOW) <= 32) { - MessageBox(NULL, "Unable to execute PuTTY!", "Error", - MB_OK | MB_ICONERROR); - } - } + default: { + if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) { + MENUITEMINFO mii; + TCHAR buf[MAX_PATH + 1]; + TCHAR param[MAX_PATH + 1]; + memset(&mii, 0, sizeof(mii)); + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_TYPE; + mii.cch = MAX_PATH; + mii.dwTypeData = buf; + GetMenuItemInfo(session_menu, wParam, false, &mii); + param[0] = '\0'; + if (restrict_putty_acl) + strcat(param, "&R"); + strcat(param, "@"); + strcat(param, mii.dwTypeData); + if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param, + _T(""), SW_SHOW) <= 32) { + MessageBox(NULL, "Unable to execute PuTTY!", "Error", + MB_OK | MB_ICONERROR); + } } break; + } } break; + } case WM_DESTROY: quit_help(hwnd); PostQuitMessage(0); return 0; - case WM_COPYDATA: - { - COPYDATASTRUCT *cds; - char *mapname, *err; - - cds = (COPYDATASTRUCT *) lParam; - if (cds->dwData != AGENT_COPYDATA_ID) - return 0; /* not our message, mate */ - mapname = (char *) cds->lpData; - if (mapname[cds->cbData - 1] != '\0') - return 0; /* failure to be ASCIZ! */ - err = answer_filemapping_message(mapname); - if (err) { + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} + +static LRESULT CALLBACK wm_copydata_WndProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COPYDATA: { + COPYDATASTRUCT *cds; + char *mapname, *err; + + cds = (COPYDATASTRUCT *) lParam; + if (cds->dwData != AGENT_COPYDATA_ID) + return 0; /* not our message, mate */ + mapname = (char *) cds->lpData; + if (mapname[cds->cbData - 1] != '\0') + return 0; /* failure to be ASCIZ! */ + err = answer_filemapping_message(mapname); + if (err) { #ifdef DEBUG_IPC - debug("IPC failed: %s\n", err); + debug("IPC failed: %s\n", err); #endif - sfree(err); - return 0; - } - return 1; + sfree(err); + return 0; } + return 1; + } } return DefWindowProc(hwnd, message, wParam, lParam); } +static DWORD WINAPI wm_copydata_threadfunc(void *param) +{ + HINSTANCE inst = *(HINSTANCE *)param; + + HWND ipchwnd = CreateWindow(IPCCLASSNAME, IPCWINTITLE, + WS_OVERLAPPEDWINDOW | WS_VSCROLL, + CW_USEDEFAULT, CW_USEDEFAULT, + 100, 100, NULL, NULL, inst, NULL); + ShowWindow(ipchwnd, SW_HIDE); + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0) == 1) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return 0; +} + /* * Fork and Exec the command in cmdline. [DBW] */ @@ -1119,10 +1351,14 @@ void spawn_cmd(const char *cmdline, const char *args, int show) } } -void agent_schedule_callback(void (*callback)(void *, void *, int), - void *callback_ctx, void *data, int len) +void logevent(LogContext *logctx, const char *event) +{ + unreachable("Pageant can't create a LogContext, so this can't be called"); +} + +void noise_ultralight(NoiseSourceId id, unsigned long data) { - unreachable("all Pageant's own agent requests should be synchronous"); + /* Pageant doesn't use random numbers, so we ignore this */ } void cleanup_exit(int code) @@ -1131,21 +1367,37 @@ void cleanup_exit(int code) exit(code); } -int flags = FLAG_SYNCAGENT; +static bool winpgnt_listener_ask_passphrase( + PageantListenerClient *plc, PageantClientDialogId *dlgid, + const char *comment) +{ + return ask_passphrase_common(dlgid, comment); +} + +struct winpgnt_client { + PageantListenerClient plc; +}; +static const PageantListenerClientVtable winpgnt_vtable = { + .log = NULL, /* no logging */ + .ask_passphrase = winpgnt_listener_ask_passphrase, +}; + +static struct winpgnt_client wpc[1]; + +HINSTANCE hinst; int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { - WNDCLASS wndclass; MSG msg; const char *command = NULL; bool added_keys = false; + bool show_keylist_on_startup = false; int argc, i; char **argv, **argstart; dll_hijacking_protection(); hinst = inst; - hwnd = NULL; /* * Determine whether we're an NT system (should have security @@ -1217,31 +1469,55 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Process the command line and add keys as listed on it. */ split_into_argv(cmdline, &argc, &argv, &argstart); + bool doing_opts = true; + bool add_keys_encrypted = false; for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "-pgpfp")) { - pgp_fingerprints(); - return 1; - } else if (!strcmp(argv[i], "-restrict-acl") || - !strcmp(argv[i], "-restrict_acl") || - !strcmp(argv[i], "-restrictacl")) { - restrict_process_acl(); - } else if (!strcmp(argv[i], "-restrict-putty-acl") || - !strcmp(argv[i], "-restrict_putty_acl")) { - restrict_putty_acl = true; - } else if (!strcmp(argv[i], "-c")) { - /* - * If we see `-c', then the rest of the - * command line should be treated as a - * command to be spawned. - */ - if (i < argc-1) - command = argstart[i+1]; - else - command = ""; - break; + char *p = argv[i]; + if (*p == '-' && doing_opts) { + if (!strcmp(p, "-pgpfp")) { + pgp_fingerprints_msgbox(NULL); + return 1; + } else if (!strcmp(p, "-restrict-acl") || + !strcmp(p, "-restrict_acl") || + !strcmp(p, "-restrictacl")) { + restrict_process_acl(); + } else if (!strcmp(p, "-restrict-putty-acl") || + !strcmp(p, "-restrict_putty_acl")) { + restrict_putty_acl = true; + } else if (!strcmp(p, "--no-decrypt") || + !strcmp(p, "-no-decrypt") || + !strcmp(p, "--no_decrypt") || + !strcmp(p, "-no_decrypt") || + !strcmp(p, "--nodecrypt") || + !strcmp(p, "-nodecrypt") || + !strcmp(p, "--encrypted") || + !strcmp(p, "-encrypted")) { + add_keys_encrypted = true; + } else if (!strcmp(p, "-keylist") || !strcmp(p, "--keylist")) { + show_keylist_on_startup = true; + } else if (!strcmp(p, "-c")) { + /* + * If we see `-c', then the rest of the + * command line should be treated as a + * command to be spawned. + */ + if (i < argc-1) + command = argstart[i+1]; + else + command = ""; + break; + } else if (!strcmp(p, "--")) { + doing_opts = false; + } else { + char *msg = dupprintf("unrecognised command-line option\n" + "'%s'", p); + MessageBox(NULL, msg, "Pageant command-line syntax error", + MB_ICONERROR | MB_OK); + exit(1); + } } else { - Filename *fn = filename_from_str(argv[i]); - win_add_keyfile(fn); + Filename *fn = filename_from_str(p); + win_add_keyfile(fn, add_keys_encrypted); filename_free(fn); added_keys = true; } @@ -1279,30 +1555,68 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) return 0; } +#if !defined NO_SECURITY + + /* + * Set up a named-pipe listener. + */ + { + Plug *pl_plug; + wpc->plc.vt = &winpgnt_vtable; + wpc->plc.suppress_logging = true; + struct pageant_listen_state *pl = + pageant_listener_new(&pl_plug, &wpc->plc); + char *pipename = agent_named_pipe_name(); + Socket *sock = new_named_pipe_listener(pipename, pl_plug); + if (sk_socket_error(sock)) { + char *err = dupprintf("Unable to open named pipe at %s " + "for SSH agent:\n%s", pipename, + sk_socket_error(sock)); + MessageBox(NULL, err, "Pageant Error", MB_ICONERROR | MB_OK); + return 1; + } + pageant_listener_got_socket(pl, sock); + sfree(pipename); + } + +#endif /* !defined NO_SECURITY */ + + /* + * Set up window classes for two hidden windows: one that receives + * all the messages to do with our presence in the system tray, + * and one that receives the WM_COPYDATA message used by the + * old-style Pageant IPC system. + */ + if (!prev) { - wndclass.style = 0; - wndclass.lpfnWndProc = WndProc; - wndclass.cbClsExtra = 0; - wndclass.cbWndExtra = 0; + WNDCLASS wndclass; + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.lpfnWndProc = TrayWndProc; wndclass.hInstance = inst; wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); - wndclass.hCursor = LoadCursor(NULL, IDC_IBEAM); - wndclass.hbrBackground = GetStockObject(BLACK_BRUSH); - wndclass.lpszMenuName = NULL; - wndclass.lpszClassName = APPNAME; + wndclass.lpszClassName = TRAYCLASSNAME; + + RegisterClass(&wndclass); + + memset(&wndclass, 0, sizeof(wndclass)); + wndclass.lpfnWndProc = wm_copydata_WndProc; + wndclass.hInstance = inst; + wndclass.lpszClassName = IPCCLASSNAME; RegisterClass(&wndclass); } keylist = NULL; - hwnd = CreateWindow(APPNAME, APPNAME, - WS_OVERLAPPEDWINDOW | WS_VSCROLL, - CW_USEDEFAULT, CW_USEDEFAULT, - 100, 100, NULL, NULL, inst, NULL); + traywindow = CreateWindow(TRAYCLASSNAME, TRAYWINTITLE, + WS_OVERLAPPEDWINDOW | WS_VSCROLL, + CW_USEDEFAULT, CW_USEDEFAULT, + 100, 100, NULL, NULL, inst, NULL); + winselgui_set_hwnd(traywindow); /* Set up a system tray icon */ - AddTrayIcon(hwnd); + AddTrayIcon(traywindow); /* Accelerators used: nsvkxa */ systray_menu = CreatePopupMenu(); @@ -1316,6 +1630,13 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys"); AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key"); + AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY_ENCRYPTED, + "Add key (encrypted)"); + AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); + AppendMenu(systray_menu, MF_ENABLED, IDM_REMOVE_ALL, + "Remove All Keys"); + AppendMenu(systray_menu, MF_ENABLED, IDM_REENCRYPT_ALL, + "Re-encrypt All Keys"); AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); if (has_help()) AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help"); @@ -1327,25 +1648,67 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) /* Set the default menu item. */ SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, false); - ShowWindow(hwnd, SW_HIDE); + ShowWindow(traywindow, SW_HIDE); + + wmcpc.vt = &wmcpc_vtable; + wmcpc.suppress_logging = true; + pageant_register_client(&wmcpc); + DWORD wm_copydata_threadid; + wmct.ev_msg_ready = CreateEvent(NULL, false, false, NULL); + wmct.ev_reply_ready = CreateEvent(NULL, false, false, NULL); + HANDLE hThread = CreateThread(NULL, 0, wm_copydata_threadfunc, + &inst, 0, &wm_copydata_threadid); + if (hThread) + CloseHandle(hThread); /* we don't need the thread handle */ + handle_add_foreign_event(wmct.ev_msg_ready, wm_copydata_got_msg, NULL); + + if (show_keylist_on_startup) + create_keylist_window(); /* * Main message loop. */ - while (GetMessage(&msg, NULL, 0, 0) == 1) { - if (!(IsWindow(keylist) && IsDialogMessage(keylist, &msg)) && - !(IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg))) { + while (true) { + HANDLE *handles; + int nhandles, n; + + handles = handle_get_events(&nhandles); + + n = MsgWaitForMultipleObjects(nhandles, handles, false, + INFINITE, QS_ALLINPUT); + + if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { + handle_got_event(handles[n - WAIT_OBJECT_0]); + sfree(handles); + } else + sfree(handles); + + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (msg.message == WM_QUIT) + goto finished; /* two-level break */ + + if (IsWindow(keylist) && IsDialogMessage(keylist, &msg)) + continue; + if (IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg)) + continue; + if (IsWindow(nonmodal_passphrase_hwnd) && + IsDialogMessage(nonmodal_passphrase_hwnd, &msg)) + continue; + TranslateMessage(&msg); DispatchMessage(&msg); } + + run_toplevel_callbacks(); } + finished: /* Clean up the system tray icon */ { NOTIFYICONDATA tnid; tnid.cbSize = sizeof(NOTIFYICONDATA); - tnid.hWnd = hwnd; + tnid.hWnd = traywindow; tnid.uID = 1; Shell_NotifyIcon(NIM_DELETE, &tnid); diff --git a/windows/winpgntc.c b/windows/winpgntc.c index 7166585..557dc53 100644 --- a/windows/winpgntc.c +++ b/windows/winpgntc.c @@ -11,11 +11,12 @@ #ifndef NO_SECURITY #include "winsecur.h" +#include "wincapi.h" #endif #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ -bool agent_exists(void) +static bool wm_copydata_agent_exists(void) { HWND hwnd; hwnd = FindWindow("Pageant", "Pageant"); @@ -25,14 +26,7 @@ bool agent_exists(void) return true; } -void agent_cancel_query(agent_pending_query *q) -{ - unreachable("Windows agent queries are never asynchronous!"); -} - -agent_pending_query *agent_query( - strbuf *query, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) +static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen) { HWND hwnd; char *mapname; @@ -48,11 +42,11 @@ agent_pending_query *agent_query( *outlen = 0; if (query->len > AGENT_MAX_MSGLEN) - return NULL; /* query too large */ + return; /* query too large */ hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) - return NULL; /* *out == NULL, so failure */ + return; /* *out == NULL, so failure */ mapname = dupprintf("PageantRequest%08x", (unsigned)GetCurrentThreadId()); psa = NULL; @@ -93,7 +87,7 @@ agent_pending_query *agent_query( 0, AGENT_MAX_MSGLEN, mapname); if (filemap == NULL || filemap == INVALID_HANDLE_VALUE) { sfree(mapname); - return NULL; /* *out == NULL, so failure */ + return; /* *out == NULL, so failure */ } p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); strbuf_finalise_agent_query(query); @@ -133,5 +127,222 @@ agent_pending_query *agent_query( sfree(mapname); if (psd) LocalFree(psd); +} + +#ifndef NO_SECURITY + +char *agent_named_pipe_name(void) +{ + char *username, *suffix, *pipename; + username = get_username(); + suffix = capi_obfuscate_string("Pageant"); + pipename = dupprintf("\\\\.\\pipe\\pageant.%s.%s", username, suffix); + sfree(username); + sfree(suffix); + return pipename; +} + +Socket *agent_connect(Plug *plug) +{ + char *pipename = agent_named_pipe_name(); + Socket *s = new_named_pipe_client(pipename, plug); + sfree(pipename); + return s; +} + +static bool named_pipe_agent_exists(void) +{ + char *pipename = agent_named_pipe_name(); + WIN32_FIND_DATA data; + HANDLE ffh = FindFirstFile(pipename, &data); + sfree(pipename); + if (ffh == INVALID_HANDLE_VALUE) + return false; + FindClose(ffh); + return true; +} + +bool agent_exists(void) +{ + return named_pipe_agent_exists() || wm_copydata_agent_exists(); +} + +struct agent_pending_query { + struct handle *handle; + HANDLE os_handle; + strbuf *response; + void (*callback)(void *, void *, int); + void *callback_ctx; +}; + +static int named_pipe_agent_accumulate_response( + strbuf *sb, const void *data, size_t len) +{ + put_data(sb, data, len); + if (sb->len >= 4) { + uint32_t length_field = GET_32BIT_MSB_FIRST(sb->u); + if (length_field > AGENT_MAX_MSGLEN) + return -1; /* badly formatted message */ + + int overall_length = length_field + 4; + if (sb->len >= overall_length) + return overall_length; + } + + return 0; /* not done yet */ +} + +static size_t named_pipe_agent_gotdata( + struct handle *h, const void *data, size_t len, int err) +{ + agent_pending_query *pq = handle_get_privdata(h); + + if (err || len == 0) { + pq->callback(pq->callback_ctx, NULL, 0); + agent_cancel_query(pq); + return 0; + } + + int status = named_pipe_agent_accumulate_response(pq->response, data, len); + if (status == -1) { + pq->callback(pq->callback_ctx, NULL, 0); + agent_cancel_query(pq); + } else if (status > 0) { + void *response_buf = strbuf_to_str(pq->response); + pq->response = NULL; + pq->callback(pq->callback_ctx, response_buf, status); + agent_cancel_query(pq); + } + return 0; +} + +static agent_pending_query *named_pipe_agent_query( + strbuf *query, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) +{ + agent_pending_query *pq = NULL; + char *err = NULL, *pipename = NULL; + strbuf *sb = NULL; + HANDLE pipehandle; + + pipename = agent_named_pipe_name(); + pipehandle = connect_to_named_pipe(pipename, &err); + if (pipehandle == INVALID_HANDLE_VALUE) + goto failure; + + strbuf_finalise_agent_query(query); + + for (DWORD done = 0; done < query->len ;) { + DWORD nwritten; + bool ret = WriteFile(pipehandle, query->s + done, query->len - done, + &nwritten, NULL); + if (!ret) + goto failure; + + done += nwritten; + } + + if (!callback) { + int status; + + sb = strbuf_new_nm(); + do { + char buf[1024]; + DWORD nread; + bool ret = ReadFile(pipehandle, buf, sizeof(buf), &nread, NULL); + if (!ret) + goto failure; + status = named_pipe_agent_accumulate_response(sb, buf, nread); + } while (status == 0); + + if (status == -1) + goto failure; + + *out = strbuf_to_str(sb); + *outlen = status; + sb = NULL; + pq = NULL; + goto out; + } + + pq = snew(agent_pending_query); + pq->handle = handle_input_new(pipehandle, named_pipe_agent_gotdata, pq, 0); + pq->os_handle = pipehandle; + pipehandle = INVALID_HANDLE_VALUE; /* prevent it being closed below */ + pq->response = strbuf_new_nm(); + pq->callback = callback; + pq->callback_ctx = callback_ctx; + goto out; + + failure: + *out = NULL; + *outlen = 0; + pq = NULL; + + out: + sfree(err); + sfree(pipename); + if (pipehandle != INVALID_HANDLE_VALUE) + CloseHandle(pipehandle); + if (sb) + strbuf_free(sb); + return pq; +} + +void agent_cancel_query(agent_pending_query *pq) +{ + handle_free(pq->handle); + CloseHandle(pq->os_handle); + if (pq->response) + strbuf_free(pq->response); + sfree(pq); +} + +agent_pending_query *agent_query( + strbuf *query, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) +{ + agent_pending_query *pq = named_pipe_agent_query( + query, out, outlen, callback, callback_ctx); + if (pq || *out) + return pq; + + wm_copydata_agent_query(query, out, outlen); return NULL; } + +#else /* NO_SECURITY */ + +Socket *agent_connect(void *vctx, Plug *plug) +{ + unreachable("no agent_connect_ctx can be constructed on this platform"); +} + +agent_connect_ctx *agent_get_connect_ctx(void) +{ + return NULL; +} + +void agent_free_connect_ctx(agent_connect_ctx *ctx) +{ +} + +bool agent_exists(void) +{ + return wm_copydata_agent_exists(); +} + +agent_pending_query *agent_query( + strbuf *query, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) +{ + wm_copydata_agent_query(query, out, outlen); + return NULL; +} + +void agent_cancel_query(agent_pending_query *q) +{ + unreachable("Windows agent queries are never asynchronous!"); +} + +#endif /* NO_SECURITY */ diff --git a/windows/winplink.c b/windows/winplink.c index dce5691..58d43e6 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -7,21 +7,11 @@ #include #include -#define PUTTY_DO_GLOBALS /* actually _define_ globals */ #include "putty.h" #include "storage.h" #include "tree234.h" #include "winsecur.h" -#define WM_AGENT_CALLBACK (WM_APP + 4) - -struct agent_callback { - void (*callback)(void *, void *, int); - void *callback_ctx; - void *data; - int len; -}; - void cmdline_error(const char *fmt, ...) { va_list ap; @@ -31,17 +21,16 @@ void cmdline_error(const char *fmt, ...) exit(1); } -HANDLE inhandle, outhandle, errhandle; -struct handle *stdin_handle, *stdout_handle, *stderr_handle; -handle_sink stdout_hs, stderr_hs; -StripCtrlChars *stdout_scc, *stderr_scc; -BinarySink *stdout_bs, *stderr_bs; -DWORD orig_console_mode; - -WSAEVENT netevent; +static HANDLE inhandle, outhandle, errhandle; +static struct handle *stdin_handle, *stdout_handle, *stderr_handle; +static handle_sink stdout_hs, stderr_hs; +static StripCtrlChars *stdout_scc, *stderr_scc; +static BinarySink *stdout_bs, *stderr_bs; +static DWORD orig_console_mode; static Backend *backend; -Conf *conf; +static LogContext *logctx; +static Conf *conf; static void plink_echoedit_update(Seat *seat, bool echo, bool edit) { @@ -84,41 +73,40 @@ static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) return ret; } +static bool plink_seat_interactive(Seat *seat) +{ + return (!*conf_get_str(conf, CONF_remote_cmd) && + !*conf_get_str(conf, CONF_remote_cmd2) && + !*conf_get_str(conf, CONF_ssh_nc_host)); +} + static const SeatVtable plink_seat_vt = { - plink_output, - plink_eof, - plink_get_userpass_input, - nullseat_notify_remote_exit, - console_connection_fatal, - nullseat_update_specials_menu, - nullseat_get_ttymode, - nullseat_set_busy_status, - console_verify_ssh_host_key, - console_confirm_weak_crypto_primitive, - console_confirm_weak_cached_hostkey, - nullseat_is_never_utf8, - plink_echoedit_update, - nullseat_get_x_display, - nullseat_get_windowid, - nullseat_get_window_pixel_size, - console_stripctrl_new, - console_set_trust_status, + .output = plink_output, + .eof = plink_eof, + .get_userpass_input = plink_get_userpass_input, + .notify_remote_exit = nullseat_notify_remote_exit, + .connection_fatal = console_connection_fatal, + .update_specials_menu = nullseat_update_specials_menu, + .get_ttymode = nullseat_get_ttymode, + .set_busy_status = nullseat_set_busy_status, + .verify_ssh_host_key = console_verify_ssh_host_key, + .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, + .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, + .is_utf8 = nullseat_is_never_utf8, + .echoedit_update = plink_echoedit_update, + .get_x_display = nullseat_get_x_display, + .get_windowid = nullseat_get_windowid, + .get_window_pixel_size = nullseat_get_window_pixel_size, + .stripctrl_new = console_stripctrl_new, + .set_trust_status = console_set_trust_status, + .verbose = cmdline_seat_verbose, + .interactive = plink_seat_interactive, + .get_cursor_position = nullseat_get_cursor_position, }; static Seat plink_seat[1] = {{ &plink_seat_vt }}; static DWORD main_thread_id; -void agent_schedule_callback(void (*callback)(void *, void *, int), - void *callback_ctx, void *data, int len) -{ - struct agent_callback *c = snew(struct agent_callback); - c->callback = callback; - c->callback_ctx = callback_ctx; - c->data = data; - c->len = len; - PostThreadMessage(main_thread_id, WM_AGENT_CALLBACK, 0, (LPARAM)c); -} - /* * Short description of parameters. */ @@ -135,6 +123,8 @@ static void usage(void) printf(" -load sessname Load settings from saved session\n"); printf(" -ssh -telnet -rlogin -raw -serial\n"); printf(" force use of a particular protocol\n"); + printf(" -ssh-connection\n"); + printf(" force use of the bare ssh-connection protocol\n"); printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -batch disable all interactive prompts\n"); @@ -153,15 +143,17 @@ static void usage(void) printf(" -X -x enable / disable X11 forwarding\n"); printf(" -A -a enable / disable agent forwarding\n"); printf(" -t -T enable / disable pty allocation\n"); - printf(" -1 -2 force use of particular protocol version\n"); + printf(" -1 -2 force use of particular SSH protocol version\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); + printf(" -no-trivial-auth\n"); + printf(" disconnect if SSH authentication succeeds trivially\n"); printf(" -noshare disable use of connection sharing\n"); printf(" -share enable use of connection sharing\n"); - printf(" -hostkey aa:bb:cc:...\n"); + printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -sanitise-stderr, -sanitise-stdout, " "-no-sanitise-stderr, -no-sanitise-stdout\n"); @@ -177,6 +169,9 @@ static void usage(void) printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); + printf(" -logoverwrite\n"); + printf(" -logappend\n"); + printf(" control what happens when a log file already exists\n"); printf(" -shareexists\n"); printf(" test whether a connection-sharing upstream exists\n"); exit(1); @@ -190,26 +185,6 @@ static void version(void) exit(0); } -char *do_select(SOCKET skt, bool startup) -{ - int events; - if (startup) { - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - } else { - events = 0; - } - if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) { - switch (p_WSAGetLastError()) { - case WSAENETDOWN: - return "Network is down"; - default: - return "WSAEventSelect(): unknown error"; - } - } - return NULL; -} - size_t stdin_gotdata(struct handle *h, const void *data, size_t len, int err) { if (err) { @@ -258,45 +233,63 @@ void stdouterr_sent(struct handle *h, size_t new_backlog, int err) const bool share_can_be_downstream = true; const bool share_can_be_upstream = true; +const unsigned cmdline_tooltype = + TOOLTYPE_HOST_ARG | + TOOLTYPE_HOST_ARG_CAN_BE_SESSION | + TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | + TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; + +static bool sending; + +static bool plink_mainloop_pre(void *vctx, const HANDLE **extra_handles, + size_t *n_extra_handles) +{ + if (!sending && backend_sendok(backend)) { + stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, + 0); + sending = true; + } + + return true; +} + +static bool plink_mainloop_post(void *vctx, size_t extra_handle_index) +{ + if (sending) + handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); + + if (!backend_connected(backend) && + handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) + return false; /* we closed the connection */ + + return true; +} + int main(int argc, char **argv) { - bool sending; - SOCKET *sklist; - size_t skcount, sksize; int exitcode; bool errors; bool use_subsystem = false; bool just_test_share_exists = false; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; - unsigned long now, next, then; const struct BackendVtable *vt; dll_hijacking_protection(); - sklist = NULL; - skcount = sksize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ - default_protocol = PROT_SSH; - default_port = 22; - - flags = 0; - cmdline_tooltype |= - (TOOLTYPE_HOST_ARG | - TOOLTYPE_HOST_ARG_CAN_BE_SESSION | - TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | - TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD); + settings_set_default_protocol(PROT_SSH); + settings_set_default_port(22); /* * Process the command line. */ conf = conf_new(); do_defaults(NULL, conf); - loaded_session = false; - default_protocol = conf_get_int(conf, CONF_protocol); - default_port = conf_get_int(conf, CONF_port); + settings_set_default_protocol(conf_get_int(conf, CONF_protocol)); + settings_set_default_port(conf_get_int(conf, CONF_port)); errors = false; { /* @@ -306,10 +299,10 @@ int main(int argc, char **argv) if (p) { const struct BackendVtable *vt = backend_vt_from_name(p); if (vt) { - default_protocol = vt->protocol; - default_port = vt->default_port; - conf_set_int(conf, CONF_protocol, default_protocol); - conf_set_int(conf, CONF_port, default_port); + settings_set_default_protocol(vt->protocol); + settings_set_default_port(vt->default_port); + conf_set_int(conf, CONF_protocol, vt->protocol); + conf_set_int(conf, CONF_port, vt->default_port); } } } @@ -396,11 +389,6 @@ int main(int argc, char **argv) if (use_subsystem) conf_set_bool(conf, CONF_ssh_subsys, true); - if (!*conf_get_str(conf, CONF_remote_cmd) && - !*conf_get_str(conf, CONF_remote_cmd2) && - !*conf_get_str(conf, CONF_ssh_nc_host)) - flags |= FLAG_INTERACTIVE; - /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. @@ -412,6 +400,13 @@ int main(int argc, char **argv) return 1; } + if (vt->flags & BACKEND_NEEDS_TERMINAL) { + fprintf(stderr, + "Plink doesn't support %s, which needs terminal emulation\n", + vt->displayname); + return 1; + } + sk_init(); if (p_WSAEventSelect == NULL) { fprintf(stderr, "Plink requires WinSock 2\n"); @@ -429,12 +424,12 @@ int main(int argc, char **argv) !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) conf_set_bool(conf, CONF_ssh_simple, true); - logctx = log_init(default_logpolicy, conf); + logctx = log_init(console_cli_logpolicy, conf); if (just_test_share_exists) { if (!vt->test_for_upstream) { - fprintf(stderr, "Connection sharing not supported for connection " - "type '%s'\n", vt->name); + fprintf(stderr, "Connection sharing not supported for this " + "connection type (%s)'\n", vt->displayname); return 1; } if (vt->test_for_upstream(conf_get_str(conf, CONF_host), @@ -444,8 +439,9 @@ int main(int argc, char **argv) return 1; } - if (restricted_acl) { - lp_eventlog(default_logpolicy, "Running with restricted process ACL"); + if (restricted_acl()) { + lp_eventlog(console_cli_logpolicy, + "Running with restricted process ACL"); } inhandle = GetStdHandle(STD_INPUT_HANDLE); @@ -502,10 +498,9 @@ int main(int argc, char **argv) /* * Start up the connection. */ - netevent = CreateEvent(NULL, false, false, NULL); + winselcli_setup(); /* ensure event object exists */ { - const char *error; - char *realhost; + char *error, *realhost; /* nodelay is only useful if stdin is a character device (console) */ bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); @@ -517,8 +512,10 @@ int main(int argc, char **argv) conf_get_bool(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); + sfree(error); return 1; } + ldisc_create(conf, NULL, backend, plink_seat); sfree(realhost); } @@ -526,126 +523,8 @@ int main(int argc, char **argv) sending = false; - now = GETTICKCOUNT(); - - while (1) { - int nhandles; - HANDLE *handles; - int n; - DWORD ticks; - - if (!sending && backend_sendok(backend)) { - stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, - 0); - sending = true; - } - - if (toplevel_callback_pending()) { - ticks = 0; - next = now; - } else if (run_timers(now, &next)) { - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - } else { - ticks = INFINITE; - /* no need to initialise next here because we can never - * get WAIT_TIMEOUT */ - } - - handles = handle_get_events(&nhandles); - handles = sresize(handles, nhandles+1, HANDLE); - handles[nhandles] = netevent; - n = MsgWaitForMultipleObjects(nhandles+1, handles, false, ticks, - QS_POSTMESSAGE); - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - } else if (n == WAIT_OBJECT_0 + nhandles) { - WSANETWORKEVENTS things; - SOCKET socket; - int i, socketstate; - - /* - * We must not call select_result() for any socket - * until we have finished enumerating within the tree. - * This is because select_result() may close the socket - * and modify the tree. - */ - /* Count the active sockets. */ - i = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) i++; - - /* Expand the buffer if necessary. */ - sgrowarray(sklist, sksize, i); - - /* Retrieve the sockets into sklist. */ - skcount = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) { - sklist[skcount++] = socket; - } - - /* Now we're done enumerating; go through the list. */ - for (i = 0; i < skcount; i++) { - WPARAM wp; - socket = sklist[i]; - wp = (WPARAM) socket; - if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { - static const struct { int bit, mask; } eventtypes[] = { - {FD_CONNECT_BIT, FD_CONNECT}, - {FD_READ_BIT, FD_READ}, - {FD_CLOSE_BIT, FD_CLOSE}, - {FD_OOB_BIT, FD_OOB}, - {FD_WRITE_BIT, FD_WRITE}, - {FD_ACCEPT_BIT, FD_ACCEPT}, - }; - int e; - - noise_ultralight(NOISE_SOURCE_IOID, socket); - - for (e = 0; e < lenof(eventtypes); e++) - if (things.lNetworkEvents & eventtypes[e].mask) { - LPARAM lp; - int err = things.iErrorCode[eventtypes[e].bit]; - lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); - select_result(wp, lp); - } - } - } - } else if (n == WAIT_OBJECT_0 + nhandles + 1) { - MSG msg; - while (PeekMessage(&msg, INVALID_HANDLE_VALUE, - WM_AGENT_CALLBACK, WM_AGENT_CALLBACK, - PM_REMOVE)) { - struct agent_callback *c = (struct agent_callback *)msg.lParam; - c->callback(c->callback_ctx, c->data, c->len); - sfree(c); - } - } - - run_toplevel_callbacks(); + cli_main_loop(plink_mainloop_pre, plink_mainloop_post, NULL); - if (n == WAIT_TIMEOUT) { - now = next; - } else { - now = GETTICKCOUNT(); - } - - sfree(handles); - - if (sending) - handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); - - if (!backend_connected(backend) && - handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) - break; /* we closed the connection */ - } exitcode = backend_exitcode(backend); if (exitcode < 0) { fprintf(stderr, "Remote process exit code unavailable\n"); diff --git a/windows/winproxy.c b/windows/winproxy.c index 24ca261..94e31fc 100644 --- a/windows/winproxy.c +++ b/windows/winproxy.c @@ -35,7 +35,7 @@ Socket *platform_new_connection(SockAddr *addr, const char *hostname, { char *msg = dupprintf("Starting local proxy command: %s", cmd); - plug_log(plug, 2, NULL, 0, msg, 0); + plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); sfree(msg); } diff --git a/windows/winseat.h b/windows/winseat.h new file mode 100644 index 0000000..c6b5fa9 --- /dev/null +++ b/windows/winseat.h @@ -0,0 +1,14 @@ +/* + * Small implementation of Seat and LogPolicy shared between window.c + * and windlg.c. + */ + +typedef struct WinGuiSeat WinGuiSeat; + +struct WinGuiSeat { + HWND term_hwnd; + Seat seat; + LogPolicy logpolicy; +}; + +extern const LogPolicyVtable win_gui_logpolicy_vt; /* in windlg.c */ diff --git a/windows/winsecur.c b/windows/winsecur.c index 3926e33..a1164af 100644 --- a/windows/winsecur.c +++ b/windows/winsecur.c @@ -9,12 +9,18 @@ #if !defined NO_SECURITY -#define WINSECUR_GLOBAL #include "winsecur.h" /* Initialised once, then kept around to reuse forever */ static PSID worldsid, networksid, usersid; +DEF_WINDOWS_FUNCTION(OpenProcessToken); +DEF_WINDOWS_FUNCTION(GetTokenInformation); +DEF_WINDOWS_FUNCTION(InitializeSecurityDescriptor); +DEF_WINDOWS_FUNCTION(SetSecurityDescriptorOwner); +DEF_WINDOWS_FUNCTION(GetSecurityInfo); +DEF_WINDOWS_FUNCTION(SetSecurityInfo); +DEF_WINDOWS_FUNCTION(SetEntriesInAclA); bool got_advapi(void) { @@ -92,7 +98,7 @@ PSID get_user_sid(void) return ret; } -bool getsids(char **error) +static bool getsids(char **error) { #ifdef __clang__ #pragma clang diagnostic push @@ -228,6 +234,9 @@ bool make_private_security_descriptor(DWORD permissions, return ret; } +static bool acl_restricted = false; +bool restricted_acl(void) { return acl_restricted; } + static bool really_restrict_process_acl(char **error) { EXPLICIT_ACCESS ea[2]; @@ -278,7 +287,7 @@ static bool really_restrict_process_acl(char **error) goto cleanup; } - + acl_restricted = true; ret=true; cleanup: diff --git a/windows/winsecur.h b/windows/winsecur.h index d5d4cfb..fdd39d8 100644 --- a/windows/winsecur.h +++ b/windows/winsecur.h @@ -8,30 +8,26 @@ #include -#ifndef WINSECUR_GLOBAL -#define WINSECUR_GLOBAL extern -#endif - /* * Functions loaded from advapi32.dll. */ -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, OpenProcessToken, +DECL_WINDOWS_FUNCTION(extern, BOOL, OpenProcessToken, (HANDLE, DWORD, PHANDLE)); -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, GetTokenInformation, +DECL_WINDOWS_FUNCTION(extern, BOOL, GetTokenInformation, (HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD)); -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, InitializeSecurityDescriptor, +DECL_WINDOWS_FUNCTION(extern, BOOL, InitializeSecurityDescriptor, (PSECURITY_DESCRIPTOR, DWORD)); -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, SetSecurityDescriptorOwner, +DECL_WINDOWS_FUNCTION(extern, BOOL, SetSecurityDescriptorOwner, (PSECURITY_DESCRIPTOR, PSID, BOOL)); -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, GetSecurityInfo, +DECL_WINDOWS_FUNCTION(extern, DWORD, GetSecurityInfo, (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *)); -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetSecurityInfo, +DECL_WINDOWS_FUNCTION(extern, DWORD, SetSecurityInfo, (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL)); -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetEntriesInAclA, +DECL_WINDOWS_FUNCTION(extern, DWORD, SetEntriesInAclA, (ULONG, PEXPLICIT_ACCESS, PACL, PACL *)); bool got_advapi(void); diff --git a/windows/winselcli.c b/windows/winselcli.c new file mode 100644 index 0000000..f19a0bb --- /dev/null +++ b/windows/winselcli.c @@ -0,0 +1,78 @@ +/* + * Implementation of do_select() for winnet.c to use, suitable for use + * when there's no GUI window to have network activity reported to. + * + * It uses WSAEventSelect, where available, to convert network + * activity into activity on an event object, for integration into an + * event loop that includes WaitForMultipleObjects. + * + * It also maintains a list of currently active sockets, which can be + * retrieved by a front end that wants to use WinSock's synchronous + * select() function. + */ + +#include "putty.h" + +static tree234 *winselcli_sockets; + +static int socket_cmp(void *av, void *bv) +{ + return memcmp(av, bv, sizeof(SOCKET)); +} + +HANDLE winselcli_event = INVALID_HANDLE_VALUE; + +void winselcli_setup(void) +{ + if (!winselcli_sockets) + winselcli_sockets = newtree234(socket_cmp); + + if (p_WSAEventSelect && winselcli_event == INVALID_HANDLE_VALUE) + winselcli_event = CreateEvent(NULL, false, false, NULL); +} + +SOCKET winselcli_unique_socket(void) +{ + if (!winselcli_sockets) + return INVALID_SOCKET; + + assert(count234(winselcli_sockets) <= 1); + + SOCKET *p = index234(winselcli_sockets, 0); + if (!p) + return INVALID_SOCKET; + + return *p; +} + +const char *do_select(SOCKET skt, bool enable) +{ + /* Check everything's been set up, for convenience of callers. */ + winselcli_setup(); + + if (enable) { + SOCKET *ptr = snew(SOCKET); + *ptr = skt; + if (add234(winselcli_sockets, ptr) != ptr) + sfree(ptr); /* already there */ + } else { + SOCKET *ptr = del234(winselcli_sockets, &skt); + if (ptr) + sfree(ptr); + } + + if (p_WSAEventSelect) { + int events; + if (enable) { + events = (FD_CONNECT | FD_READ | FD_WRITE | + FD_OOB | FD_CLOSE | FD_ACCEPT); + } else { + events = 0; + } + + if (p_WSAEventSelect(skt, winselcli_event, events) == SOCKET_ERROR) + return winsock_error_string(p_WSAGetLastError()); + } + + return NULL; +} diff --git a/windows/winselgui.c b/windows/winselgui.c new file mode 100644 index 0000000..48a1521 --- /dev/null +++ b/windows/winselgui.c @@ -0,0 +1,38 @@ +/* + * Implementation of do_select() for winnet.c to use, that uses + * WSAAsyncSelect to convert network activity into window messages, + * for integration into a GUI event loop. + */ + +#include "putty.h" + +static HWND winsel_hwnd = NULL; + +void winselgui_set_hwnd(HWND hwnd) +{ + winsel_hwnd = hwnd; +} + +void winselgui_clear_hwnd(void) +{ + winsel_hwnd = NULL; +} + +const char *do_select(SOCKET skt, bool enable) +{ + int msg, events; + if (enable) { + msg = WM_NETEVENT; + events = (FD_CONNECT | FD_READ | FD_WRITE | + FD_OOB | FD_CLOSE | FD_ACCEPT); + } else { + msg = events = 0; + } + + assert(winsel_hwnd); + + if (p_WSAAsyncSelect(skt, winsel_hwnd, msg, events) == SOCKET_ERROR) + return winsock_error_string(p_WSAGetLastError()); + + return NULL; +} diff --git a/windows/winser.c b/windows/winser.c index d87ca6c..7f4bcf2 100644 --- a/windows/winser.c +++ b/windows/winser.c @@ -91,7 +91,7 @@ static void serial_sentdata(struct handle *h, size_t new_backlog, int err) } } -static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) +static char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) { DCB dcb; COMMTIMEOUTS timeouts; @@ -125,18 +125,21 @@ static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) * Configurable parameters. */ dcb.BaudRate = conf_get_int(conf, CONF_serspeed); - logeventf(serial->logctx, "Configuring baud rate %lu", dcb.BaudRate); + logeventf(serial->logctx, "Configuring baud rate %lu", + (unsigned long)dcb.BaudRate); dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); - logeventf(serial->logctx, "Configuring %u data bits", dcb.ByteSize); + logeventf(serial->logctx, "Configuring %u data bits", + (unsigned)dcb.ByteSize); switch (conf_get_int(conf, CONF_serstopbits)) { - case 2: dcb.StopBits = ONESTOPBIT; str = "1"; break; - case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5"; break; - case 4: dcb.StopBits = TWOSTOPBITS; str = "2"; break; - default: return "Invalid number of stop bits (need 1, 1.5 or 2)"; + case 2: dcb.StopBits = ONESTOPBIT; str = "1 stop bit"; break; + case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5 stop bits"; break; + case 4: dcb.StopBits = TWOSTOPBITS; str = "2 stop bits"; break; + default: return dupstr("Invalid number of stop bits " + "(need 1, 1.5 or 2)"); } - logeventf(serial->logctx, "Configuring %s data bits", str); + logeventf(serial->logctx, "Configuring %s", str); switch (conf_get_int(conf, CONF_serparity)) { case SER_PAR_NONE: dcb.Parity = NOPARITY; str = "no"; break; @@ -169,7 +172,8 @@ static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) logeventf(serial->logctx, "Configuring %s flow control", str); if (!SetCommState(serport, &dcb)) - return "Unable to configure serial port"; + return dupprintf("Configuring serial port: %s", + win_strerror(GetLastError())); timeouts.ReadIntervalTimeout = 1; timeouts.ReadTotalTimeoutMultiplier = 0; @@ -177,7 +181,8 @@ static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(serport, &timeouts)) - return "Unable to configure serial timeouts"; + return dupprintf("Configuring serial timeouts: %s", + win_strerror(GetLastError())); } return NULL; @@ -191,14 +196,14 @@ static const char *serial_configure(Serial *serial, HANDLE serport, Conf *conf) * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ -static const char *serial_init(Seat *seat, Backend **backend_handle, - LogContext *logctx, Conf *conf, - const char *host, int port, - char **realhost, bool nodelay, bool keepalive) +static char *serial_init(const BackendVtable *vt, Seat *seat, + Backend **backend_handle, LogContext *logctx, + Conf *conf, const char *host, int port, + char **realhost, bool nodelay, bool keepalive) { Serial *serial; HANDLE serport; - const char *err; + char *err; char *serline; /* No local authentication phase in this protocol */ @@ -209,7 +214,7 @@ static const char *serial_init(Seat *seat, Backend **backend_handle, serial->out = serial->in = NULL; serial->bufsize = 0; serial->break_in_progress = false; - serial->backend.vt = &serial_backend; + serial->backend.vt = vt; *backend_handle = &serial->backend; serial->seat = seat; @@ -218,39 +223,41 @@ static const char *serial_init(Seat *seat, Backend **backend_handle, serline = conf_get_str(conf, CONF_serline); logeventf(serial->logctx, "Opening serial device %s", serline); - { - /* - * Munge the string supplied by the user into a Windows filename. - * - * Windows supports opening a few "legacy" devices (including - * COM1-9) by specifying their names verbatim as a filename to - * open. (Thus, no files can ever have these names. See - * - * ("Naming a File") for the complete list of reserved names.) - * - * However, this doesn't let you get at devices COM10 and above. - * For that, you need to specify a filename like "\\.\COM10". - * This is also necessary for special serial and serial-like - * devices such as \\.\WCEUSBSH001. It also works for the "legacy" - * names, so you can do \\.\COM1 (verified as far back as Win95). - * See - * (CreateFile() docs). - * - * So, we believe that prepending "\\.\" should always be the - * Right Thing. However, just in case someone finds something to - * talk to that doesn't exist under there, if the serial line - * contains a backslash, we use it verbatim. (This also lets - * existing configurations using \\.\ continue working.) - */ - char *serfilename = - dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); - serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + /* + * Munge the string supplied by the user into a Windows filename. + * + * Windows supports opening a few "legacy" devices (including + * COM1-9) by specifying their names verbatim as a filename to + * open. (Thus, no files can ever have these names. See + * + * ("Naming a File") for the complete list of reserved names.) + * + * However, this doesn't let you get at devices COM10 and above. + * For that, you need to specify a filename like "\\.\COM10". + * This is also necessary for special serial and serial-like + * devices such as \\.\WCEUSBSH001. It also works for the "legacy" + * names, so you can do \\.\COM1 (verified as far back as Win95). + * See + * (CreateFile() docs). + * + * So, we believe that prepending "\\.\" should always be the + * Right Thing. However, just in case someone finds something to + * talk to that doesn't exist under there, if the serial line + * contains a backslash, we use it verbatim. (This also lets + * existing configurations using \\.\ continue working.) + */ + char *serfilename = + dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); + serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (serport == INVALID_HANDLE_VALUE) { + err = dupprintf("Opening '%s': %s", + serfilename, win_strerror(GetLastError())); sfree(serfilename); + return err; } - if (serport == INVALID_HANDLE_VALUE) - return "Unable to open serial port"; + sfree(serfilename); err = serial_configure(serial, serport, conf); if (err) @@ -427,24 +434,32 @@ static int serial_cfg_info(Backend *be) return 0; } -const struct BackendVtable serial_backend = { - serial_init, - serial_free, - serial_reconfig, - serial_send, - serial_sendbuffer, - serial_size, - serial_special, - serial_get_specials, - serial_connected, - serial_exitcode, - serial_sendok, - serial_ldisc, - serial_provide_ldisc, - serial_unthrottle, - serial_cfg_info, - NULL /* test_for_upstream */, - "serial", - PROT_SERIAL, - 0 +const BackendVtable serial_backend = { + .init = serial_init, + .free = serial_free, + .reconfig = serial_reconfig, + .send = serial_send, + .sendbuffer = serial_sendbuffer, + .size = serial_size, + .special = serial_special, + .get_specials = serial_get_specials, + .connected = serial_connected, + .exitcode = serial_exitcode, + .sendok = serial_sendok, + .ldisc_option_state = serial_ldisc, + .provide_ldisc = serial_provide_ldisc, + .unthrottle = serial_unthrottle, + .cfg_info = serial_cfg_info, + .id = "serial", + .displayname = "Serial", + .protocol = PROT_SERIAL, + .serial_parity_mask = ((1 << SER_PAR_NONE) | + (1 << SER_PAR_ODD) | + (1 << SER_PAR_EVEN) | + (1 << SER_PAR_MARK) | + (1 << SER_PAR_SPACE)), + .serial_flow_mask = ((1 << SER_FLOW_NONE) | + (1 << SER_FLOW_XONXOFF) | + (1 << SER_FLOW_RTSCTS) | + (1 << SER_FLOW_DSRDTR)), }; diff --git a/windows/winsftp.c b/windows/winsftp.c index ab32340..0c695d2 100644 --- a/windows/winsftp.c +++ b/windows/winsftp.c @@ -274,7 +274,7 @@ DirHandle *open_directory(const char *name, const char **errmsg) DirHandle *ret; /* Enumerate files in dir `foo'. */ - findfile = dupcat(name, "/*", NULL); + findfile = dupcat(name, "/*"); h = FindFirstFile(findfile, &fdat); if (h == INVALID_HANDLE_VALUE) { *errmsg = win_strerror(GetLastError()); @@ -395,7 +395,7 @@ WildcardMatcher *begin_wildcard_matching(const char *name) (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) ret->name = NULL; else - ret->name = dupcat(ret->srcpath, fdat.cFileName, NULL); + ret->name = dupcat(ret->srcpath, fdat.cFileName); return ret; } @@ -413,7 +413,7 @@ char *wildcard_get_filename(WildcardMatcher *dir) (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) dir->name = NULL; else - dir->name = dupcat(dir->srcpath, fdat.cFileName, NULL); + dir->name = dupcat(dir->srcpath, fdat.cFileName); } if (dir->name) { @@ -455,162 +455,45 @@ char *dir_file_cat(const char *dir, const char *file) return dupcat( dir, (ptrlen_endswith(dir_pl, PTRLEN_LITERAL("\\"), NULL) || ptrlen_endswith(dir_pl, PTRLEN_LITERAL("/"), NULL)) ? "" : "\\", - file, NULL); + file); } /* ---------------------------------------------------------------------- * Platform-specific network handling. */ - -/* - * Be told what socket we're supposed to be using. - */ -static SOCKET sftp_ssh_socket = INVALID_SOCKET; -static HANDLE netevent = INVALID_HANDLE_VALUE; -char *do_select(SOCKET skt, bool startup) +struct winsftp_cliloop_ctx { + HANDLE other_event; + int toret; +}; +static bool winsftp_cliloop_pre(void *vctx, const HANDLE **extra_handles, + size_t *n_extra_handles) { - int events; - if (startup) - sftp_ssh_socket = skt; - else - sftp_ssh_socket = INVALID_SOCKET; - - if (p_WSAEventSelect) { - if (startup) { - events = (FD_CONNECT | FD_READ | FD_WRITE | - FD_OOB | FD_CLOSE | FD_ACCEPT); - netevent = CreateEvent(NULL, false, false, NULL); - } else { - events = 0; - } - if (p_WSAEventSelect(skt, netevent, events) == SOCKET_ERROR) { - switch (p_WSAGetLastError()) { - case WSAENETDOWN: - return "Network is down"; - default: - return "WSAEventSelect(): unknown error"; - } - } - } - return NULL; -} + struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; -int do_eventsel_loop(HANDLE other_event) -{ - int n, nhandles, nallhandles, netindex, otherindex; - unsigned long next, then; - long ticks; - HANDLE *handles; - SOCKET *sklist; - int skcount; - unsigned long now = GETTICKCOUNT(); - - if (toplevel_callback_pending()) { - ticks = 0; - next = now; - } else if (run_timers(now, &next)) { - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - } else { - ticks = INFINITE; - /* no need to initialise next here because we can never get - * WAIT_TIMEOUT */ + if (ctx->other_event != INVALID_HANDLE_VALUE) { + *extra_handles = &ctx->other_event; + *n_extra_handles = 1; } - handles = handle_get_events(&nhandles); - handles = sresize(handles, nhandles+2, HANDLE); - nallhandles = nhandles; - - if (netevent != INVALID_HANDLE_VALUE) - handles[netindex = nallhandles++] = netevent; - else - netindex = -1; - if (other_event != INVALID_HANDLE_VALUE) - handles[otherindex = nallhandles++] = other_event; - else - otherindex = -1; - - n = WaitForMultipleObjects(nallhandles, handles, false, ticks); - - if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { - handle_got_event(handles[n - WAIT_OBJECT_0]); - } else if (netindex >= 0 && n == WAIT_OBJECT_0 + netindex) { - WSANETWORKEVENTS things; - SOCKET socket; - int i, socketstate; - - /* - * We must not call select_result() for any socket - * until we have finished enumerating within the - * tree. This is because select_result() may close - * the socket and modify the tree. - */ - /* Count the active sockets. */ - i = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) i++; - - /* Expand the buffer if necessary. */ - sklist = snewn(i, SOCKET); - - /* Retrieve the sockets into sklist. */ - skcount = 0; - for (socket = first_socket(&socketstate); - socket != INVALID_SOCKET; - socket = next_socket(&socketstate)) { - sklist[skcount++] = socket; - } - - /* Now we're done enumerating; go through the list. */ - for (i = 0; i < skcount; i++) { - WPARAM wp; - socket = sklist[i]; - wp = (WPARAM) socket; - if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { - static const struct { int bit, mask; } eventtypes[] = { - {FD_CONNECT_BIT, FD_CONNECT}, - {FD_READ_BIT, FD_READ}, - {FD_CLOSE_BIT, FD_CLOSE}, - {FD_OOB_BIT, FD_OOB}, - {FD_WRITE_BIT, FD_WRITE}, - {FD_ACCEPT_BIT, FD_ACCEPT}, - }; - int e; - - noise_ultralight(NOISE_SOURCE_IOID, socket); - - for (e = 0; e < lenof(eventtypes); e++) - if (things.lNetworkEvents & eventtypes[e].mask) { - LPARAM lp; - int err = things.iErrorCode[eventtypes[e].bit]; - lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); - select_result(wp, lp); - } - } - } - - sfree(sklist); - } - - sfree(handles); - - run_toplevel_callbacks(); - - if (n == WAIT_TIMEOUT) { - now = next; - } else { - now = GETTICKCOUNT(); - } + return true; +} +static bool winsftp_cliloop_post(void *vctx, size_t extra_handle_index) +{ + struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; - if (otherindex >= 0 && n == WAIT_OBJECT_0 + otherindex) - return 1; + if (ctx->other_event != INVALID_HANDLE_VALUE && + extra_handle_index == 0) + ctx->toret = 1; /* other_event was set */ - return 0; + return false; /* always run only one loop iteration */ +} +int do_eventsel_loop(HANDLE other_event) +{ + struct winsftp_cliloop_ctx ctx[1]; + ctx->other_event = other_event; + ctx->toret = 0; + cli_main_loop(winsftp_cliloop_pre, winsftp_cliloop_post, ctx); + return ctx->toret; } /* @@ -628,12 +511,13 @@ int ssh_sftp_loop_iteration(void) fd_set readfds; int ret; unsigned long now = GETTICKCOUNT(), then; + SOCKET skt = winselcli_unique_socket(); - if (sftp_ssh_socket == INVALID_SOCKET) + if (skt == INVALID_SOCKET) return -1; /* doom */ - if (socket_writable(sftp_ssh_socket)) - select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_WRITE); + if (socket_writable(skt)) + select_result((WPARAM) skt, (LPARAM) FD_WRITE); do { unsigned long next; @@ -655,7 +539,7 @@ int ssh_sftp_loop_iteration(void) } FD_ZERO(&readfds); - FD_SET(sftp_ssh_socket, &readfds); + FD_SET(skt, &readfds); ret = p_select(1, &readfds, NULL, NULL, ptv); if (ret < 0) @@ -667,7 +551,7 @@ int ssh_sftp_loop_iteration(void) } while (ret == 0); - select_result((WPARAM) sftp_ssh_socket, (LPARAM) FD_READ); + select_result((WPARAM) skt, (LPARAM) FD_READ); return 0; } else { @@ -703,14 +587,14 @@ static DWORD WINAPI command_read_thread(void *param) char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) { int ret; - struct command_read_ctx actx, *ctx = &actx; + struct command_read_ctx ctx[1]; DWORD threadid; HANDLE hThread; fputs(prompt, stdout); fflush(stdout); - if ((sftp_ssh_socket == INVALID_SOCKET && no_fds_ok) || + if ((winselcli_unique_socket() == INVALID_SOCKET && no_fds_ok) || p_WSAEventSelect == NULL) { return fgetline(stdin); /* very simple */ } @@ -732,7 +616,9 @@ char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) do { ret = do_eventsel_loop(ctx->event); - /* Error return can only occur if netevent==NULL, and it ain't. */ + /* do_eventsel_loop can't return an error (unlike + * ssh_sftp_loop_iteration, which can return -1 if select goes + * wrong or if the socket doesn't exist). */ assert(ret >= 0); } while (ret == 0); @@ -742,10 +628,10 @@ char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) return ctx->line; } -void platform_psftp_pre_conn_setup(void) +void platform_psftp_pre_conn_setup(LogPolicy *lp) { - if (restricted_acl) { - lp_eventlog(default_logpolicy, "Running with restricted process ACL"); + if (restricted_acl()) { + lp_eventlog(lp, "Running with restricted process ACL"); } } diff --git a/windows/winshare.c b/windows/winshare.c index 7f4af86..f0e409a 100644 --- a/windows/winshare.c +++ b/windows/winshare.c @@ -16,95 +16,9 @@ #include "wincapi.h" #include "winsecur.h" -#ifdef COVERITY -/* - * The hack I use to build for Coverity scanning, using winegcc and - * Makefile.mgw, didn't provide some defines in wincrypt.h last time I - * looked. Therefore, define them myself here, but enclosed in #ifdef - * COVERITY to ensure I don't make up random nonsense values for any - * real build. - */ -#ifndef CRYPTPROTECTMEMORY_BLOCK_SIZE -#define CRYPTPROTECTMEMORY_BLOCK_SIZE 16 -#endif -#ifndef CRYPTPROTECTMEMORY_CROSS_PROCESS -#define CRYPTPROTECTMEMORY_CROSS_PROCESS 1 -#endif -#endif - #define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare" #define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex" -static char *obfuscate_name(const char *realname) -{ - /* - * Windows's named pipes all live in the same namespace, so one - * user can see what pipes another user has open. This is an - * undesirable privacy leak and in particular permits one user to - * know what username@host another user is SSHing to, so we - * protect that information by using CryptProtectMemory (which - * uses a key built in to each user's account). - */ - char *cryptdata; - int cryptlen; - unsigned char digest[32]; - char retbuf[65]; - int i; - - cryptlen = strlen(realname) + 1; - cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1; - cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE; - cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE; - - cryptdata = snewn(cryptlen, char); - memset(cryptdata, 0, cryptlen); - strcpy(cryptdata, realname); - - /* - * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to - * use the same key in all processes with this user id, meaning - * that the next PuTTY process calling this function with the same - * input will get the same data. - * - * (Contrast with CryptProtectData, which invents a new session - * key every time since its API permits returning more data than - * was input, so calling _that_ and hashing the output would not - * be stable.) - * - * We don't worry too much if this doesn't work for some reason. - * Omitting this step still has _some_ privacy value (in that - * another user can test-hash things to confirm guesses as to - * where you might be connecting to, but cannot invert SHA-256 in - * the absence of any plausible guess). So we don't abort if we - * can't call CryptProtectMemory at all, or if it fails. - */ - if (got_crypt()) - p_CryptProtectMemory(cryptdata, cryptlen, - CRYPTPROTECTMEMORY_CROSS_PROCESS); - - /* - * We don't want to give away the length of the hostname either, - * so having got it back out of CryptProtectMemory we now hash it. - */ - { - ssh_hash *h = ssh_hash_new(&ssh_sha256); - put_string(h, cryptdata, cryptlen); - ssh_hash_final(h, digest); - } - - sfree(cryptdata); - - /* - * Finally, make printable. - */ - for (i = 0; i < 32; i++) { - sprintf(retbuf + 2*i, "%02x", digest[i]); - /* the last of those will also write the trailing NUL */ - } - - return dupstr(retbuf); -} - static char *make_name(const char *prefix, const char *name) { char *username, *retname; @@ -134,7 +48,7 @@ int platform_ssh_share(const char *pi_name, Conf *conf, * that it also eliminates any characters illegal in Windows pipe * names. */ - name = obfuscate_name(pi_name); + name = capi_obfuscate_string(pi_name); if (!name) { *logtext = dupprintf("Unable to call CryptProtectMemory: %s", win_strerror(GetLastError())); diff --git a/windows/winsocks.c b/windows/winsocks.c new file mode 100644 index 0000000..83ba364 --- /dev/null +++ b/windows/winsocks.c @@ -0,0 +1,24 @@ +/* + * Main program for Windows psocks. + */ + +#include "putty.h" +#include "ssh.h" +#include "psocks.h" + +static const PsocksPlatform platform = { + NULL /* open_pipes */, + NULL /* start_subcommand */, +}; + +int main(int argc, char **argv) +{ + psocks_state *ps = psocks_new(&platform); + psocks_cmdline(ps, argc, argv); + + sk_init(); + winselcli_setup(); + psocks_start(ps); + + cli_main_loop(cliloop_null_pre, cliloop_null_post, NULL); +} diff --git a/windows/winstore.c b/windows/winstore.c index 1fba6dd..09e5c02 100644 --- a/windows/winstore.c +++ b/windows/winstore.c @@ -175,7 +175,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name) if (!fontname) return NULL; - settingname = dupcat(name, "IsBold", NULL); + settingname = dupcat(name, "IsBold"); isbold = read_setting_i(handle, settingname, -1); sfree(settingname); if (isbold == -1) { @@ -183,7 +183,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name) return NULL; } - settingname = dupcat(name, "CharSet", NULL); + settingname = dupcat(name, "CharSet"); charset = read_setting_i(handle, settingname, -1); sfree(settingname); if (charset == -1) { @@ -191,7 +191,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name) return NULL; } - settingname = dupcat(name, "Height", NULL); + settingname = dupcat(name, "Height"); height = read_setting_i(handle, settingname, INT_MIN); sfree(settingname); if (height == INT_MIN) { @@ -210,13 +210,13 @@ void write_setting_fontspec(settings_w *handle, char *settingname; write_setting_s(handle, name, font->name); - settingname = dupcat(name, "IsBold", NULL); + settingname = dupcat(name, "IsBold"); write_setting_i(handle, settingname, font->isbold); sfree(settingname); - settingname = dupcat(name, "CharSet", NULL); + settingname = dupcat(name, "CharSet"); write_setting_i(handle, settingname, font->charset); sfree(settingname); - settingname = dupcat(name, "Height", NULL); + settingname = dupcat(name, "Height"); write_setting_i(handle, settingname, font->height); sfree(settingname); } @@ -551,15 +551,13 @@ static HANDLE access_random_seed(int action) char profile[MAX_PATH + 1]; if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, profile)) && - try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND", - (const char *)NULL), + try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"), action, &rethandle)) return rethandle; if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, profile)) && - try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND", - (const char *)NULL), + try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"), action, &rethandle)) return rethandle; } @@ -582,8 +580,7 @@ static HANDLE access_random_seed(int action) if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 && try_random_seed_and_free( - dupcat(drv, path, "\\PUTTY.RND", (const char *)NULL), - action, &rethandle)) + dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle)) return rethandle; } @@ -595,8 +592,7 @@ static HANDLE access_random_seed(int action) DWORD len = GetWindowsDirectory(windir, sizeof(windir)); if (len < lenof(windir) && try_random_seed_and_free( - dupcat(windir, "\\PUTTY.RND", (const char *)NULL), - action, &rethandle)) + dupcat(windir, "\\PUTTY.RND"), action, &rethandle)) return rethandle; } diff --git a/windows/winstuff.h b/windows/winstuff.h index 0b191d5..c0df5a3 100644 --- a/windows/winstuff.h +++ b/windows/winstuff.h @@ -140,6 +140,9 @@ struct FontSpec *fontspec_new( #define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \ typedef rettype (WINAPI *t_##name) params; \ linkage t_##name p_##name +/* If you DECL_WINDOWS_FUNCTION as extern in a header file, use this to + * define the function pointer in a source file */ +#define DEF_WINDOWS_FUNCTION(name) t_##name p_##name #define STR1(x) #x #define STR(x) STR1(x) #define GET_WINDOWS_FUNCTION_PP(module, name) \ @@ -154,19 +157,6 @@ struct FontSpec *fontspec_new( (p_##name = module ? \ (t_##name) GetProcAddress(module, #name) : NULL) -/* - * Global variables. Most modules declare these `extern', but - * window.c will do `#define PUTTY_DO_GLOBALS' before including this - * module, and so will get them properly defined. -*/ -#ifndef GLOBAL -#ifdef PUTTY_DO_GLOBALS -#define GLOBAL -#else -#define GLOBAL extern -#endif -#endif - #define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY" #define PUTTY_REG_PARENT "Software\\SimonTatham" #define PUTTY_REG_PARENT_CHILD "PuTTY" @@ -207,16 +197,11 @@ typedef void *Ssh_gss_name; #endif /* - * Window handles for the windows that can be running during a - * PuTTY session. + * The all-important instance handle, saved from WinMain in every GUI + * program and exported for other GUI code to pass back to the Windows + * API. */ -GLOBAL HWND hwnd; /* the main terminal window */ -GLOBAL HWND logbox; - -/* - * The all-important instance handle. - */ -GLOBAL HINSTANCE hinst; +extern HINSTANCE hinst; /* * Help file stuff in winhelp.c. @@ -228,23 +213,13 @@ void launch_help(HWND hwnd, const char *topic); void quit_help(HWND hwnd); int has_embedded_chm(void); /* 1 = yes, 0 = no, -1 = N/A */ -/* - * The terminal and logging context are notionally local to the - * Windows front end, but they must be shared between window.c and - * windlg.c. Likewise the Seat structure for the Windows GUI, and the - * Conf for the main session.. - */ -GLOBAL Terminal *term; -GLOBAL LogContext *logctx; -GLOBAL Conf *conf; - /* * GUI seat methods in windlg.c, so that the vtable definition in * window.c can refer to them. */ int win_seat_verify_ssh_host_key( - Seat *seat, const char *host, int port, - const char *keytype, char *keystr, char *key_fingerprint, + Seat *seat, const char *host, int port, const char *keytype, + char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); int win_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, @@ -253,12 +228,6 @@ int win_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); -/* - * The Windows GUI seat object itself, so that its methods can be - * called outside window.c. - */ -extern Seat *const win_seat; - /* * Windows-specific clipboard helper function shared with windlg.c, * which takes the data string in the system code page instead of @@ -319,6 +288,8 @@ bool socket_writable(SOCKET skt); void socket_reselect_all(void); /* Make a SockAddr which just holds a named pipe address. */ SockAddr *sk_namedpipe_addr(const char *pipename); +/* Turn a WinSock error code into a string. */ +const char *winsock_error_string(int error); /* * winnet.c dynamically loads WinSock 2 or WinSock 1 depending on @@ -326,12 +297,12 @@ SockAddr *sk_namedpipe_addr(const char *pipename); * that module must be exported from it as function pointers. So * here they are. */ -DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAAsyncSelect, +DECL_WINDOWS_FUNCTION(extern, int, WSAAsyncSelect, (SOCKET, HWND, u_int, long)); -DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEventSelect, +DECL_WINDOWS_FUNCTION(extern, int, WSAEventSelect, (SOCKET, WSAEVENT, long)); -DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAGetLastError, (void)); -DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEnumNetworkEvents, +DECL_WINDOWS_FUNCTION(extern, int, WSAGetLastError, (void)); +DECL_WINDOWS_FUNCTION(extern, int, WSAEnumNetworkEvents, (SOCKET, WSAEVENT, LPWSANETWORKEVENTS)); #ifdef NEED_DECLARATION_OF_SELECT /* This declaration is protected by an ifdef for the sake of building @@ -341,16 +312,27 @@ DECL_WINDOWS_FUNCTION(GLOBAL, int, WSAEnumNetworkEvents, * only a modules actually needing to use (or define, or initialise) * this function pointer will see its declaration, and _those_ modules * - which will be Windows-specific anyway - can take more care. */ -DECL_WINDOWS_FUNCTION(GLOBAL, int, select, +DECL_WINDOWS_FUNCTION(extern, int, select, (int, fd_set FAR *, fd_set FAR *, fd_set FAR *, const struct timeval FAR *)); #endif /* - * Provided by each client of winnet.c, and called by winnet.c to turn - * on or off WSA*Select for a given socket. + * Implemented differently depending on the client of winnet.c, and + * called by winnet.c to turn on or off WSA*Select for a given socket. + */ +const char *do_select(SOCKET skt, bool enable); + +/* + * Exports from winselgui.c and winselcli.c, each of which provides an + * implementation of do_select. */ -char *do_select(SOCKET skt, bool startup); +void winselgui_set_hwnd(HWND hwnd); +void winselgui_clear_hwnd(void); + +void winselcli_setup(void); +SOCKET winselcli_unique_socket(void); +extern HANDLE winselcli_event; /* * Network-subsystem-related functions provided in other Windows modules. @@ -360,6 +342,12 @@ Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, Socket *new_named_pipe_client(const char *pipename, Plug *plug); /* winnpc */ Socket *new_named_pipe_listener(const char *pipename, Plug *plug); /* winnps */ +/* A lower-level function in winnpc.c, which does most of the work of + * new_named_pipe_client (including checking the ownership of what + * it's connected to), but returns a plain HANDLE instead of wrapping + * it into a Socket. */ +HANDLE connect_to_named_pipe(const char *pipename, char **err); + /* * Exports from winctrls.c. */ @@ -382,7 +370,10 @@ typedef struct filereq_tag filereq; /* cwd for file requester */ bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save); filereq *filereq_new(void); void filereq_free(filereq *state); -int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid); +void pgp_fingerprints_msgbox(HWND owner); +int message_box(HWND owner, LPCTSTR text, LPCTSTR caption, + DWORD style, DWORD helpctxid); +void MakeDlgItemBorderless(HWND parent, int id); char *GetDlgItemText_alloc(HWND hwnd, int id); void split_into_argv(char *, int *, char ***, char ***); @@ -500,6 +491,12 @@ struct winctrl { */ int base_id; int num_ids; + /* + * For vertical alignment, the id of a particular representative + * control that has the y-extent of the sensible part of the + * control. + */ + int align_id; /* * Remember what keyboard shortcuts were used by this control, * so that when we remove it again we can take them out of the @@ -551,24 +548,25 @@ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, * Exports from windlg.c. */ void defuse_showwindow(void); -bool do_config(void); -bool do_reconfig(HWND, int); +bool do_config(Conf *); +bool do_reconfig(HWND, Conf *, int); void showeventlog(HWND); void showabout(HWND); void force_normal(HWND hwnd); void modal_about_box(HWND hwnd); void show_help(HWND hwnd); +HWND event_log_window(void); /* * Exports from winmisc.c. */ -GLOBAL DWORD osMajorVersion, osMinorVersion, osPlatformId; +extern DWORD osMajorVersion, osMinorVersion, osPlatformId; void init_winver(void); void dll_hijacking_protection(void); HMODULE load_system32_dll(const char *libname); const char *win_strerror(int error); void restrict_process_acl(void); -GLOBAL bool restricted_acl; +bool restricted_acl(void); void escape_registry_key(const char *in, strbuf *out); void unescape_registry_key(const char *in, strbuf *out); @@ -635,16 +633,9 @@ struct handle_sink { void handle_sink_init(handle_sink *sink, struct handle *h); /* - * winpgntc.c needs to schedule callbacks for asynchronous agent - * requests. This has to be done differently in GUI and console, so - * there's an exported function used for the purpose. - * - * Also, we supply FLAG_SYNCAGENT to force agent requests to be - * synchronous in pscp and psftp. + * Exports from winpgntc.c. */ -void agent_schedule_callback(void (*callback)(void *, void *, int), - void *callback_ctx, void *data, int len); -#define FLAG_SYNCAGENT 0x1000 +char *agent_named_pipe_name(void); /* * Exports from winser.c. @@ -702,4 +693,12 @@ char *get_jumplist_registry_entries(void); /* In winmisc.c */ char *registry_get_string(HKEY root, const char *path, const char *leaf); +/* In wincliloop.c */ +typedef bool (*cliloop_pre_t)(void *vctx, const HANDLE **extra_handles, + size_t *n_extra_handles); +typedef bool (*cliloop_post_t)(void *vctx, size_t extra_handle_index); +void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx); +bool cliloop_null_pre(void *vctx, const HANDLE **, size_t *); +bool cliloop_null_post(void *vctx, size_t); + #endif diff --git a/windows/winutils.c b/windows/winutils.c index 404e11a..dec8984 100644 --- a/windows/winutils.c +++ b/windows/winutils.c @@ -91,6 +91,8 @@ void filereq_free(filereq *state) * Message box with optional context help. */ +static HWND message_box_owner; + /* Callback function to launch context help. */ static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo) { @@ -107,10 +109,11 @@ static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo) CHECK_CTX(pgp_fingerprints); #undef CHECK_CTX if (context) - launch_help(hwnd, context); + launch_help(message_box_owner, context); } -int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid) +int message_box(HWND owner, LPCTSTR text, LPCTSTR caption, + DWORD style, DWORD helpctxid) { MSGBOXPARAMS mbox; @@ -121,7 +124,7 @@ int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid) mbox.cbSize = sizeof(mbox); /* Assumes the globals `hinst' and `hwnd' have sensible values. */ mbox.hInstance = hinst; - mbox.hwndOwner = hwnd; + mbox.hwndOwner = message_box_owner = owner; mbox.lpfnMsgBoxCallback = &message_box_help_callback; mbox.dwLanguageId = LANG_NEUTRAL; mbox.lpszText = text; @@ -135,21 +138,40 @@ int message_box(LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid) /* * Display the fingerprints of the PGP Master Keys to the user. */ -void pgp_fingerprints(void) +void pgp_fingerprints_msgbox(HWND owner) +{ + message_box( + owner, + "These are the fingerprints of the PuTTY PGP Master Keys. They can\n" + "be used to establish a trust path from this executable to another\n" + "one. See the manual for more information.\n" + "(Note: these fingerprints have nothing to do with SSH!)\n" + "\n" + "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR + " (" PGP_MASTER_KEY_DETAILS "):\n" + " " PGP_MASTER_KEY_FP "\n\n" + "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR + ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" + " " PGP_PREV_MASTER_KEY_FP, + "PGP fingerprints", MB_ICONINFORMATION | MB_OK, + HELPCTXID(pgp_fingerprints)); +} + +/* + * Helper function to remove the border around a dialog item such as + * a read-only edit control. + */ +void MakeDlgItemBorderless(HWND parent, int id) { - message_box("These are the fingerprints of the PuTTY PGP Master Keys. They can\n" - "be used to establish a trust path from this executable to another\n" - "one. See the manual for more information.\n" - "(Note: these fingerprints have nothing to do with SSH!)\n" - "\n" - "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR - " (" PGP_MASTER_KEY_DETAILS "):\n" - " " PGP_MASTER_KEY_FP "\n\n" - "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR - ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" - " " PGP_PREV_MASTER_KEY_FP, - "PGP fingerprints", MB_ICONINFORMATION | MB_OK, - HELPCTXID(pgp_fingerprints)); + HWND child = GetDlgItem(parent, id); + LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE); + LONG_PTR exstyle = GetWindowLongPtr(child, GWL_EXSTYLE); + style &= ~WS_BORDER; + exstyle &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE); + SetWindowLongPtr(child, GWL_STYLE, style); + SetWindowLongPtr(child, GWL_EXSTYLE, exstyle); + SetWindowPos(child, NULL, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } /* @@ -188,6 +210,144 @@ char *GetDlgItemText_alloc(HWND hwnd, int id) * treat the rest as a raw string, you can. If you don't want to, * `argstart' can be safely left NULL. */ + +/* + * The precise argument-breaking rules vary with compiler version, or + * rather, with the crt0-type startup code that comes with each + * compiler's C library. We do our best to match the compiler version, + * so that we faithfully imitate in our GUI utilities what the + * corresponding set of CLI utilities can't be prevented from doing. + * + * The basic rules are: + * + * - Single quotes are not special characters. + * + * - Double quotes are removed, but within them spaces cease to be + * special. + * + * - Backslashes are _only_ special when a sequence of them appear + * just before a double quote. In this situation, they are treated + * like C backslashes: so \" just gives a literal quote, \\" gives + * a literal backslash and then opens or closes a double-quoted + * segment, \\\" gives a literal backslash and then a literal + * quote, \\\\" gives two literal backslashes and then opens/closes + * a double-quoted segment, and so forth. Note that this behaviour + * is identical inside and outside double quotes. + * + * - Two successive double quotes become one literal double quote, + * but only _inside_ a double-quoted segment. Outside, they just + * form an empty double-quoted segment (which may cause an empty + * argument word). + * + * That only leaves the interesting question of what happens when one + * or more backslashes precedes two or more double quotes, starting + * inside a double-quoted string. + * + * I investigated this in an ordinary CLI program, using the + * toolchain's crt0 to split a command line of the form + * + * "a\\\"""b c" d + * + * Here I tabulate number of backslashes (across the top) against + * number of quotes (down the left), and indicate how many backslashes + * are output, how many quotes are output, and whether a quoted + * segment is open at the end of the sequence: + * + * backslashes + * + * 0 1 2 3 4 + * + * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y + * --------+----------------------------- + * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n + * q 2 0,1,y | 0,1,n 1,1,y 1,1,n 2,1,y + * u 3 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n + * o 4 0,2,y | 0,2,n 1,2,y 1,2,n 2,2,y + * t 5 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n + * e 6 0,3,y | 0,3,n 1,3,y 1,3,n 2,3,y + * s 7 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n + * 8 0,4,y | 0,4,n 1,4,y 1,4,n 2,4,y + * + * The row at the top of this table, with quotes=0, demonstrates what + * I claimed above, that when a sequence of backslashes are not + * followed by a double quote, they don't act specially at all. The + * rest of the table shows that the backslashes escape each other in + * pairs (so that with 2n or 2n+1 input backslashes you get n output + * ones); if there's an odd number of input backslashes then the last + * one escapes the first double quote (so you get a literal quote and + * enter a quoted string); thereafter, each input quote character + * either opens or closes a quoted string, and if it closes one, it + * generates a literal " as a side effect. + * + * But here's the corresponding table from the older Visual Studio 7: + * + * backslashes + * + * 0 1 2 3 4 + * + * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y + * --------+----------------------------- + * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n + * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n + * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y + * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n + * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n + * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y + * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n + * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n + * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y + * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n + * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n + * + * There is very weird mod-3 behaviour going on here in the + * number of quotes, and it even applies when there aren't any + * backslashes! How ghastly. + * + * With a bit of thought, this extremely odd diagram suddenly + * coalesced itself into a coherent, if still ghastly, model of + * how things work: + * + * - As before, backslashes are only special when one or more + * of them appear contiguously before at least one double + * quote. In this situation the backslashes do exactly what + * you'd expect: each one quotes the next thing in front of + * it, so you end up with n/2 literal backslashes (if n is + * even) or (n-1)/2 literal backslashes and a literal quote + * (if n is odd). In the latter case the double quote + * character right after the backslashes is used up. + * + * - After that, any remaining double quotes are processed. A + * string of contiguous unescaped double quotes has a mod-3 + * behaviour: + * + * * inside a quoted segment, a quote ends the segment. + * * _immediately_ after ending a quoted segment, a quote + * simply produces a literal quote. + * * otherwise, outside a quoted segment, a quote begins a + * quoted segment. + * + * So, for example, if we started inside a quoted segment + * then two contiguous quotes would close the segment and + * produce a literal quote; three would close the segment, + * produce a literal quote, and open a new segment. If we + * started outside a quoted segment, then two contiguous + * quotes would open and then close a segment, producing no + * output (but potentially creating a zero-length argument); + * but three quotes would open and close a segment and then + * produce a literal quote. + */ + +/* + * We select between two behaviours depending on the version of Visual + * Studio (see large comment below). I don't know exactly when the bug + * fix happened, but I know that VS7 had the odd mod-3 behaviour. + */ +#if _MSC_VER < 1400 +#define MOD3 1 +#else +#define MOD3 0 +#endif + void split_into_argv(char *cmdline, int *argc, char ***argv, char ***argstart) { @@ -196,107 +356,6 @@ void split_into_argv(char *cmdline, int *argc, char ***argv, char **outputargv, **outputargstart; int outputargc; - /* - * These argument-breaking rules apply to Visual Studio 7, which - * is currently the compiler expected to be used for PuTTY. Visual - * Studio 10 has different rules, lacking the curious mod 3 - * behaviour of consecutive quotes described below; I presume they - * fixed a bug. As and when we migrate to a newer compiler, we'll - * have to adjust this to match; however, for the moment we - * faithfully imitate in our GUI utilities what our CLI utilities - * can't be prevented from doing. - * - * When I investigated this, at first glance the rules appeared to - * be: - * - * - Single quotes are not special characters. - * - * - Double quotes are removed, but within them spaces cease - * to be special. - * - * - Backslashes are _only_ special when a sequence of them - * appear just before a double quote. In this situation, - * they are treated like C backslashes: so \" just gives a - * literal quote, \\" gives a literal backslash and then - * opens or closes a double-quoted segment, \\\" gives a - * literal backslash and then a literal quote, \\\\" gives - * two literal backslashes and then opens/closes a - * double-quoted segment, and so forth. Note that this - * behaviour is identical inside and outside double quotes. - * - * - Two successive double quotes become one literal double - * quote, but only _inside_ a double-quoted segment. - * Outside, they just form an empty double-quoted segment - * (which may cause an empty argument word). - * - * - That only leaves the interesting question of what happens - * when one or more backslashes precedes two or more double - * quotes, starting inside a double-quoted string. And the - * answer to that appears somewhat bizarre. Here I tabulate - * number of backslashes (across the top) against number of - * quotes (down the left), and indicate how many backslashes - * are output, how many quotes are output, and whether a - * quoted segment is open at the end of the sequence: - * - * backslashes - * - * 0 1 2 3 4 - * - * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y - * --------+----------------------------- - * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n - * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n - * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y - * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n - * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n - * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y - * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n - * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n - * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y - * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n - * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n - * - * - * [Test fragment was of the form "a\\\"""b c" d.] - * - * There is very weird mod-3 behaviour going on here in the - * number of quotes, and it even applies when there aren't any - * backslashes! How ghastly. - * - * With a bit of thought, this extremely odd diagram suddenly - * coalesced itself into a coherent, if still ghastly, model of - * how things work: - * - * - As before, backslashes are only special when one or more - * of them appear contiguously before at least one double - * quote. In this situation the backslashes do exactly what - * you'd expect: each one quotes the next thing in front of - * it, so you end up with n/2 literal backslashes (if n is - * even) or (n-1)/2 literal backslashes and a literal quote - * (if n is odd). In the latter case the double quote - * character right after the backslashes is used up. - * - * - After that, any remaining double quotes are processed. A - * string of contiguous unescaped double quotes has a mod-3 - * behaviour: - * - * * inside a quoted segment, a quote ends the segment. - * * _immediately_ after ending a quoted segment, a quote - * simply produces a literal quote. - * * otherwise, outside a quoted segment, a quote begins a - * quoted segment. - * - * So, for example, if we started inside a quoted segment - * then two contiguous quotes would close the segment and - * produce a literal quote; three would close the segment, - * produce a literal quote, and open a new segment. If we - * started outside a quoted segment, then two contiguous - * quotes would open and then close a segment, producing no - * output (but potentially creating a zero-length argument); - * but three quotes would open and close a segment and then - * produce a literal quote. - */ - /* * First deal with the simplest of all special cases: if there * aren't any arguments, return 0,NULL,NULL. @@ -366,11 +425,17 @@ void split_into_argv(char *cmdline, int *argc, char ***argv, /* Outside a quote segment, a quote starts one. */ if (!quote) quotes--; - /* Now we produce (n+1)/3 literal quotes... */ +#if !MOD3 + /* New behaviour: produce n/2 literal quotes... */ + for (i = 2; i <= quotes; i += 2) *q++ = '"'; + /* ... and end in a quote segment iff 2 divides n. */ + quote = (quotes % 2 == 0); +#else + /* Old behaviour: produce (n+1)/3 literal quotes... */ for (i = 3; i <= quotes+1; i += 3) *q++ = '"'; - /* ... and end in a quote segment iff 3 divides n. */ quote = (quotes % 3 == 0); +#endif } } } else { @@ -400,6 +465,100 @@ const struct argv_test { * We generate this set of tests by invoking ourself with * `-generate'. */ +#if !MOD3 + /* Newer behaviour, with no weird mod-3 glitch. */ + {"ab c\" d", {"ab", "c d", NULL}}, + {"a\"b c\" d", {"ab c", "d", NULL}}, + {"a\"\"b c\" d", {"ab", "c d", NULL}}, + {"a\"\"\"b c\" d", {"a\"b c", "d", NULL}}, + {"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, + {"a\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, + {"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, + {"a\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, + {"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, + {"a\\b c\" d", {"a\\b", "c d", NULL}}, + {"a\\\"b c\" d", {"a\"b", "c d", NULL}}, + {"a\\\"\"b c\" d", {"a\"b c", "d", NULL}}, + {"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}}, + {"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, + {"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, + {"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, + {"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, + {"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, + {"a\\\\b c\" d", {"a\\\\b", "c d", NULL}}, + {"a\\\\\"b c\" d", {"a\\b c", "d", NULL}}, + {"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}}, + {"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, + {"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, + {"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, + {"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, + {"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, + {"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, + {"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}}, + {"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}}, + {"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, + {"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, + {"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, + {"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, + {"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, + {"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, + {"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, + {"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}}, + {"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}}, + {"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}}, + {"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, + {"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, + {"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, + {"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, + {"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b c", "d", NULL}}, + {"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, + {"\"ab c\" d", {"ab c", "d", NULL}}, + {"\"a\"b c\" d", {"ab", "c d", NULL}}, + {"\"a\"\"b c\" d", {"a\"b c", "d", NULL}}, + {"\"a\"\"\"b c\" d", {"a\"b", "c d", NULL}}, + {"\"a\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, + {"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, + {"\"a\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, + {"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, + {"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, + {"\"a\\b c\" d", {"a\\b c", "d", NULL}}, + {"\"a\\\"b c\" d", {"a\"b c", "d", NULL}}, + {"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}}, + {"\"a\\\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, + {"\"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, + {"\"a\\\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, + {"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, + {"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, + {"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b", "c d", NULL}}, + {"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}}, + {"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}}, + {"\"a\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, + {"\"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, + {"\"a\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, + {"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, + {"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, + {"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, + {"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, + {"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}}, + {"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}}, + {"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, + {"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, + {"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, + {"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, + {"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, + {"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, + {"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b", "c d", NULL}}, + {"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}}, + {"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}}, + {"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, + {"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, + {"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, + {"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, + {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b c", "d", NULL}}, + {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, + {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"\"b c", "d", NULL}}, +#else /* MOD3 */ + /* VS7 mod-3 behaviour. */ {"ab c\" d", {"ab", "c d", NULL}}, {"a\"b c\" d", {"ab c", "d", NULL}}, {"a\"\"b c\" d", {"ab", "c d", NULL}}, @@ -490,6 +649,7 @@ const struct argv_test { {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, +#endif /* MOD3 */ }; int main(int argc, char **argv) diff --git a/x11fwd.c b/x11fwd.c index faca786..86f8583 100644 --- a/x11fwd.c +++ b/x11fwd.c @@ -684,7 +684,7 @@ void x11_format_auth_for_authfile( put_stringpl_xauth(bs, authdata); } -static void x11_log(Plug *p, int type, SockAddr *addr, int port, +static void x11_log(Plug *p, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { /* We have no interface to the logging module here, so we drop these. */ @@ -762,11 +762,10 @@ int x11_get_screen_number(char *display) } static const PlugVtable X11Connection_plugvt = { - x11_log, - x11_closing, - x11_receive, - x11_sent, - NULL + .log = x11_log, + .closing = x11_closing, + .receive = x11_receive, + .sent = x11_sent, }; static void x11_chan_free(Channel *chan); @@ -776,29 +775,29 @@ static void x11_send_eof(Channel *chan); static void x11_set_input_wanted(Channel *chan, bool wanted); static char *x11_log_close_msg(Channel *chan); -static const struct ChannelVtable X11Connection_channelvt = { - x11_chan_free, - chan_remotely_opened_confirmation, - chan_remotely_opened_failure, - x11_send, - x11_send_eof, - x11_set_input_wanted, - x11_log_close_msg, - chan_default_want_close, - chan_no_exit_status, - chan_no_exit_signal, - chan_no_exit_signal_numeric, - chan_no_run_shell, - chan_no_run_command, - chan_no_run_subsystem, - chan_no_enable_x11_forwarding, - chan_no_enable_agent_forwarding, - chan_no_allocate_pty, - chan_no_set_env, - chan_no_send_break, - chan_no_send_signal, - chan_no_change_window_size, - chan_no_request_response, +static const ChannelVtable X11Connection_channelvt = { + .free = x11_chan_free, + .open_confirmation = chan_remotely_opened_confirmation, + .open_failed = chan_remotely_opened_failure, + .send = x11_send, + .send_eof = x11_send_eof, + .set_input_wanted = x11_set_input_wanted, + .log_close_msg = x11_log_close_msg, + .want_close = chan_default_want_close, + .rcvd_exit_status = chan_no_exit_status, + .rcvd_exit_signal = chan_no_exit_signal, + .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, + .run_shell = chan_no_run_shell, + .run_command = chan_no_run_command, + .run_subsystem = chan_no_run_subsystem, + .enable_x11_forwarding = chan_no_enable_x11_forwarding, + .enable_agent_forwarding = chan_no_enable_agent_forwarding, + .allocate_pty = chan_no_allocate_pty, + .set_env = chan_no_set_env, + .send_break = chan_no_send_break, + .send_signal = chan_no_send_signal, + .change_window_size = chan_no_change_window_size, + .request_response = chan_no_request_response, }; /*