From d03024905e50f472e55aad9924018b0dcfcdbe01 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Sun, 11 Oct 2015 09:27:55 +0100 Subject: [PATCH 01/31] bignum_set_bit: Don't abort if asked to clear an inaccessible bit All those bits are clear anyway. Bug found with the help of afl-fuzz. (cherry picked from commit 4f340599029715d863b84bdfc0407f582114a23c) --- sshbn.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sshbn.c b/sshbn.c index facdf3d5..8393721a 100644 --- a/sshbn.c +++ b/sshbn.c @@ -1202,9 +1202,9 @@ int bignum_bit(Bignum bn, int i) */ void bignum_set_bit(Bignum bn, int bitnum, int value) { - if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) - abort(); /* beyond the end */ - else { + if (bitnum < 0 || bitnum >= (int)(BIGNUM_INT_BITS * bn[0])) { + if (value) abort(); /* beyond the end */ + } else { int v = bitnum / BIGNUM_INT_BITS + 1; BignumInt mask = (BignumInt)1 << (bitnum % BIGNUM_INT_BITS); if (value) From f53a6553a33adbd44d2a61a0c336448083ff8b5a Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 7 Nov 2015 10:12:00 +0000 Subject: [PATCH 02/31] Post-0.66 release checklist updates. The one-off reminder to finish the key rollover is now done, so I can remove it. (cherry picked from commit 503061e569af091b9c31f75e5e17c6f39a70f72f) --- CHECKLST.txt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/CHECKLST.txt b/CHECKLST.txt index 78e9d223..93996f8d 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -112,11 +112,6 @@ for it: + .htaccess has an individual redirect for each version number. Add a new one. - - For 0.66 only: if it's not already done, switch the remaining - signature links on the Download page over to using the new - signature style. Then remove this checklist item, since it'll only - need doing this once. - - If there are any last-minute wishlist entries (e.g. security vulnerabilities fixed in the new release), write entries for them in a local checkout of putty-wishlist. From 6e0146476c9887a49f1039b1ccafd59b5998895f Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 7 Nov 2015 15:15:07 +0000 Subject: [PATCH 03/31] One small post-release checklist tweak. I spotted that I've been checking that old-style Windows Help files were delivered with content-type "application/octet-stream", but not also checking the same thing about the marginally newer .CHM ones. (Or at least not writing it down in the wishlist; I think I did actually check on at least one occasion.) (cherry picked from commit 3552f37ba5eab32247e44af96fa7a41994268159) --- CHECKLST.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHECKLST.txt b/CHECKLST.txt index 93996f8d..ff3c62fa 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -174,8 +174,8 @@ locally, this is the procedure for putting it up on the web. should do this automatically, owing to the `latest' HTTP redirect.) - - Check that the web server attaches the right content type to .HLP - and .CNT files. + - Check that the web server attaches the right content type to .HLP, + .CNT and .CHM files. - Run 'git push' in the website checkout, and then 'git pull' in ~/www/putty on atreus to fetch the website updates. From 4c5ba660668228fb3ed4732712d2538dad9d1ebf Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 7 Nov 2015 15:59:00 +0000 Subject: [PATCH 04/31] More post-release checklist updates, and a new script. I've added a few sample shell commands in the upload procedure (mostly so that I don't have to faff about remembering how rsync trailing slashes work every time), and also written a script called 'release.pl', which automates the updating of the version number in all the various places it needs to be done and also ensures the PSCP and Plink transcripts in the docs will match the release itself. (cherry picked from commit f3230c85457cc3d13c46e8ea91c9748dcd0054af) --- CHECKLST.txt | 42 ++++++++++++++++--------------- release.pl | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 20 deletions(-) create mode 100755 release.pl diff --git a/CHECKLST.txt b/CHECKLST.txt index ff3c62fa..51e11593 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -64,29 +64,25 @@ for it: XXX-REVIEW-BEFORE-RELEASE. ('git grep XXX-RE' should only show up hits in this file itself.) - - Now update version numbers in + - Now update the version numbers and the transcripts in the docs, by + checking out the release branch and running + + make distclean + ./release.pl --set-version=X.YZ + + Then check that the resulting automated git commit has updated the + version number in the following places: + * putty/LATEST.VER + * putty/doc/plink.but + * putty/doc/pscp.but * putty/windows/putty.iss (four times, on consecutive lines) - * putty/doc/pscp.but (and make sure the rest of the transcript is - up to date) - * putty/doc/plink.but (likewise) - - Reset the epoch used for the $(Days) value computed in Buildscr for - the Windows binary version resource. It's probably not a good idea - to set it to _today_ (since it might clash with the zero-valued - field used in actual releases), so perhaps we should start it 1000 - days before the release date so as to have a largish number - recognisable as being the right kind of thing by its order of - magnitude. So, do this: + and also check that it has reset the definition of 'Epoch' in + Buildscr. - perl -e 'printf "%d\n", time/86400 - 1000' - - and then substitute the resulting value into the definition of - 'Epoch' in Buildscr. - - - Commit those version number and epoch changes (on the appropriate - branch, of course!), and then make the release tag pointing at the - resulting commit. + - Make the release tag, pointing at the version-update commit we just + generated. - If the release is on a branch (which I expect it generally will be), merge that branch to master. @@ -146,8 +142,10 @@ locally, this is the procedure for putting it up on the web. - Save the link maps. Currently I keep these on atreus, in src/putty-local/maps-. + rsync -av maps-x86/ atreus:src/putty-local/maps-X.YZ - Upload the entire release directory to atreus:www/putty/. + rsync -av putty/ atreus:www/putty/X.YZ - Do final checks on the release directory in its new location: + verify all the signatures: @@ -160,6 +158,8 @@ locally, this is the procedure for putting it up on the web. - Having double-checked the release, copy it from atreus to chiark:ftp/putty- and to the:www/putty/. + rsync -av putty/ chiark:ftp/putty-X.YZ + rsync -av putty/ the:www/putty/X.YZ - Check the permissions! Actually try downloading from the, to make sure it really works. @@ -175,7 +175,9 @@ locally, this is the procedure for putting it up on the web. redirect.) - Check that the web server attaches the right content type to .HLP, - .CNT and .CHM files. + .CNT and .CHM files, by downloading one of each and checking + they're all application/octet-stream. + for ext in hlp cnt chm; do curl -v http://the.earth.li/~sgtatham/putty/X.YZ/putty.$ext 2>&1 >/dev/null | grep Content-Type; done - Run 'git push' in the website checkout, and then 'git pull' in ~/www/putty on atreus to fetch the website updates. diff --git a/release.pl b/release.pl new file mode 100755 index 00000000..27e0f8c3 --- /dev/null +++ b/release.pl @@ -0,0 +1,71 @@ +#!/usr/bin/perl + +# Script to automate some easy-to-mess-up parts of the PuTTY release +# procedure. + +use strict; +use warnings; +use Getopt::Long; +use File::Temp qw/tempdir/; + +my $version = undef; +GetOptions("set-version=s" => \$version) + or &usage(); + +if (defined $version) { + 0 == system "git", "diff-index", "--quiet", "--cached", "HEAD" + or die "index is dirty"; + 0 == system "git", "diff-files", "--quiet" or die "working tree is dirty"; + -f "Makefile" and die "run 'make distclean' first"; + my $builddir = tempdir(DIR => ".", CLEANUP => 1); + 0 == system "./mkfiles.pl" or die; + 0 == system "cd $builddir && ../configure" or die; + 0 == system "cd $builddir && make pscp plink RELEASE=${version}" or die; + our $pscp_transcript = `cd $builddir && ./pscp --help`; + $pscp_transcript =~ s/^Unidentified build/Release ${version}/m or die; + $pscp_transcript =~ s/^/\\c /mg; + our $plink_transcript = `cd $builddir && ./plink --help`; + $plink_transcript =~ s/^Unidentified build/Release ${version}/m or die; + $plink_transcript =~ s/^/\\c /mg; + &transform("LATEST.VER", sub { s/^\d+\.\d+$/$version/ }); + &transform("windows/putty.iss", sub { + s/^(AppVerName=PuTTY version |VersionInfoTextVersion=Release |AppVersion=|VersionInfoVersion=)\d+\.\d+/$1$version/ }); + our $transforming = 0; + &transform("doc/pscp.but", sub { + if (/^\\c.*>pscp$/) { $transforming = 1; $_ .= $pscp_transcript; } + elsif (!/^\\c/) { $transforming = 0; } + elsif ($transforming) { $_=""; } + }); + $transforming = 0; + &transform("doc/plink.but", sub { + if (/^\\c.*>plink$/) { $transforming = 1; $_ .= $plink_transcript; } + elsif (!/^\\c/) { $transforming = 0; } + elsif ($transforming) { $_=""; } + }); + &transform("Buildscr", sub { + s!^(set Epoch )\d+!$1 . sprintf "%d", time/86400 - 1000!e }); + 0 == system ("git", "commit", "-a", "-m", + "Update version number for ${version} release.") or die; + exit 0; +} + +&usage(); + +sub transform { + my ($filename, $proc) = @_; + my $file; + open $file, "<", $filename or die "$file: open for read: $!\n"; + my $data = ""; + while (<$file>) { + $proc->(); + $data .= $_; + } + close $file; + open $file, ">", $filename or die "$file: open for write: $!\n"; + print $file $data; + close $file or die "$file: close after write: $!\n";; +} + +sub usage { + die "usage: release.pl --set-version=X.YZ\n"; +} From ac9862ec91dba84f9906b1fd58e922321c918918 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sun, 8 Nov 2015 11:57:39 +0000 Subject: [PATCH 05/31] Rationalise and document log options somewhat. TOOLTYPE_NONNETWORK (i.e. pterm) already has "-log" (as does Unix PuTTY), so there's no sense suppressing the synonym "-sessionlog". Undocumented lacunae that remain: plink accepts -sessionlog, but does nothing with it. Arguably it should. puttytel accepts -sshlog/-sshrawlog (and happily logs e.g. Telnet negotiation, as does PuTTY proper). (cherry picked from commit a454399ec8d841e627d9d5e05ac977536e776754) Conflicts: unix/uxplink.c windows/winplink.c (cherry-picker's notes: the conflict was only contextual, in the Plink help output) --- cmdline.c | 16 +++++++++++++--- doc/man-pl.but | 14 ++++++++++++++ doc/man-pscp.but | 14 ++++++++++++++ doc/man-psft.but | 14 ++++++++++++++ doc/man-ptel.but | 2 +- doc/man-pter.but | 2 +- doc/man-putt.but | 15 ++++++++++++++- pscp.c | 3 +++ psftp.c | 3 +++ unix/gtkwin.c | 2 +- unix/uxplink.c | 3 +++ windows/winplink.c | 3 +++ 12 files changed, 84 insertions(+), 7 deletions(-) diff --git a/cmdline.c b/cmdline.c index bcfdcf88..9f3360b2 100644 --- a/cmdline.c +++ b/cmdline.c @@ -573,8 +573,19 @@ int cmdline_process_param(char *p, char *value, int need_save, Conf *conf) } } - if (!strcmp(p, "-sessionlog") || - !strcmp(p, "-sshlog") || + if (!strcmp(p, "-sessionlog")) { + Filename *fn; + RETURN(2); + UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER); + /* but available even in TOOLTYPE_NONNETWORK, cf pterm "-log" */ + SAVEABLE(0); + fn = filename_from_str(value); + conf_set_filename(conf, CONF_logfilename, fn); + conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); + filename_free(fn); + } + + if (!strcmp(p, "-sshlog") || !strcmp(p, "-sshrawlog")) { Filename *fn; RETURN(2); @@ -583,7 +594,6 @@ int cmdline_process_param(char *p, char *value, int need_save, Conf *conf) fn = filename_from_str(value); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, - !strcmp(p, "-sessionlog") ? LGTYP_DEBUG : !strcmp(p, "-sshlog") ? LGTYP_PACKETS : /* !strcmp(p, "-sshrawlog") ? */ LGTYP_SSHRAW); filename_free(fn); diff --git a/doc/man-pl.but b/doc/man-pl.but index 6a358cea..b597b83f 100644 --- a/doc/man-pl.but +++ b/doc/man-pl.but @@ -184,6 +184,20 @@ DSR/DTR. } +\dt \cw{\-sshlog} \e{logfile} + +\dt \cw{\-sshrawlog} \e{logfile} + +\dd For SSH connections, these options make \cw{plink} log protocol +details to a file. (Some of these may be sensitive, although by default +an effort is made to suppress obvious passwords.) + +\lcont{ +\cw{\-sshlog} logs decoded SSH packets and other events (those that +\cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw +encrypted packet data. +} + \S{plink-manpage-more-information} MORE INFORMATION For more information on plink, it's probably best to go and look at diff --git a/doc/man-pscp.but b/doc/man-pscp.but index 5cd5ac8c..1b95d4d2 100644 --- a/doc/man-pscp.but +++ b/doc/man-pscp.but @@ -115,6 +115,20 @@ written. } \dd Force use of SFTP protocol. +\dt \cw{\-sshlog} \e{logfile} + +\dt \cw{\-sshrawlog} \e{logfile} + +\dd These options make \cw{pscp} log protocol details to a file. +(Some of these may be sensitive, although by default an effort is made +to suppress obvious passwords.) + +\lcont{ +\cw{\-sshlog} logs decoded SSH packets and other events (those that +\cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw +encrypted packet data. +} + \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-psft.but index f24d7206..0194779a 100644 --- a/doc/man-psft.but +++ b/doc/man-psft.but @@ -95,6 +95,20 @@ 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. } +\dt \cw{\-sshlog} \e{logfile} + +\dt \cw{\-sshrawlog} \e{logfile} + +\dd These options make \cw{psftp} log protocol details to a file. +(Some of these may be sensitive, although by default an effort is made +to suppress obvious passwords.) + +\lcont{ +\cw{\-sshlog} logs decoded SSH packets and other events (those that +\cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw +encrypted packet data. +} + \S{psftp-manpage-commands} COMMANDS For a list of commands available inside \cw{psftp}, type \cw{help} diff --git a/doc/man-ptel.but b/doc/man-ptel.but index c0ee9c64..07a756b1 100644 --- a/doc/man-ptel.but +++ b/doc/man-ptel.but @@ -109,7 +109,7 @@ changed under control of the server.) to specify it explicitly if you have changed the default using the \cw{ScrollBar} resource. -\dt \cw{\-log} \e{filename} +\dt \cw{\-log} \e{logfile}, \cw{\-sessionlog} \e{logfile} \dd This option makes \cw{puttytel} log all the terminal output to a file as well as displaying it in the terminal. diff --git a/doc/man-pter.but b/doc/man-pter.but index a357e3e3..26d5842d 100644 --- a/doc/man-pter.but +++ b/doc/man-pter.but @@ -153,7 +153,7 @@ default using the \cw{LoginShell} resource. to specify it explicitly if you have changed the default using the \cw{ScrollBar} resource. -\dt \cw{\-log} \e{filename} +\dt \cw{\-log} \e{logfile}, \cw{\-sessionlog} \e{logfile} \dd This option makes \cw{pterm} log all the terminal output to a file as well as displaying it in the terminal. diff --git a/doc/man-putt.but b/doc/man-putt.but index 76694def..67bcba83 100644 --- a/doc/man-putt.but +++ b/doc/man-putt.but @@ -108,11 +108,24 @@ changed under control of the server.) to specify it explicitly if you have changed the default using the \cw{ScrollBar} resource. -\dt \cw{\-log} \e{filename} +\dt \cw{\-log} \e{logfile}, \cw{\-sessionlog} \e{logfile} \dd This option makes \cw{putty} log all the terminal output to a file as well as displaying it in the terminal. +\dt \cw{\-sshlog} \e{logfile} + +\dt \cw{\-sshrawlog} \e{logfile} + +\dd For SSH connections, these options make \cw{putty} log protocol +details to a file. (Some of these may be sensitive, although by default +an effort is made to suppress obvious passwords.) + +\lcont{ +\cw{\-sshlog} logs decoded SSH packets and other events (those that +\cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw +encrypted packet data. +} \dt \cw{\-cs} \e{charset} diff --git a/pscp.c b/pscp.c index e56d760f..3e41454d 100644 --- a/pscp.c +++ b/pscp.c @@ -2262,6 +2262,9 @@ static void usage(void) printf(" -unsafe allow server-side wildcards (DANGEROUS)\n"); printf(" -sftp force use of SFTP protocol\n"); printf(" -scp force use of SCP protocol\n"); + printf(" -sshlog file\n"); + printf(" -sshrawlog file\n"); + printf(" log protocol details to a file\n"); #if 0 /* * -gui is an internal option, used by GUI front ends to get diff --git a/psftp.c b/psftp.c index 2a82ff8e..7f081ab5 100644 --- a/psftp.c +++ b/psftp.c @@ -2679,6 +2679,9 @@ static void usage(void) printf(" -hostkey aa:bb:cc:...\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -batch disable all interactive prompts\n"); + printf(" -sshlog file\n"); + printf(" -sshrawlog file\n"); + printf(" log protocol details to a file\n"); cleanup_exit(1); } diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 1c29b41e..12501e69 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -2653,7 +2653,7 @@ static void help(FILE *fp) { " -ut, +ut Do(default) or do not update utmp\n" " -ls, +ls Do(default) or do not make shell a login shell\n" " -sb, +sb Do(default) or do not display a scrollbar\n" -" -log PATH Log all output to a file\n" +" -log PATH, -sessionlog PATH Log all output to a file\n" " -nethack Map numeric keypad to hjklyubn direction keys\n" " -xrm RESOURCE-STRING Set an X resource\n" " -e COMMAND [ARGS...] Execute command (consumes all remaining args)\n" diff --git a/unix/uxplink.c b/unix/uxplink.c index 6290a2c2..90ad8d52 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -580,6 +580,9 @@ static void usage(void) printf(" -N don't start a shell/command (SSH-2 only)\n"); printf(" -nc host:port\n"); printf(" open tunnel in place of session (SSH-2 only)\n"); + printf(" -sshlog file\n"); + printf(" -sshrawlog file\n"); + printf(" log protocol details to a file\n"); exit(1); } diff --git a/windows/winplink.c b/windows/winplink.c index 188d89bb..77a2dc54 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -212,6 +212,9 @@ static void usage(void) printf(" -N don't start a shell/command (SSH-2 only)\n"); printf(" -nc host:port\n"); printf(" open tunnel in place of session (SSH-2 only)\n"); + printf(" -sshlog file\n"); + printf(" -sshrawlog file\n"); + printf(" log protocol details to a file\n"); exit(1); } From c195ff2b4f0e7cae40232ae71fe3100027a29061 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:22:23 +0000 Subject: [PATCH 06/31] Fix a segfault in parsing OpenSSH private key files. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The initial test for a line ending with "PRIVATE KEY-----" failed to take into account the possibility that the line might be shorter than that. Fixed by introducing a new library function strendswith(), and strstartswith() for good measure, and using that. Thanks to Hanno Böck for spotting this, with the aid of AFL. (cherry picked from commit fa7b23ce9025daba08e86bb934fc430099792b9a) Conflicts: misc.c misc.h (cherry-picker's note: the conflicts were only due to other functions introduced on trunk just next to the ones introduced by this commit) --- import.c | 8 ++++---- misc.c | 11 +++++++++++ misc.h | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/import.c b/import.c index bc35a4ab..466326f6 100644 --- a/import.c +++ b/import.c @@ -345,8 +345,8 @@ static struct openssh_key *load_openssh_key(const Filename *filename, goto error; } strip_crlf(line); - if (0 != strncmp(line, "-----BEGIN ", 11) || - 0 != strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) { + if (!strstartswith(line, "-----BEGIN ") || + !strendswith(line, "PRIVATE KEY-----")) { errmsg = "file does not begin with OpenSSH key header"; goto error; } @@ -369,8 +369,8 @@ static struct openssh_key *load_openssh_key(const Filename *filename, goto error; } strip_crlf(line); - if (0 == strncmp(line, "-----END ", 9) && - 0 == strcmp(line+strlen(line)-16, "PRIVATE KEY-----")) { + if (strstartswith(line, "-----END ") && + strendswith(line, "PRIVATE KEY-----")) { sfree(line); line = NULL; break; /* done */ diff --git a/misc.c b/misc.c index 507837f9..d3a67f66 100644 --- a/misc.c +++ b/misc.c @@ -1035,3 +1035,14 @@ int smemeq(const void *av, const void *bv, size_t len) * we want to return 1, so then we can just shift down. */ return (0x100 - val) >> 8; } + +int strstartswith(const char *s, const char *t) +{ + return !memcmp(s, t, strlen(t)); +} + +int strendswith(const char *s, const char *t) +{ + size_t slen = strlen(s), tlen = strlen(t); + return slen >= tlen && !strcmp(s + (slen - tlen), t); +} diff --git a/misc.h b/misc.h index a16a2fa0..e53f8929 100644 --- a/misc.h +++ b/misc.h @@ -51,6 +51,8 @@ wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string); int toint(unsigned); char *fgetline(FILE *fp); +int strstartswith(const char *s, const char *t); +int strendswith(const char *s, const char *t); void base64_encode_atom(unsigned char *data, int n, char *out); int base64_decode_atom(char *atom, unsigned char *out); From cac650b8a56a362b5fc8f6309ad83734bf6a71e5 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 10 Nov 2015 18:49:09 +0000 Subject: [PATCH 07/31] Fix an out-of-bounds read in fgetline(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Forgot that a zero-length string might have come back from fgets. Thanks to Hanno Böck for spotting this, with the aid of AFL. (cherry picked from commit 5815d6a65af992881f5462097c9320f3a4716e0c) --- misc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc.c b/misc.c index d3a67f66..d40f9901 100644 --- a/misc.c +++ b/misc.c @@ -459,7 +459,7 @@ char *fgetline(FILE *fp) int size = 512, len = 0; while (fgets(ret + len, size - len, fp)) { len += strlen(ret + len); - if (ret[len-1] == '\n') + if (len > 0 && ret[len-1] == '\n') break; /* got a newline, we're done */ size = len + 512; ret = sresize(ret, size, char); From f0222e74645297e410f90815c16474918d6ca41b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:26:33 +0000 Subject: [PATCH 08/31] Fix potential segfaults in reading OpenSSH's ASN.1 key format. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The length coming back from ber_read_id_len might have overflowed, so treat it as potentially negative. Also, while I'm here, accumulate it inside ber_read_id_len as an unsigned, so as to avoid undefined behaviour on integer overflow, and toint() it before return. Thanks to Hanno Böck for spotting this, with the aid of AFL. (cherry picked from commit 5b7833cd474a24ec098654dcba8cb9509f3bf2c1) Conflicts: import.c (cherry-picker's note: resolving the conflict involved removing an entire section of the original commit which fixed ECDSA code not present on this branch) --- import.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/import.c b/import.c index 466326f6..4f3ed1fa 100644 --- a/import.c +++ b/import.c @@ -176,14 +176,16 @@ static int ber_read_id_len(void *source, int sourcelen, return -1; if (*p & 0x80) { + unsigned len; int n = *p & 0x7F; p++, sourcelen--; if (sourcelen < n) return -1; - *length = 0; + len = 0; while (n--) - *length = (*length << 8) | (*p++); + len = (len << 8) | (*p++); sourcelen -= n; + *length = toint(len); } else { *length = *p; p++, sourcelen--; @@ -599,7 +601,8 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, * decrypt, if the key was encrypted. */ ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); p += ret; - if (ret < 0 || id != 16) { + if (ret < 0 || id != 16 || len < 0 || + key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; @@ -630,7 +633,7 @@ struct ssh2_userkey *openssh_read(const Filename *filename, char *passphrase, ret = ber_read_id_len(p, key->keyblob+key->keyblob_len-p, &id, &len, &flags); p += ret; - if (ret < 0 || id != 2 || + if (ret < 0 || id != 2 || len < 0 || key->keyblob+key->keyblob_len-p < len) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; From c4f6b60cfd16e0cad34b356598c183c9e9cb9042 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 12 Nov 2015 19:09:36 +0000 Subject: [PATCH 09/31] Further release automation. I've added extra modes to release.pl which should automate the more tedious parts of the deployment phase: uploading the release build to all the places it needs to go, checking its integrity once it gets there, verifying that everything can be downloaded again usefully, checking content-types etc. The new version should check more thoroughly (it checks the whole FTP and HTTP download directories, so it will spot errors like failing to update the FTP 'latest' symlink), and take fewer commands to run. (cherry picked from commit f08e2de078b9122d4732a94cbbd81ca66cb87eed) --- release.pl | 170 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 2 deletions(-) diff --git a/release.pl b/release.pl index 27e0f8c3..73106d59 100755 --- a/release.pl +++ b/release.pl @@ -6,13 +6,26 @@ use strict; use warnings; use Getopt::Long; +use File::Find; use File::Temp qw/tempdir/; +use LWP::UserAgent; my $version = undef; -GetOptions("set-version=s" => \$version) +my $setver = 0; +my $upload = 0; +my $precheck = 0; +my $postcheck = 0; +GetOptions("version=s" => \$version, + "setver" => \$setver, + "upload" => \$upload, + "precheck" => \$precheck, + "postcheck" => \$postcheck) or &usage(); -if (defined $version) { +# --set-version: 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"; 0 == system "git", "diff-index", "--quiet", "--cached", "HEAD" or die "index is dirty"; 0 == system "git", "diff-files", "--quiet" or die "working tree is dirty"; @@ -49,6 +62,159 @@ if (defined $version) { exit 0; } +# --upload: upload the release to all the places it should live, and +# check all signatures and md5sums once it arrives there. +if ($upload) { + defined $version or die "use --version"; + + # Run this inside the build.out directory. + -d "maps-x86" or die "no maps-x86 directory in cwd"; + -d "putty" or die "no putty directory in cwd"; + + 0 == system("rsync", "-av", "maps-x86/", + "atreus:src/putty-local/maps-$version") + or die "could not upload link maps"; + + for my $location (["atreus", "www/putty/$version"], + ["the", "www/putty/$version"], + ["chiark", "ftp/putty-$version"]) { + my ($host, $path) = @$location; + 0 == system("rsync", "-av", "putty/", "$host:$path") + or die "could not upload release to $host"; + open my $pipe, "|-", "ssh", $host, "cd $path && sh"; + print $pipe "set -e\n"; + print $pipe "pwd\n"; + find({ wanted => sub + { + if (m!^putty/(.*).gpg!) { + my $file = $1; + print $pipe "echo verifying $file\n"; + if ($file =~ /sums$/) { + print $pipe "gpg --verify $file.gpg\n"; + } else { + print $pipe "gpg --verify $file.gpg $file\n"; + } + } elsif (m!^putty/(.*sum)s!) { + print $pipe "echo checking ${1}s\n"; + print $pipe "$1 -c ${1}s\n"; + } + }, no_chdir => 1}, "putty"); + print $pipe "echo all verified ok\n"; + close $pipe; + die "VERIFICATION FAILED on $host" if $? != 0; + } + + print "Uploaded $version OK!\n"; + exit 0; +} + +# --precheck and --postcheck: attempt to download the release from its +# various web and FTP locations. +if ($precheck || $postcheck) { + defined $version or die "use --version"; + + # Run this inside the build.out directory, so we can check the + # downloaded files against the exact contents they should have. + -d "putty" or die "no putty directory in cwd"; + + my $httpprefix = "http://the.earth.li/~sgtatham/putty/"; + my $ftpprefix = "ftp://ftp.chiark.greenend.org.uk/users/sgtatham/putty-"; + + # Go through all the files in build.out. + find({ wanted => sub + { + if (-f $_) { + die unless (m!^putty/(.*)$!); + my $path = $1; + + # Don't try to check .htaccess - web servers will + # treat it weirdly. + return if $path =~ m!^(.*/)?.htaccess$!; + + print "Checking $path\n"; + + my $real_content = ""; + open my $fh, "<", $_ or die "$_: open local file: $!"; + $real_content .= $_ while <$fh>; + close $fh; + + my $http_numbered = "${httpprefix}$version/$path"; + my $http_latest = "${httpprefix}latest/$path"; + my $ftp_numbered = "${ftpprefix}$version/$path"; + my $ftp_latest = "${ftpprefix}latest/$path"; + + my ($http_uri, $ftp_uri); + + if ($precheck) { + # Before the 'latest' links/redirects update, + # we just download from explicitly version- + # numbered URLs. + $http_uri = $http_numbered; + $ftp_uri = $ftp_numbered; + } + if ($postcheck) { + # After 'latest' is updated, we're testing that + # the redirects work, so we download from the + # URLs with 'latest' in them. + $http_uri = $http_latest; + $ftp_uri = $ftp_latest; + } + + # Now test-download the files themselves. + my $ftpdata = `curl -s $ftp_uri`; + printf " got %d bytes via FTP", length $ftpdata; + die "FTP download for $ftp_uri did not match" + if $ftpdata ne $real_content; + print ", ok\n"; + + my $ua = LWP::UserAgent->new; + my $httpresponse = $ua->get($http_uri); + my $httpdata = $httpresponse->{_content}; + printf " got %d bytes via HTTP", length $httpdata; + die "HTTP download for $http_uri did not match" + if $httpdata ne $real_content; + print ", ok\n"; + + # Check content types on any files likely to go + # wrong. + my $ct = $httpresponse->{_headers}->{"content-type"}; + if (defined $ct) { + printf " got content-type %s", $ct; + } else { + printf " got no content-type"; + } + my $right_ct = undef; + if ($path =~ m/\.(hlp|cnt|chm)$/) { + $right_ct = "application/octet-stream"; + } elsif ($path =~ /\.gpg$/) { + $right_ct = "application/pgp-signature"; + } + if (defined $right_ct) { + if ($ct ne $right_ct) { + die "content-type $ct should be $right_ct"; + } else { + print ", ok\n"; + } + } else { + print "\n"; + } + + if ($postcheck) { + # Finally, if we're testing the 'latest' URL, + # also check that the HTTP redirect header was + # present and correct. + my $redirected = $httpresponse->{_request}->{_uri}; + printf " redirect -> %s\n", $redirected; + die "redirect header wrong for $http_uri" + if $redirected ne $http_numbered; + } + } + }, no_chdir => 1}, "putty"); + + print "Check OK\n"; + exit 0; +} + &usage(); sub transform { From 4ff60ab7f50e4623850b6561716ae8c17886e506 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 12 Nov 2015 19:11:07 +0000 Subject: [PATCH 10/31] Big revision to CHECKLST.txt for release.pl and Mason. Half the release checklist has changed recently, what with me completely reworking the website and also writing all this release automation. I think these are all the checklist changes needed now the dust has settled, though of course when I do the next actual release I expect there'll turn out to be something I missed... (cherry picked from commit 3e811b3dff506cef03426469fc676a519d531781) --- CHECKLST.txt | 127 +++++++++++++++++++-------------------------------- 1 file changed, 48 insertions(+), 79 deletions(-) diff --git a/CHECKLST.txt b/CHECKLST.txt index 51e11593..dd1218a7 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -35,10 +35,6 @@ The documentation (both the preamble blurb and the licence appendix): - putty/doc/blurb.but - putty/doc/licence.but -The website: - - - putty-website/licence.html - Preparing to make a release --------------------------- @@ -68,7 +64,7 @@ for it: checking out the release branch and running make distclean - ./release.pl --set-version=X.YZ + ./release.pl --version=X.YZ --setver Then check that the resulting automated git commit has updated the version number in the following places: @@ -92,29 +88,23 @@ for it: atreus:src/putty-local/announce- in case it's needed again within days of the release going out. - - Update the web site, in a local checkout. - + Adjust front page to say 'The latest version is '. - + Adjust front page to add a news item. - + Adjust Download page to say 'The latest release version ()'. - + Adjust Download page to update filenames of installer and Unix - tarball (both in the hrefs themselves and the link text). - + Check over the Download page and remove any mention of - pre-releases, if there were any before this release. Comment out - the big pre-release section at the top, and also adjust the - sections about source archives at the bottom. - + Do the same on the Docs page. - + Adjust header text on Changelog page. (That includes changing - `are new' in previous version to `were new'!) - + .htaccess has an individual redirect for each version number. Add - a new one. + - Update the website, in a local checkout: + * Write a release file in components/releases which identifies the + new version, its release date, a section for the Changes page, + and a news announcement for the front page. + * Disable the pre-release sections of the website (if previously + enabled), by editing prerel_version() in components/Base.mc to + return undef. - - If there are any last-minute wishlist entries (e.g. security - vulnerabilities fixed in the new release), write entries for them - in a local checkout of putty-wishlist. - - - Update the wishlist mechanism for the new release. This can be done - without touching individual items by editing the @releases array in - control/bugs2html. + - Update the wishlist, in a local checkout: + * If there are any last-minute wishlist entries (e.g. security + vulnerabilities fixed in the new release), write entries for + them. + * If any other bug fixes have been cherry-picked to the release + branch (so that the wishlist mechanism can't automatically mark + them as fixed in the new release), add appropriate Fixed-in + headers for those. + * Add an entry to the @releases array in control/bugs2html. - Build the release, by checking out the release tag: git checkout 0.XX @@ -126,9 +116,13 @@ for it: - Double-check in build.log that the release was built from the right git commit. - - Do a bit of checking that the release binaries basically work, - report their version numbers accurately, and so on. Test the - installer and the Unix source tarball. + - Do a bit of checking of the release binaries: + * make sure they basically work + * 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. - Sign the release: in the `build.out' directory, type sh sign.sh -r putty @@ -140,61 +134,36 @@ The actual release procedure Once all the above preparation is done and the release has been built locally, this is the procedure for putting it up on the web. - - Save the link maps. Currently I keep these on atreus, in - src/putty-local/maps-. - rsync -av maps-x86/ atreus:src/putty-local/maps-X.YZ + - Upload the release itself and its link maps to everywhere it needs + to be, by running this in the build.out directory: + ../release.pl --version=X.YZ --upload - - Upload the entire release directory to atreus:www/putty/. - rsync -av putty/ atreus:www/putty/X.YZ + - Check that downloads via version-numbered URLs all work: + ../release.pl --version=X.YZ --precheck - - Do final checks on the release directory in its new location: - + verify all the signatures: - for i in `find . -name '*.gpg'`; do case $i in *sums*) gpg --verify $i;; *) gpg --verify $i ${i%%.gpg};; esac; done - + check the checksum files: - md5sum -c md5sums - sha1sum -c sha1sums - sha256sum -c sha256sums - sha512sum -c sha512sums + - Switch the 'latest' links over to the new release: + * Update the HTTP redirect at the:www/putty/htaccess . + * Update the FTP symlink at chiark:ftp/putty-latest . - - Having double-checked the release, copy it from atreus to - chiark:ftp/putty- and to the:www/putty/. - rsync -av putty/ chiark:ftp/putty-X.YZ - rsync -av putty/ the:www/putty/X.YZ + - Now verify that downloads via the 'latest' URLs are all redirected + correctly and work: + ../release.pl --version=X.YZ --postcheck - - Check the permissions! Actually try downloading from the, to make - sure it really works. + - Push all the git repositories: + * run 'git push' in the website checkout + * run 'git push' in the wishlist checkout + * push from the main PuTTY checkout. Typically this one will be + pushing both the release tag and an update to the master branch, + plus removing the pre-release branch, so you'll want some + commands along these lines: + git push origin master # update the master branch + git push origin --tags # should push the new release tag + git push origin :pre-0.XX # delete the pre-release branch - - Update the HTTP redirect at the:www/putty/htaccess which points the - virtual subdir `latest' at the actual latest release dir. TEST THIS - ONE - it's quite important. + - Run ~/adm/puttyweb.sh on atreus to update the website after all + those git pushes. - - Update the FTP symlink (chiark:ftp/putty-latest -> putty-). - - - Check the Docs page links correctly to the release docs. (It - should do this automatically, owing to the `latest' HTTP - redirect.) - - - Check that the web server attaches the right content type to .HLP, - .CNT and .CHM files, by downloading one of each and checking - they're all application/octet-stream. - for ext in hlp cnt chm; do curl -v http://the.earth.li/~sgtatham/putty/X.YZ/putty.$ext 2>&1 >/dev/null | grep Content-Type; done - - - Run 'git push' in the website checkout, and then 'git pull' in - ~/www/putty on atreus to fetch the website updates. - - - Push the changes to PuTTY itself. Something like: - git push origin master # update the master branch - git push origin --tags # should push the new release tag - git push origin :pre-0.XX # delete the pre-release branch - - - Run 'git push' in the putty-wishlist checkout. Then run 'git pull' - in ~/pub/putty-wishlist on atreus, and update the wishlist web - pages with the commands - cd ~/pub/putty-wishlist/control - perl bugs2html - - - Check over the web site to make sure all the changes to wishlist - and main web pages are present and correct. + - Check that the unpublished website on atreus looks sensible. - Run webupdate, so that all the changes on atreus propagate to chiark. Important to do this _before_ announcing that the release From 6b9bccf388b75674508e6f044cecc9fe28e107a2 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 17 Nov 2015 18:41:52 +0000 Subject: [PATCH 11/31] Convert Buildscr to use the new "do/win" mechanism. (cherry picked from commit 470337d0f2591534221390f50a69f8c9f6fe0558) --- Buildscr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Buildscr b/Buildscr index 466571bc..86532214 100644 --- a/Buildscr +++ b/Buildscr @@ -150,12 +150,12 @@ in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE delegate windows # FIXME: Cygwin alternative? - in putty/windows do cmd /c vcvars32 \& nmake -f Makefile.vc $(Makeargs) + in putty/windows do/win vcvars32 && nmake -f Makefile.vc $(Makeargs) # Ignore exit code from hhc, in favour of seeing whether the .chm # file was created. (Yuck; but hhc appears to return non-zero # exit codes on whim.) - in putty/doc do hhc putty.hhp; test -f putty.chm - in putty/windows do iscc putty.iss + in putty/doc do/win hhc putty.hhp & type putty.chm >nul + in putty/windows do/win iscc putty.iss return putty/windows/*.exe return putty/windows/*.map return putty/doc/putty.chm From 906f8ed262316d7180f929e4f9988d93df3e8797 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 21 Nov 2015 12:21:31 +0000 Subject: [PATCH 12/31] Document 'Cannot assign requested address' error. Often it means you tried to connect to port 0. (cherry picked from commit c4f963ebd71dd07b3c6dcade9a2a9a86a7322519) --- doc/errors.but | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/errors.but b/doc/errors.but index 2d253520..bf9672d0 100644 --- a/doc/errors.but +++ b/doc/errors.but @@ -338,3 +338,13 @@ 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 \k{config-ssh-kex-rekey}) or due to keepalives (\k{config-keepalive}). + +\H{errors-cannotassignaddress} \q{Network error: Cannot assign requested +address} + +This means that the operating system rejected the parameters of the +network connection PuTTY tried to make, usually without actually +trying to connect to anything, because they were simply invalid. + +A common way to provoke this error is to accidentally try to connect +to port 0, which is not a valid port number. From e80b1b8a3424d0d95f50c766d14b58f4b642da11 Mon Sep 17 00:00:00 2001 From: Owen Dunn Date: Sun, 22 Nov 2015 12:04:04 +0000 Subject: [PATCH 13/31] Move SID-getting code into a separate function so it can be shared by make_private_security_descriptor and a new function protectprocess(). protectprocess() opens the running PuTTY process and adjusts the Everyone and user access control entries in its ACL to deny a selection of permissions which malicious processes running as the same user could use to hijack PuTTY. (cherry picked from commit aba7234bc167c8c056a9ea4f939a6dcda10e84f3) --- windows/winsecur.c | 155 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 122 insertions(+), 33 deletions(-) diff --git a/windows/winsecur.c b/windows/winsecur.c index df93a74c..6e4bd7d4 100644 --- a/windows/winsecur.c +++ b/windows/winsecur.c @@ -12,6 +12,10 @@ #define WINSECUR_GLOBAL #include "winsecur.h" +/* Initialised once, then kept around to reuse forever */ +static PSID worldsid, networksid, usersid; + + int got_advapi(void) { static int attempted = FALSE; @@ -99,6 +103,52 @@ PSID get_user_sid(void) return ret; } +int getsids(char *error) +{ + SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; + int ret; + + error=NULL; + + if (!usersid) { + if ((usersid = get_user_sid()) == NULL) { + error = dupprintf("unable to construct SID for current user: %s", + win_strerror(GetLastError())); + goto cleanup; + } + } + + if (!worldsid) { + if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, &worldsid)) { + error = dupprintf("unable to construct SID for world: %s", + win_strerror(GetLastError())); + goto cleanup; + } + } + + if (!networksid) { + if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID, + 0, 0, 0, 0, 0, 0, 0, &networksid)) { + error = dupprintf("unable to construct SID for " + "local same-user access only: %s", + win_strerror(GetLastError())); + goto cleanup; + } + } + + ret=TRUE; + + cleanup: + if (ret) { + sfree(error); + error = NULL; + } + return ret; +} + + int make_private_security_descriptor(DWORD permissions, PSECURITY_DESCRIPTOR *psd, PACL *acl, @@ -110,44 +160,13 @@ int make_private_security_descriptor(DWORD permissions, int acl_err; int ret = FALSE; - /* Initialised once, then kept around to reuse forever */ - static PSID worldsid, networksid, usersid; *psd = NULL; *acl = NULL; *error = NULL; - if (!got_advapi()) { - *error = dupprintf("unable to load advapi32.dll"); - goto cleanup; - } - - if (!usersid) { - if ((usersid = get_user_sid()) == NULL) { - *error = dupprintf("unable to construct SID for current user: %s", - win_strerror(GetLastError())); - goto cleanup; - } - } - - if (!worldsid) { - if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, - 0, 0, 0, 0, 0, 0, 0, &worldsid)) { - *error = dupprintf("unable to construct SID for world: %s", - win_strerror(GetLastError())); - goto cleanup; - } - } - - if (!networksid) { - if (!AllocateAndInitializeSid(&nt_auth, 1, SECURITY_NETWORK_RID, - 0, 0, 0, 0, 0, 0, 0, &networksid)) { - *error = dupprintf("unable to construct SID for " - "local same-user access only: %s", - win_strerror(GetLastError())); - goto cleanup; - } - } + if (!getsids(*error)) + goto cleanup; memset(ea, 0, sizeof(ea)); ea[0].grfAccessPermissions = permissions; @@ -218,4 +237,74 @@ int make_private_security_descriptor(DWORD permissions, return ret; } +int protectprocess(char *error) +{ + SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; + SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; + EXPLICIT_ACCESS ea[2]; + int acl_err; + int ret=FALSE; + PACL acl = NULL; + + static const nastyace=WRITE_DAC | WRITE_OWNER | + PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | + PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | + PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | + PROCESS_SUSPEND_RESUME; + + if (!getsids(error)) + goto cleanup; + + memset(ea, 0, sizeof(ea)); + + /* Everyone: deny */ + ea[0].grfAccessPermissions = nastyace; + ea[0].grfAccessMode = DENY_ACCESS; + ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[0].Trustee.ptstrName = (LPTSTR)worldsid; + + /* User: user ace */ + ea[1].grfAccessPermissions = ~nastyace & 0x1fff; + ea[1].grfAccessMode = GRANT_ACCESS; + ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; + ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea[1].Trustee.ptstrName = (LPTSTR)usersid; + + acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl); + + if (acl_err != ERROR_SUCCESS || acl == NULL) { + error = dupprintf("unable to construct ACL: %s", + win_strerror(acl_err)); + goto cleanup; + } + + if (ERROR_SUCCESS != + SetSecurityInfo( + GetCurrentProcess(), + SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + usersid, + NULL, + acl, + NULL + )) { + error=dupprintf("Unable to set process ACL: %s", + win_strerror(GetLastError())); + goto cleanup; + } + + + ret=TRUE; + + cleanup: + if (!ret) { + if (acl) { + LocalFree(acl); + acl = NULL; + } + } + return ret; +} #endif /* !defined NO_SECURITY */ From 14d0a08a96b37690f2104f252c280cea7a699dae Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 22 Nov 2015 15:02:14 +0000 Subject: [PATCH 14/31] Fix a memory leak in uxproxy.c. We set up a pair of bufchains for the standard input and output exchanged with the proxy process, but forgot to clear them when the Local_Proxy_Socket is cleaned up. (cherry picked from commit bb66e9870e1d297de502767031563b8f2334cb1c) --- unix/uxproxy.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unix/uxproxy.c b/unix/uxproxy.c index fe81d5fd..6f1f793f 100644 --- a/unix/uxproxy.c +++ b/unix/uxproxy.c @@ -104,6 +104,8 @@ static void sk_localproxy_close (Socket s) uxsel_del(ps->from_cmd); close(ps->from_cmd); + bufchain_clear(&ps->pending_input_data); + bufchain_clear(&ps->pending_output_data); sfree(ps); } From db910f712c8355da553167ea5ad9cd267366a287 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:30:58 +0000 Subject: [PATCH 15/31] Make our process's ACL more restrictive. By default Windows processes have wide open ACLs which allow interference by other processes running as the same user. Adjust our ACL to make this a bit harder. Because it's useful to protect PuTTYtel as well, carve winsecur.c into advapi functions and wincapi.c for crypt32 functions. (cherry picked from commit 48db456801cf90369330248075b7e480252696ff) Conflicts: Recipe (cherry-picker's note: the conflict was just some context not looking quite the same) --- Recipe | 4 ++-- windows/wincapi.c | 27 +++++++++++++++++++++++++++ windows/wincapi.h | 18 ++++++++++++++++++ windows/window.c | 15 +++++++++++++++ windows/winsecur.c | 17 +---------------- windows/winsecur.h | 9 ++------- windows/winshare.c | 2 +- 7 files changed, 66 insertions(+), 26 deletions(-) create mode 100644 windows/wincapi.c create mode 100644 windows/wincapi.h diff --git a/Recipe b/Recipe index fc663296..1bd9e08b 100644 --- a/Recipe +++ b/Recipe @@ -218,7 +218,7 @@ SSH = ssh sshcrc sshdes sshmd5 sshrsa sshrand sshsha sshblowf + sshdh sshcrcda sshpubk sshzlib sshdss x11fwd portfwd + sshaes sshsh256 sshsh512 sshbn wildcard pinger ssharcf + sshgssc pgssapi sshshare -WINSSH = SSH winnoise winsecur winpgntc wingss winshare winnps winnpc +WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc + winhsock errsock UXSSH = SSH uxnoise uxagentc uxgss uxshare @@ -229,7 +229,7 @@ SFTP = sftp int64 logging # Pageant or PuTTYgen). MISC = timing callback misc version settings tree234 proxy conf WINMISC = MISC winstore winnet winhandl cmdline windefs winmisc winproxy - + wintime winhsock errsock + + wintime winhsock errsock winsecur UXMISC = MISC uxstore uxsel uxnet uxpeer cmdline uxmisc uxproxy time OSXMISC = MISC uxstore uxsel osxsel uxnet uxpeer uxmisc uxproxy time diff --git a/windows/wincapi.c b/windows/wincapi.c new file mode 100644 index 00000000..2550b6de --- /dev/null +++ b/windows/wincapi.c @@ -0,0 +1,27 @@ +/* + * wincapi.c: implementation of wincapi.h. + */ + +#include "putty.h" + +#if !defined NO_SECURITY + +#define WINCAPI_GLOBAL +#include "wincapi.h" + +int got_crypt(void) +{ + static int attempted = FALSE; + static int successful; + static HMODULE crypt; + + if (!attempted) { + attempted = TRUE; + crypt = load_system32_dll("crypt32.dll"); + successful = crypt && + GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory); + } + return successful; +} + +#endif /* !defined NO_SECURITY */ diff --git a/windows/wincapi.h b/windows/wincapi.h new file mode 100644 index 00000000..06ee2d36 --- /dev/null +++ b/windows/wincapi.h @@ -0,0 +1,18 @@ +/* + * wincapi.h: Windows Crypto API functions defined in wincrypt.c + * that use the crypt32 library. Also centralises the machinery + * for dynamically loading that library. + */ + +#if !defined NO_SECURITY + +#ifndef WINCAPI_GLOBAL +#define WINCAPI_GLOBAL extern +#endif + +DECL_WINDOWS_FUNCTION(WINCAPI_GLOBAL, BOOL, CryptProtectMemory, + (LPVOID,DWORD,DWORD)); + +int got_crypt(void); + +#endif diff --git a/windows/window.c b/windows/window.c index 2bbdc263..ad100c53 100644 --- a/windows/window.c +++ b/windows/window.c @@ -19,6 +19,7 @@ #include "terminal.h" #include "storage.h" #include "win_res.h" +#include "winsecur.h" #ifndef NO_MULTIMON #include @@ -390,6 +391,20 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) return 1; } + /* + * Protect our process + */ + { + char *error = NULL; + + if (! setprocessacl(error)) { + /* FIXME: prepare to stuff this into event log somehow */ + MessageBox(NULL, "Process protection", + error, MB_OK | MB_ICONEXCLAMATION); + } + sfree(error); + + } /* * Process the command line. */ diff --git a/windows/winsecur.c b/windows/winsecur.c index 6e4bd7d4..9cdac26c 100644 --- a/windows/winsecur.c +++ b/windows/winsecur.c @@ -36,21 +36,6 @@ int got_advapi(void) return successful; } -int got_crypt(void) -{ - static int attempted = FALSE; - static int successful; - static HMODULE crypt; - - if (!attempted) { - attempted = TRUE; - crypt = load_system32_dll("crypt32.dll"); - successful = crypt && - GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory); - } - return successful; -} - PSID get_user_sid(void) { HANDLE proc = NULL, tok = NULL; @@ -237,7 +222,7 @@ int make_private_security_descriptor(DWORD permissions, return ret; } -int protectprocess(char *error) +int setprocessacl(char *error) { SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; diff --git a/windows/winsecur.h b/windows/winsecur.h index bd649827..03e8314d 100644 --- a/windows/winsecur.h +++ b/windows/winsecur.h @@ -32,13 +32,6 @@ DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetEntriesInAclA, (ULONG, PEXPLICIT_ACCESS, PACL, PACL *)); int got_advapi(void); -/* - * Functions loaded from crypt32.dll. - */ -DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, BOOL, CryptProtectMemory, - (LPVOID, DWORD, DWORD)); -int got_crypt(void); - /* * Find the SID describing the current user. The return value (if not * NULL for some error-related reason) is smalloced. @@ -60,4 +53,6 @@ int make_private_security_descriptor(DWORD permissions, PACL *acl, char **error); +int setprocessacl(char *error); + #endif diff --git a/windows/winshare.c b/windows/winshare.c index 2f21638e..5f1c7244 100644 --- a/windows/winshare.c +++ b/windows/winshare.c @@ -14,7 +14,7 @@ #include "proxy.h" #include "ssh.h" -#include "winsecur.h" +#include "wincapi.h" #ifdef COVERITY /* From 7346e9bc4b8e51c77bfd92c4a4127ce2d26cb089 Mon Sep 17 00:00:00 2001 From: Owen Dunn Date: Tue, 24 Nov 2015 23:12:33 +0000 Subject: [PATCH 16/31] Surround process protection with an #ifndef UNPROTECT (cherry picked from commit 8b65fef55c688d8a52bd56f426e345671fab0303) --- windows/window.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/windows/window.c b/windows/window.c index ad100c53..ec7e1719 100644 --- a/windows/window.c +++ b/windows/window.c @@ -395,15 +395,14 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) * Protect our process */ { - char *error = NULL; - - if (! setprocessacl(error)) { - /* FIXME: prepare to stuff this into event log somehow */ - MessageBox(NULL, "Process protection", - error, MB_OK | MB_ICONEXCLAMATION); - } - sfree(error); - +#ifndef UNPROTECT + char *error = NULL; + if (! setprocessacl(error)) { + logevent(NULL, "Could not restrict process ACL: %s", + error); + } + sfree(error); +#endif } /* * Process the command line. From a01f3bfdad9f2709b105fbfac409e64e1d7e9462 Mon Sep 17 00:00:00 2001 From: Owen Dunn Date: Tue, 24 Nov 2015 23:13:03 +0000 Subject: [PATCH 17/31] Document UNPROTECT define that disables tightened ACL. (cherry picked from commit 21a37d287cced473c12d23581fc1a200552ad1e0) --- Recipe | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Recipe b/Recipe index 1bd9e08b..b3dbbcb2 100644 --- a/Recipe +++ b/Recipe @@ -114,6 +114,10 @@ # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # +# - XFLAGS=/DUNPROTECT +# Disable tightened ACL on PuTTY process so that e.g. debuggers +# can attach to it. +# # - XFLAGS=/DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. From 63597ea215870c8fa52c48be3ef19adec0f5b068 Mon Sep 17 00:00:00 2001 From: Owen Dunn Date: Fri, 27 Nov 2015 19:52:46 +0000 Subject: [PATCH 18/31] Move sfree inside if. (cherry picked from commit 0f5299e5a86e87068277b19c008ff5eb0f78d022) --- windows/window.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/window.c b/windows/window.c index ec7e1719..bcb0d9ca 100644 --- a/windows/window.c +++ b/windows/window.c @@ -400,8 +400,8 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) if (! setprocessacl(error)) { logevent(NULL, "Could not restrict process ACL: %s", error); + sfree(error); } - sfree(error); #endif } /* From 941421b8fa669f729129e7462457c29911476b17 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 27 Nov 2015 23:55:16 +0000 Subject: [PATCH 19/31] Fix a mistaken use of a format string in logevent(). logevent() doesn't do printf-style formatting (though the logeventf wrapper in ssh.c does), so if you need to format a message, it has to be done separately with dupprintf. (cherry picked from commit 1659cf3f1455f7e3d9c97a66f90a0cfa914d1ce3) --- windows/window.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/windows/window.c b/windows/window.c index bcb0d9ca..03325054 100644 --- a/windows/window.c +++ b/windows/window.c @@ -398,8 +398,10 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) #ifndef UNPROTECT char *error = NULL; if (! setprocessacl(error)) { - logevent(NULL, "Could not restrict process ACL: %s", - error); + char *message = dupprintf("Could not restrict process ACL: %s", + error); + logevent(NULL, message); + sfree(message); sfree(error); } #endif From 0d919e2124a3db43f23a2a330e18f767ebdaf361 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 11 Dec 2015 06:47:20 +0000 Subject: [PATCH 20/31] Code-sign the Windows PuTTY binaries and installer. Or, at least, potentially do so. The build script now has a slot into which code-signing can be dropped by setting a variable in the bob configuration to specify an appropriate command line. The variable will typically need to point at a script wrapping the actual signing tool, since there are lots of fiddly details (timestamping countersignature, certificate, private key, etc) not given on the command lines in this build script, on the basis that they're local configuration questions for whoever is _running_ this build script. (cherry picked from commit d0e9630e1c2f880bb7cb7ae107685bd1a6d189c4) --- Buildscr | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Buildscr b/Buildscr index 86532214..c9420658 100644 --- a/Buildscr +++ b/Buildscr @@ -151,11 +151,18 @@ in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE delegate windows # FIXME: Cygwin alternative? in putty/windows do/win vcvars32 && nmake -f Makefile.vc $(Makeargs) + # Code-sign the binaries, if the local bob config provides a script + # to do so. We assume here that the script accepts an -i option to + # provide a 'more info' URL, and an optional -n option to provide a + # program name, and that it can take multiple .exe filename + # arguments and sign them all in place. + ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/putty/ *.exe # Ignore exit code from hhc, in favour of seeing whether the .chm # file was created. (Yuck; but hhc appears to return non-zero # exit codes on whim.) in putty/doc do/win hhc putty.hhp & type putty.chm >nul in putty/windows do/win iscc putty.iss + ifneq "$(winsigncode)" "" in putty/windows do $(winsigncode) -i http://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" Output/setup.exe return putty/windows/*.exe return putty/windows/*.map return putty/doc/putty.chm From a5634e0ccb7955bef810cfaadb5562d6da33f5b1 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sat, 28 Nov 2015 18:31:10 +0000 Subject: [PATCH 21/31] Put back in a missing dynamic-load wrapper on SetSecurityInfo. We had inadvertently raised the minimum supported Windows version in the course of restricting PuTTY's ACL. (cherry picked from commit bf3621f247937b51e983f364377bb408b4cb609b) --- windows/winsecur.c | 15 +++++---------- windows/winsecur.h | 3 +++ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/windows/winsecur.c b/windows/winsecur.c index 9cdac26c..8d0b223a 100644 --- a/windows/winsecur.c +++ b/windows/winsecur.c @@ -27,6 +27,7 @@ int got_advapi(void) advapi = load_system32_dll("advapi32.dll"); successful = advapi && GET_WINDOWS_FUNCTION(advapi, GetSecurityInfo) && + GET_WINDOWS_FUNCTION(advapi, SetSecurityInfo) && GET_WINDOWS_FUNCTION(advapi, OpenProcessToken) && GET_WINDOWS_FUNCTION(advapi, GetTokenInformation) && GET_WINDOWS_FUNCTION(advapi, InitializeSecurityDescriptor) && @@ -265,16 +266,10 @@ int setprocessacl(char *error) goto cleanup; } - if (ERROR_SUCCESS != - SetSecurityInfo( - GetCurrentProcess(), - SE_KERNEL_OBJECT, - OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, - usersid, - NULL, - acl, - NULL - )) { + if (ERROR_SUCCESS != p_SetSecurityInfo + (GetCurrentProcess(), SE_KERNEL_OBJECT, + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + usersid, NULL, acl, NULL)) { error=dupprintf("Unable to set process ACL: %s", win_strerror(GetLastError())); goto cleanup; diff --git a/windows/winsecur.h b/windows/winsecur.h index 03e8314d..ed3151c5 100644 --- a/windows/winsecur.h +++ b/windows/winsecur.h @@ -28,6 +28,9 @@ DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, GetSecurityInfo, (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *)); +DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetSecurityInfo, + (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, + PSID, PSID, PACL, PACL)); DECL_WINDOWS_FUNCTION(WINSECUR_GLOBAL, DWORD, SetEntriesInAclA, (ULONG, PEXPLICIT_ACCESS, PACL, PACL *)); int got_advapi(void); From 7a5d434e34519335107187f26bbd7674a0a31334 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:39:22 +0000 Subject: [PATCH 22/31] Make some static text in GTK dialogs selectable. I've made the licence text, the About box, and the host key dialog into GTK selectable edit controls. (The former because it contains a lot of text; the About box because pasting version numbers into bug reports is obviously useful; the host key because of the fingerprint.) (cherry picked from commit 21101c7397e460933635a7bfed813864fc4f88fe) Conflicts: unix/gtkdlg.c unix/unix.h (cherry-picker's notes: not a trivial resolution, since I had to apply the equivalent changes in the pre-GTK3-port version of the code) --- unix/gtkdlg.c | 75 ++++++++++++++++++++++++++++++++++++++------------- unix/gtkwin.c | 2 +- unix/unix.h | 3 ++- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 86a71cd6..9139f6fc 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -3138,12 +3138,13 @@ static void messagebox_handler(union control *ctrl, void *dlg, if (event == EVENT_ACTION) dlg_end(dlg, ctrl->generic.context.i); } -int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) +int messagebox(GtkWidget *parentwin, char *title, char *msg, + int minwid, int selectable, ...) { GtkWidget *window, *w0, *w1; struct controlbox *ctrlbox; struct controlset *s0, *s1; - union control *c; + union control *c, *textctrl; struct dlgparam dp; struct Shortcuts scs; int index, ncols; @@ -3158,7 +3159,7 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) ctrlbox = ctrl_new_box(); ncols = 0; - va_start(ap, minwid); + va_start(ap, selectable); while (va_arg(ap, char *) != NULL) { ncols++; (void) va_arg(ap, int); /* shortcut */ @@ -3173,7 +3174,7 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) c->columns.percentages = sresize(c->columns.percentages, ncols, int); for (index = 0; index < ncols; index++) c->columns.percentages[index] = (index+1)*100/ncols - index*100/ncols; - va_start(ap, minwid); + va_start(ap, selectable); index = 0; while (1) { char *title = va_arg(ap, char *); @@ -3194,7 +3195,7 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) va_end(ap); s1 = ctrl_getset(ctrlbox, "x", "", ""); - ctrl_text(s1, msg, HELPCTX(no_help)); + textctrl = ctrl_text(s1, msg, HELPCTX(no_help)); window = gtk_dialog_new(); gtk_window_set_title(GTK_WINDOW(window), title); @@ -3213,6 +3214,26 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) dp.retval = 0; dp.window = window; + if (selectable) { +#if GTK_CHECK_VERSION(2,0,0) + struct uctrl *uc = dlg_find_byctrl(&dp, textctrl); + gtk_label_set_selectable(GTK_LABEL(uc->text), TRUE); + + /* + * GTK selectable labels have a habit of selecting their + * entire contents when they gain focus. It's ugly to have + * text in a message box start up all selected, so we suppress + * this by manually selecting none of it - but we must do this + * when the widget _already has_ focus, otherwise our work + * will be undone when it gains it shortly. + */ + gtk_widget_grab_focus(uc->text); + gtk_label_select_region(GTK_LABEL(uc->text), 0, 0); +#else + (void)textctrl; /* placate warning */ +#endif + } + gtk_window_set_modal(GTK_WINDOW(window), TRUE); if (parentwin) { set_transient_window_pos(parentwin, window); @@ -3220,7 +3241,9 @@ int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...) GTK_WINDOW(parentwin)); } else gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); + gtk_container_set_focus_child(GTK_CONTAINER(window), NULL); gtk_widget_show(window); + gtk_window_set_focus(GTK_WINDOW(window), NULL); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(window_destroy), NULL); @@ -3250,6 +3273,7 @@ int reallyclose(void *frontend) int ret = messagebox(GTK_WIDGET(get_window(frontend)), title, "Are you sure you want to close this session?", string_width("Most of the width of the above text"), + FALSE, "Yes", 'y', +1, 1, "No", 'n', -1, 0, NULL); @@ -3303,6 +3327,7 @@ int verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, ret = messagebox(GTK_WIDGET(get_window(frontend)), "PuTTY Security Alert", text, string_width(fingerprint), + TRUE, "Accept", 'a', 0, 2, "Connect Once", 'o', 0, 1, "Cancel", 'c', -1, 0, @@ -3336,6 +3361,7 @@ int askalg(void *frontend, const char *algtype, const char *algname, ret = messagebox(GTK_WIDGET(get_window(frontend)), "PuTTY Security Alert", text, string_width("Continue with connection?"), + FALSE, "Yes", 'y', 0, 1, "No", 'n', 0, 0, NULL); @@ -3359,14 +3385,14 @@ void fatal_message_box(void *window, char *msg) { messagebox(window, "PuTTY Fatal Error", msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), - "OK", 'o', 1, 1, NULL); + FALSE, "OK", 'o', 1, 1, NULL); } void nonfatal_message_box(void *window, char *msg) { messagebox(window, "PuTTY Error", msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), - "OK", 'o', 1, 1, NULL); + FALSE, "OK", 'o', 1, 1, NULL); } void fatalbox(char *p, ...) @@ -3439,7 +3465,7 @@ static void licence_clicked(GtkButton *button, gpointer data) messagebox(aboutbox, title, licence, string_width("LONGISH LINE OF TEXT SO THE LICENCE" " BOX ISN'T EXCESSIVELY TALL AND THIN"), - "OK", 'o', 1, 1, NULL); + TRUE, "OK", 'o', 1, 1, NULL); sfree(title); } @@ -3476,25 +3502,35 @@ void about_box(void *window) GTK_SIGNAL_FUNC(licence_clicked), NULL); gtk_widget_show(w); - w = gtk_label_new(appname); + { + char *label_text = dupprintf + ("%s\n\n%s\n\n%s", + appname, ver, + "Copyright 1997-2015 Simon Tatham. All rights reserved"); + w = gtk_label_new(label_text); + gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_CENTER); +#if GTK_CHECK_VERSION(2,0,0) + gtk_label_set_selectable(GTK_LABEL(w), TRUE); +#endif + sfree(label_text); + } gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), w, FALSE, FALSE, 0); - gtk_widget_show(w); - - w = gtk_label_new(ver); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), - w, FALSE, FALSE, 5); - gtk_widget_show(w); - - w = gtk_label_new("Copyright 1997-2015 Simon Tatham. All rights reserved"); - gtk_box_pack_start(GTK_BOX(GTK_DIALOG(aboutbox)->vbox), - w, FALSE, FALSE, 5); +#if GTK_CHECK_VERSION(2,0,0) + /* + * Same precautions against initial select-all as in messagebox(). + */ + gtk_widget_grab_focus(w); + gtk_label_select_region(GTK_LABEL(w), 0, 0); +#endif gtk_widget_show(w); set_transient_window_pos(GTK_WIDGET(window), aboutbox); gtk_window_set_transient_for(GTK_WINDOW(aboutbox), GTK_WINDOW(window)); + gtk_container_set_focus_child(GTK_CONTAINER(aboutbox), NULL); gtk_widget_show(aboutbox); + gtk_window_set_focus(GTK_WINDOW(aboutbox), NULL); } struct eventlog_stuff { @@ -3757,6 +3793,7 @@ int askappend(void *frontend, Filename *filename, mbret = messagebox(get_window(frontend), mbtitle, message, string_width("LINE OF TEXT SUITABLE FOR THE" " ASKAPPEND WIDTH"), + FALSE, "Overwrite", 'o', 1, 2, "Append", 'a', 0, 1, "Disable", 'd', -1, 0, diff --git a/unix/gtkwin.c b/unix/gtkwin.c index 12501e69..3d3789f8 100644 --- a/unix/gtkwin.c +++ b/unix/gtkwin.c @@ -3191,7 +3191,7 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data) errmsg); messagebox(inst->window, "Font setup error", msgboxtext, string_width("Could not change fonts in terminal window:"), - "OK", 'o', +1, 1, + FALSE, "OK", 'o', +1, 1, NULL); sfree(msgboxtext); sfree(errmsg); diff --git a/unix/unix.h b/unix/unix.h index e78800b5..1968a423 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -93,7 +93,8 @@ void showeventlog(void *estuff, void *parentwin); void logevent_dlg(void *estuff, const char *string); int reallyclose(void *frontend); #ifdef MAY_REFER_TO_GTK_IN_HEADERS -int messagebox(GtkWidget *parentwin, char *title, char *msg, int minwid, ...); +int messagebox(GtkWidget *parentwin, char *title, + char *msg, int minwid, int selectable, ...); int string_width(char *text); #endif From 4327fe71fe599e7a34d72541b792f5fcbb5614df Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:42:00 +0000 Subject: [PATCH 23/31] Use readonly edit controls in some Windows dialogs. This makes the About and Licence boxes copy-and-pasteable, similarly to what I've just done on Unix. (But unlike on the Unix side, here I haven't touched the host key prompt dialog, because that's a standard Windows MessageBox and not easy to mess around with. Plus, in any case, you can already hit ^C to copy the whole text out of a MessageBox. Same goes for the PGP fingerprints dialog.) As a side effect, several copies of the copyright notice and licence text have moved from .rc files into C source. I've updated CHECKLST.txt, but they won't stay there for long. (cherry picked from commit 2eb952ca31aa13d1f6f429305fbb6f43a9a28c56) Conflicts: windows/pageant.rc windows/puttygen.rc windows/win_res.rc2 (cherry-picker's notes: the conflict was just because several copies of the licence text were deleted, and they weren't quite the same between branches) --- CHECKLST.txt | 8 ++++---- windows/pageant.rc | 45 +++++++------------------------------------ windows/puttygen.rc | 45 +++++++------------------------------------ windows/win_res.h | 8 +++----- windows/win_res.rc2 | 47 ++++++++------------------------------------- windows/windlg.c | 41 +++++++++++++++++++++++++++++++++++++-- windows/winpgen.c | 39 ++++++++++++++++++++++++++++++++++++- windows/winpgnt.c | 39 ++++++++++++++++++++++++++++++++++++- 8 files changed, 144 insertions(+), 128 deletions(-) diff --git a/CHECKLST.txt b/CHECKLST.txt index dd1218a7..2cf15dc7 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -13,15 +13,15 @@ The LICENCE file in the main source distribution: - putty/LICENCE -The resource files: +The various About and Licence boxes: - - putty/windows/pageant.rc + - putty/windows/winpgnt.c + the copyright date appears twice, once in the About box and once in the Licence box. Don't forget to change both! - - putty/windows/puttygen.rc + - putty/windows/winpgen.c + the copyright date appears twice, once in the About box and once in the Licence box. Don't forget to change both! - - putty/windows/win_res.rc2 + - putty/windows/windlg.c + the copyright date appears twice, once in the About box and once in the Licence box. Don't forget to change both! - putty/windows/version.rc2 diff --git a/windows/pageant.rc b/windows/pageant.rc index 20ec509b..91432cc7 100644 --- a/windows/pageant.rc +++ b/windows/pageant.rc @@ -36,56 +36,25 @@ BEGIN END /* Accelerators used: cl */ -213 DIALOG DISCARDABLE 140, 40, 136, 70 +213 DIALOG DISCARDABLE 140, 40, 214, 74 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Pageant" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14 - PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 - CTEXT "Pageant", 102, 10, 6, 120, 8 - CTEXT "", 100, 10, 16, 120, 16 - CTEXT "\251 1997-2015 Simon Tatham. All rights reserved.", - 103, 10, 34, 120, 16 + DEFPUSHBUTTON "&Close", IDOK, 160, 56, 48, 14 + PUSHBUTTON "View &Licence", 101, 6, 56, 70, 14 + EDITTEXT 1000, 10, 6, 194, 48, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE END /* No accelerators used */ -214 DIALOG DISCARDABLE 50, 50, 226, 263 +214 DIALOG DISCARDABLE 50, 50, 326, 231 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 - - LTEXT "Copyright \251 1997-2015 Simon Tatham", 1000, 10, 10, 206, 8 - - LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 - LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 - LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 - LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 - - LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 - LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 - LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 - LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 - LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 - LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 - LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8 - - LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8 - LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8 - - LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8 - LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8 - LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8 - LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8 - LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8 - LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8 - LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8 - LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8 - LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8 - LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8 + DEFPUSHBUTTON "OK", IDOK, 148, 211, 44, 14 + EDITTEXT 1000, 10, 10, 306, 192, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END #include "version.rc2" diff --git a/windows/puttygen.rc b/windows/puttygen.rc index 9547e754..5c61ee7d 100644 --- a/windows/puttygen.rc +++ b/windows/puttygen.rc @@ -29,56 +29,25 @@ BEGIN END /* Accelerators used: cl */ -213 DIALOG DISCARDABLE 140, 40, 136, 70 +213 DIALOG DISCARDABLE 140, 40, 214, 74 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About PuTTYgen" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "&Close", IDOK, 82, 52, 48, 14 - PUSHBUTTON "View &Licence", 101, 6, 52, 70, 14 - CTEXT "PuTTYgen", 102, 10, 6, 120, 8 - CTEXT "", 100, 10, 16, 120, 16 - CTEXT "\251 1997-2015 Simon Tatham. All rights reserved.", - 103, 10, 34, 120, 16 + DEFPUSHBUTTON "&Close", IDOK, 160, 56, 48, 14 + PUSHBUTTON "View &Licence", 101, 6, 56, 70, 14 + EDITTEXT 1000, 10, 6, 194, 48, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE END /* No accelerators used */ -214 DIALOG DISCARDABLE 50, 50, 226, 263 +214 DIALOG DISCARDABLE 50, 50, 326, 231 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 - - LTEXT "Copyright \251 1997-2015 Simon Tatham", 1000, 10, 10, 206, 8 - - LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 - LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 - LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 - LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 - - LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 - LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 - LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 - LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 - LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 - LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 - LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8 - - LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8 - LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8 - - LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8 - LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8 - LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8 - LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8 - LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8 - LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8 - LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8 - LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8 - LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8 - LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8 + DEFPUSHBUTTON "OK", IDOK, 148, 211, 44, 14 + EDITTEXT 1000, 10, 10, 306, 192, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END #include "version.rc2" diff --git a/windows/win_res.h b/windows/win_res.h index 118f7495..4a5a48f6 100644 --- a/windows/win_res.h +++ b/windows/win_res.h @@ -18,11 +18,9 @@ #define IDN_COPY 1002 #define IDA_ICON 1001 -#define IDA_TEXT1 1002 -#define IDA_VERSION 1003 -#define IDA_TEXT2 1004 -#define IDA_LICENCE 1005 -#define IDA_WEB 1006 +#define IDA_TEXT 1002 +#define IDA_LICENCE 1003 +#define IDA_WEB 1004 #define IDC_TAB 1001 #define IDC_TABSTATIC1 1002 diff --git a/windows/win_res.rc2 b/windows/win_res.rc2 index b7dfd945..28e66320 100644 --- a/windows/win_res.rc2 +++ b/windows/win_res.rc2 @@ -16,18 +16,15 @@ IDI_MAINICON ICON "putty.ico" IDI_CFGICON ICON "puttycfg.ico" /* Accelerators used: clw */ -IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 214, 70 +IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 214, 74 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About PuTTY" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "&Close", IDOK, 160, 52, 48, 14 - PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 52, 70, 14 - PUSHBUTTON "Visit &Web Site", IDA_WEB, 84, 52, 70, 14 - CTEXT "PuTTY", IDA_TEXT1, 10, 6, 194, 8 - CTEXT "", IDA_VERSION, 10, 16, 194, 16 - CTEXT "\251 1997-2015 Simon Tatham. All rights reserved.", - IDA_TEXT2, 10, 34, 194, 16 + DEFPUSHBUTTON "&Close", IDOK, 160, 56, 48, 14 + PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 56, 70, 14 + PUSHBUTTON "Visit &Web Site", IDA_WEB, 84, 56, 70, 14 + EDITTEXT IDA_TEXT, 10, 6, 194, 48, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE END /* Accelerators used: aco */ @@ -51,42 +48,14 @@ BEGIN END /* No accelerators used */ -IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 226, 263 +IDD_LICENCEBOX DIALOG DISCARDABLE 50, 50, 326, 231 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN - DEFPUSHBUTTON "OK", IDOK, 98, 243, 44, 14 - - LTEXT "Copyright \251 1997-2015 Simon Tatham", 1000, 10, 10, 206, 8 - - LTEXT "Portions copyright Robert de Bath, Joris van Rantwijk, Delian", 1001, 10, 26, 206, 8 - LTEXT "Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas", 1002, 10, 34, 206, 8 - LTEXT "Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa,", 1003, 10, 42, 206, 8 - LTEXT "Markus Kuhn, Colin Watson, and CORE SDI S.A.", 1004, 10, 50, 206, 8 - - LTEXT "Permission is hereby granted, free of charge, to any person", 1005, 10, 66, 206, 8 - LTEXT "obtaining a copy of this software and associated documentation", 1006, 10, 74, 206, 8 - LTEXT "files (the ""Software""), to deal in the Software without restriction,", 1007, 10, 82, 206, 8 - LTEXT "including without limitation the rights to use, copy, modify, merge,", 1008, 10, 90, 206, 8 - LTEXT "publish, distribute, sublicense, and/or sell copies of the Software,", 1009, 10, 98, 206, 8 - LTEXT "and to permit persons to whom the Software is furnished to do so,", 1010, 10, 106, 206, 8 - LTEXT "subject to the following conditions:", 1011, 10, 114, 206, 8 - - LTEXT "The above copyright notice and this permission notice shall be", 1012, 10, 130, 206, 8 - LTEXT "included in all copies or substantial portions of the Software.", 1013, 10, 138, 206, 8 - - LTEXT "THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT", 1014, 10, 154, 206, 8 - LTEXT "WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,", 1015, 10, 162, 206, 8 - LTEXT "INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF", 1016, 10, 170, 206, 8 - LTEXT "MERCHANTABILITY, FITNESS FOR A PARTICULAR", 1017, 10, 178, 206, 8 - LTEXT "PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", 1018, 10, 186, 206, 8 - LTEXT "COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES", 1019, 10, 194, 206, 8 - LTEXT "OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,", 1020, 10, 202, 206, 8 - LTEXT "TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN", 1021, 10, 210, 206, 8 - LTEXT "CONNECTION WITH THE SOFTWARE OR THE USE OR", 1022, 10, 218, 206, 8 - LTEXT "OTHER DEALINGS IN THE SOFTWARE.", 1023, 10, 226, 206, 8 + DEFPUSHBUTTON "OK", IDOK, 148, 211, 44, 14 + EDITTEXT IDA_TEXT, 10, 10, 306, 192, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END #include "version.rc2" diff --git a/windows/windlg.c b/windows/windlg.c index 716d046b..0e1d29a0 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -170,6 +170,37 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg, char *str = dupprintf("%s Licence", appname); SetWindowText(hwnd, str); sfree(str); + + SetDlgItemText(hwnd, IDA_TEXT, + "Copyright 1997-2015 Simon Tatham.\r\n\r\n" + + "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, and CORE SDI S.A.\r\n\r\n" + + "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:\r\n\r\n" + + "The above copyright notice and this permission notice shall be " + "included in all copies or substantial portions of the Software.\r\n\r\n" + + "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." +); } return 1; case WM_COMMAND: @@ -197,8 +228,14 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, str = dupprintf("About %s", appname); SetWindowText(hwnd, str); sfree(str); - SetDlgItemText(hwnd, IDA_TEXT1, appname); - SetDlgItemText(hwnd, IDA_VERSION, ver); + { + char *text = dupprintf + ("%s\r\n\r\n%s\r\n\r\n%s", + appname, ver, + "\251 1997-2015 Simon Tatham. All rights reserved."); + SetDlgItemText(hwnd, IDA_TEXT, text); + sfree(text); + } return 1; case WM_COMMAND: switch (LOWORD(wParam)) { diff --git a/windows/winpgen.c b/windows/winpgen.c index 33d76c64..d0d095da 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -253,6 +253,36 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg, rd.right - rd.left, rd.bottom - rd.top, TRUE); } + SetDlgItemText(hwnd, 1000, + "Copyright 1997-2015 Simon Tatham.\r\n\r\n" + + "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, and CORE SDI S.A.\r\n\r\n" + + "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:\r\n\r\n" + + "The above copyright notice and this permission notice shall be " + "included in all copies or substantial portions of the Software.\r\n\r\n" + + "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." +); return 1; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -292,7 +322,14 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, rd.right - rd.left, rd.bottom - rd.top, TRUE); } - SetDlgItemText(hwnd, 100, ver); + { + char *text = dupprintf + ("Pageant\r\n\r\n%s\r\n\r\n%s", + ver, + "\251 1997-2015 Simon Tatham. All rights reserved."); + SetDlgItemText(hwnd, 1000, text); + sfree(text); + } return 1; case WM_COMMAND: switch (LOWORD(wParam)) { diff --git a/windows/winpgnt.c b/windows/winpgnt.c index 22b60788..facbfa57 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -183,6 +183,36 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg, { switch (msg) { case WM_INITDIALOG: + SetDlgItemText(hwnd, 1000, + "Copyright 1997-2015 Simon Tatham.\r\n\r\n" + + "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, and CORE SDI S.A.\r\n\r\n" + + "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:\r\n\r\n" + + "The above copyright notice and this permission notice shall be " + "included in all copies or substantial portions of the Software.\r\n\r\n" + + "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." +); return 1; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -207,7 +237,14 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, { switch (msg) { case WM_INITDIALOG: - SetDlgItemText(hwnd, 100, ver); + { + char *text = dupprintf + ("Pageant\r\n\r\n%s\r\n\r\n%s", + ver, + "\251 1997-2015 Simon Tatham. All rights reserved."); + SetDlgItemText(hwnd, 1000, text); + sfree(text); + } return 1; case WM_COMMAND: switch (LOWORD(wParam)) { From 442627408fc562f99c90a306ef66a652fca5b55b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:43:54 +0000 Subject: [PATCH 24/31] Stop copying the licence text into C source code. Now all the uses of the licence text or the short copyright notice get it from a new header "licence.h", which in turn is built by a Perl script licence.pl invoked by mkfiles.pl, using LICENCE itself as the source. Hence, I can completely remove a whole section from the list of licence locations in CHECKLST.txt :-) (cherry picked from commit 9ddd071ec28050b3be572f25f3ae7d44e46e4039) Conflicts: unix/gtkdlg.c windows/winpgnt.c (cherry-picker's notes: one conflict was just changed context, the other was deleting a copy of the licence that wasn't quite the same between branches) --- .gitignore | 1 + CHECKLST.txt | 19 +------------- licence.pl | 64 +++++++++++++++++++++++++++++++++++++++++++++ mkfiles.pl | 5 ++-- unix/gtkdlg.c | 35 +++---------------------- windows/version.rc2 | 3 ++- windows/windlg.c | 35 +++---------------------- windows/winpgen.c | 34 +++--------------------- windows/winpgnt.c | 34 +++--------------------- 9 files changed, 83 insertions(+), 147 deletions(-) create mode 100644 licence.pl diff --git a/.gitignore b/.gitignore index bea01d86..05d3b2c3 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ /missing /uxconfig.in /uxconfig.h +/licence.h /*.a /charset/sbcsdat.c /contrib/cygtermd/cygtermd.exe diff --git a/CHECKLST.txt b/CHECKLST.txt index 2cf15dc7..034f5708 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -4,7 +4,7 @@ Checklists for PuTTY administrative procedures Locations of the licence ------------------------ -The PuTTY copyright notice and licence are stored in quite a few +The PuTTY copyright notice and licence are stored in multiple places. At the start of a new year, the copyright year needs updating in all of them; and when someone sends a massive patch, their name needs adding in all of them too. @@ -13,23 +13,6 @@ The LICENCE file in the main source distribution: - putty/LICENCE -The various About and Licence boxes: - - - putty/windows/winpgnt.c - + the copyright date appears twice, once in the About box and - once in the Licence box. Don't forget to change both! - - putty/windows/winpgen.c - + the copyright date appears twice, once in the About box and - once in the Licence box. Don't forget to change both! - - putty/windows/windlg.c - + the copyright date appears twice, once in the About box and - once in the Licence box. Don't forget to change both! - - putty/windows/version.rc2 - + the copyright date appears once only. - - putty/unix/gtkdlg.c - + the copyright date appears twice, once in the About box and - once in the Licence box. Don't forget to change both! - The documentation (both the preamble blurb and the licence appendix): - putty/doc/blurb.but diff --git a/licence.pl b/licence.pl new file mode 100644 index 00000000..12d38a55 --- /dev/null +++ b/licence.pl @@ -0,0 +1,64 @@ +#!/usr/bin/env perl -w + +# This script generates licence.h (containing the PuTTY licence in the +# form of macros expanding to C string literals) from the LICENCE +# master file. + +use File::Basename; + +$infile = "LICENCE"; +$outfile = "licence.h"; +open my $in, $infile or die "$infile: open: $!\n"; +open my $out, ">", $outfile or die "$outfile: open: $!\n"; +select $out; + +print "/*\n"; +print " * $outfile - macro definitions for the PuTTY licence.\n"; +print " *\n"; +print " * Generated by @{[basename __FILE__]} from $infile.\n"; +print " * You should edit those files rather than editing this one.\n"; +print " */\n"; +print "\n"; + +my @lines = (); +while (<$in>) { + chomp; + push @lines, $_; +} +close $in; + +# Format into paragraphs. +my @paras = (); +my $para = undef; +for my $line (@lines) { + if ($line eq "") { + $para = undef; + } elsif (!defined $para) { + push @paras, $line; + $para = \$paras[$#paras]; + } else { + $$para .= " " . $line; + } +} + +print "#define LICENCE_TEXT(parsep) \\\n"; +for my $i (0..$#paras) { + my $lit = &stringlit($paras[$i]); + print " parsep \\\n" if $i > 0; + print " \"$lit\""; + print " \\" if $i < $#paras; + print "\n"; +} +print "\n"; + +die "bad format of first paragraph\n" + unless $paras[0] =~ m!copyright ([^\.]*)\.!i; + +printf "#define SHORT_COPYRIGHT_DETAILS \"%s\"\n", &stringlit($1); + +sub stringlit { + my ($lit) = @_; + $lit =~ s!\\!\\\\!g; + $lit =~ s!"!\\"!g; + return $lit; +} diff --git a/mkfiles.pl b/mkfiles.pl index f4365198..342db4d0 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -45,9 +45,10 @@ open IN, "Recipe" or do { }; # HACK: One of the source files in `charset' is auto-generated by -# sbcsgen.pl. We need to generate that _now_, before attempting -# dependency analysis. +# sbcsgen.pl, and licence.h is likewise generated by licence.pl. We +# need to generate those _now_, before attempting dependency analysis. eval 'chdir "charset"; require "sbcsgen.pl"; chdir ".."; select STDOUT;'; +eval 'require "licence.pl"; select STDOUT;'; @srcdirs = ("./"); diff --git a/unix/gtkdlg.c b/unix/gtkdlg.c index 9139f6fc..aa97e002 100644 --- a/unix/gtkdlg.c +++ b/unix/gtkdlg.c @@ -23,6 +23,7 @@ #include "storage.h" #include "dialog.h" #include "tree234.h" +#include "licence.h" struct Shortcut { GtkWidget *widget; @@ -3430,39 +3431,9 @@ static void licence_clicked(GtkButton *button, gpointer data) { char *title; - char *licence = - "Copyright 1997-2015 Simon Tatham.\n\n" - - "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, and CORE SDI S.A.\n\n" - - "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:\n\n" - - "The above copyright notice and this permission notice shall be " - "included in all copies or substantial portions of the Software.\n\n" - - "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."; - title = dupcat(appname, " Licence", NULL); assert(aboutbox != NULL); - messagebox(aboutbox, title, licence, + messagebox(aboutbox, title, LICENCE_TEXT("\n\n"), string_width("LONGISH LINE OF TEXT SO THE LICENCE" " BOX ISN'T EXCESSIVELY TALL AND THIN"), TRUE, "OK", 'o', 1, 1, NULL); @@ -3506,7 +3477,7 @@ void about_box(void *window) char *label_text = dupprintf ("%s\n\n%s\n\n%s", appname, ver, - "Copyright 1997-2015 Simon Tatham. All rights reserved"); + "Copyright " SHORT_COPYRIGHT_DETAILS ". All rights reserved"); w = gtk_label_new(label_text); gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_CENTER); #if GTK_CHECK_VERSION(2,0,0) diff --git a/windows/version.rc2 b/windows/version.rc2 index f3c002df..500f9002 100644 --- a/windows/version.rc2 +++ b/windows/version.rc2 @@ -7,6 +7,7 @@ */ #include "version.h" +#include "licence.h" /* * The actual VERSIONINFO resource. @@ -44,7 +45,7 @@ BEGIN VALUE "OriginalFilename", APPNAME VALUE "FileVersion", TEXTVER VALUE "ProductVersion", TEXTVER - VALUE "LegalCopyright", "Copyright \251 1997-2015 Simon Tatham." + VALUE "LegalCopyright", "Copyright \251 " SHORT_COPYRIGHT_DETAILS "." #if (!defined SNAPSHOT) && (!defined RELEASE) && (!defined PRERELEASE) /* Only if VS_FF_PRIVATEBUILD. */ VALUE "PrivateBuild", TEXTVER /* NBI */ diff --git a/windows/windlg.c b/windows/windlg.c index 0e1d29a0..61ba9ad1 100644 --- a/windows/windlg.c +++ b/windows/windlg.c @@ -14,6 +14,7 @@ #include "win_res.h" #include "storage.h" #include "dialog.h" +#include "licence.h" #include #include @@ -170,37 +171,7 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg, char *str = dupprintf("%s Licence", appname); SetWindowText(hwnd, str); sfree(str); - - SetDlgItemText(hwnd, IDA_TEXT, - "Copyright 1997-2015 Simon Tatham.\r\n\r\n" - - "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, and CORE SDI S.A.\r\n\r\n" - - "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:\r\n\r\n" - - "The above copyright notice and this permission notice shall be " - "included in all copies or substantial portions of the Software.\r\n\r\n" - - "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." -); + SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n")); } return 1; case WM_COMMAND: @@ -232,7 +203,7 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, char *text = dupprintf ("%s\r\n\r\n%s\r\n\r\n%s", appname, ver, - "\251 1997-2015 Simon Tatham. All rights reserved."); + "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); SetDlgItemText(hwnd, IDA_TEXT, text); sfree(text); } diff --git a/windows/winpgen.c b/windows/winpgen.c index d0d095da..9497ed60 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -11,6 +11,7 @@ #include "putty.h" #include "ssh.h" +#include "licence.h" #include @@ -253,36 +254,7 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg, rd.right - rd.left, rd.bottom - rd.top, TRUE); } - SetDlgItemText(hwnd, 1000, - "Copyright 1997-2015 Simon Tatham.\r\n\r\n" - - "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, and CORE SDI S.A.\r\n\r\n" - - "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:\r\n\r\n" - - "The above copyright notice and this permission notice shall be " - "included in all copies or substantial portions of the Software.\r\n\r\n" - - "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." -); + SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n")); return 1; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -326,7 +298,7 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, char *text = dupprintf ("Pageant\r\n\r\n%s\r\n\r\n%s", ver, - "\251 1997-2015 Simon Tatham. All rights reserved."); + "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); SetDlgItemText(hwnd, 1000, text); sfree(text); } diff --git a/windows/winpgnt.c b/windows/winpgnt.c index facbfa57..f4194a68 100644 --- a/windows/winpgnt.c +++ b/windows/winpgnt.c @@ -15,6 +15,7 @@ #include "misc.h" #include "tree234.h" #include "winsecur.h" +#include "licence.h" #include @@ -183,36 +184,7 @@ static int CALLBACK LicenceProc(HWND hwnd, UINT msg, { switch (msg) { case WM_INITDIALOG: - SetDlgItemText(hwnd, 1000, - "Copyright 1997-2015 Simon Tatham.\r\n\r\n" - - "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, and CORE SDI S.A.\r\n\r\n" - - "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:\r\n\r\n" - - "The above copyright notice and this permission notice shall be " - "included in all copies or substantial portions of the Software.\r\n\r\n" - - "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." -); + SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n")); return 1; case WM_COMMAND: switch (LOWORD(wParam)) { @@ -241,7 +213,7 @@ static int CALLBACK AboutProc(HWND hwnd, UINT msg, char *text = dupprintf ("Pageant\r\n\r\n%s\r\n\r\n%s", ver, - "\251 1997-2015 Simon Tatham. All rights reserved."); + "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); SetDlgItemText(hwnd, 1000, text); sfree(text); } From 329cd919593feed1c000b6f4ee662bd1ff33fd6b Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Thu, 25 Feb 2016 20:45:27 +0000 Subject: [PATCH 25/31] Autogenerate licence text in doc subdir from LICENCE. Now we have licence.pl, it seems to me to make very good sense to have it generate the Halibut form(s) of the licence and copyright year as well as the source-code forms. As a result, I believe _no_ copies of the licence text or copyright date exist any more except for the master one in LICENCE - so I can completely remove the checklist section about all the places to update it, because there's only one. Hooray! (cherry picked from commit 774d37a0dc79441d6add265a0d360af3e53f8460) Conflicts: doc/licence.but (cherry-picker's note: the conflict was just because the deleted file didn't have identical contents) --- .gitignore | 1 + CHECKLST.txt | 17 ---------- doc/Makefile | 4 +-- doc/blurb.but | 2 +- doc/licence.but | 27 ---------------- licence.pl | 85 +++++++++++++++++++++++++++++++++++++++---------- 6 files changed, 72 insertions(+), 64 deletions(-) delete mode 100644 doc/licence.but diff --git a/.gitignore b/.gitignore index 05d3b2c3..f4b14901 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ /doc/*.hhp /doc/*.hhc /doc/*.hhk +/doc/licence.but /icons/*.png /icons/*.ico /icons/*.xpm diff --git a/CHECKLST.txt b/CHECKLST.txt index 034f5708..35de3368 100644 --- a/CHECKLST.txt +++ b/CHECKLST.txt @@ -1,23 +1,6 @@ Checklists for PuTTY administrative procedures ============================================== -Locations of the licence ------------------------- - -The PuTTY copyright notice and licence are stored in multiple -places. At the start of a new year, the copyright year needs -updating in all of them; and when someone sends a massive patch, -their name needs adding in all of them too. - -The LICENCE file in the main source distribution: - - - putty/LICENCE - -The documentation (both the preamble blurb and the licence appendix): - - - putty/doc/blurb.but - - putty/doc/licence.but - Preparing to make a release --------------------------- diff --git a/doc/Makefile b/doc/Makefile index 96a19eb1..37d8b0c1 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -34,8 +34,8 @@ else VERSIONIDS=vids endif -CHAPTERS := $(SITE) blurb intro gs using config pscp psftp plink pubkey -CHAPTERS += pageant errors faq feedback licence udp pgpkeys sshnames +CHAPTERS := $(SITE) copy blurb intro gs using config pscp psftp plink +CHAPTERS += pubkey pageant errors faq feedback licence udp pgpkeys sshnames CHAPTERS += index $(VERSIONIDS) INPUTS = $(patsubst %,%.but,$(CHAPTERS)) diff --git a/doc/blurb.but b/doc/blurb.but index d04c4ddb..4e3ccf71 100644 --- a/doc/blurb.but +++ b/doc/blurb.but @@ -31,6 +31,6 @@ features not described here; and the \i\cw{pterm} and command-line Unix-specific documentation that currently exists is the \I{man pages for PuTTY tools}man pages. -\copyright This manual is copyright 2001-2015 Simon Tatham. All +\copyright This manual is copyright \shortcopyrightdetails. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. diff --git a/doc/licence.but b/doc/licence.but deleted file mode 100644 index 0856d863..00000000 --- a/doc/licence.but +++ /dev/null @@ -1,27 +0,0 @@ -\A{licence} PuTTY \ii{Licence} - -PuTTY is \i{copyright} 1997-2015 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, 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: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED \q{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. diff --git a/licence.pl b/licence.pl index 12d38a55..e1d3a51f 100644 --- a/licence.pl +++ b/licence.pl @@ -2,24 +2,14 @@ # This script generates licence.h (containing the PuTTY licence in the # form of macros expanding to C string literals) from the LICENCE -# master file. +# master file. It also regenerates the licence-related Halibut input +# files. use File::Basename; +# Read the input file. $infile = "LICENCE"; -$outfile = "licence.h"; open my $in, $infile or die "$infile: open: $!\n"; -open my $out, ">", $outfile or die "$outfile: open: $!\n"; -select $out; - -print "/*\n"; -print " * $outfile - macro definitions for the PuTTY licence.\n"; -print " *\n"; -print " * Generated by @{[basename __FILE__]} from $infile.\n"; -print " * You should edit those files rather than editing this one.\n"; -print " */\n"; -print "\n"; - my @lines = (); while (<$in>) { chomp; @@ -41,6 +31,25 @@ for my $line (@lines) { } } +# Get the copyright years and short form of copyright holder. +die "bad format of first paragraph\n" + unless $paras[0] =~ m!copyright ([^\.]*)\.!i; +$shortdetails = $1; + +# Write out licence.h. + +$outfile = "licence.h"; +open my $out, ">", $outfile or die "$outfile: open: $!\n"; +select $out; + +print "/*\n"; +print " * $outfile - macro definitions for the PuTTY licence.\n"; +print " *\n"; +print " * Generated by @{[basename __FILE__]} from $infile.\n"; +print " * You should edit those files rather than editing this one.\n"; +print " */\n"; +print "\n"; + print "#define LICENCE_TEXT(parsep) \\\n"; for my $i (0..$#paras) { my $lit = &stringlit($paras[$i]); @@ -51,10 +60,7 @@ for my $i (0..$#paras) { } print "\n"; -die "bad format of first paragraph\n" - unless $paras[0] =~ m!copyright ([^\.]*)\.!i; - -printf "#define SHORT_COPYRIGHT_DETAILS \"%s\"\n", &stringlit($1); +printf "#define SHORT_COPYRIGHT_DETAILS \"%s\"\n", &stringlit($shortdetails); sub stringlit { my ($lit) = @_; @@ -62,3 +68,48 @@ sub stringlit { $lit =~ s!"!\\"!g; return $lit; } + +close $out; + +# Write out doc/licence.but. + +$outfile = "doc/licence.but"; +open $out, ">", $outfile or die "$outfile: open: $!\n"; +select $out; + +print "\\# Generated by @{[basename __FILE__]} from $infile.\n"; +print "\\# You should edit those files rather than editing this one.\n\n"; + +print "\\A{licence} PuTTY \\ii{Licence}\n\n"; + +for my $i (0..$#paras) { + my $para = &halibutescape($paras[$i]); + if ($i == 0) { + $para =~ s!copyright!\\i{copyright}!; # index term in paragraph 1 + } + print "$para\n\n"; +} + +close $out; + +# And write out doc/copy.but, which defines a macro used in the manual +# preamble blurb. + +$outfile = "doc/copy.but"; +open $out, ">", $outfile or die "$outfile: open: $!\n"; +select $out; + +print "\\# Generated by @{[basename __FILE__]} from $infile.\n"; +print "\\# You should edit those files rather than editing this one.\n\n"; + +printf "\\define{shortcopyrightdetails} %s\n\n", + &halibutescape($shortdetails); + +close $out; + +sub halibutescape { + my ($text) = @_; + $text =~ s![\\{}]!\\$&!g; # Halibut escaping + $text =~ s!"([^"]*)"!\\q{$1}!g; # convert quoted strings to \q{} + return $text; +} From 9af11a601a6377a2d9c7e3caca952c4ce3c82e05 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 22 Dec 2015 13:56:07 +0000 Subject: [PATCH 26/31] Add the new copy.but to .gitignore. Arrgh, _another_ one I only remember seconds too late! (cherry picked from commit 51465fac73742602003db2c445109a3526fad16e) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f4b14901..3ae8d1f3 100644 --- a/.gitignore +++ b/.gitignore @@ -80,6 +80,7 @@ /doc/*.hhc /doc/*.hhk /doc/licence.but +/doc/copy.but /icons/*.png /icons/*.ico /icons/*.xpm From ab147df1757fae4a300943b7bb1587ae14bb23d2 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 26 Jan 2016 18:36:26 +0000 Subject: [PATCH 27/31] Remove some unused variables. Thanks to @ch3root again for this patch. (cherry picked from commit 70f641f84527fcb5a2ccbff7c8e238003ff2d2f3) --- contrib/cygtermd/main.c | 2 +- contrib/cygtermd/telnet.c | 2 -- sshcrc.c | 1 - windows/winsecur.c | 4 ---- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/contrib/cygtermd/main.c b/contrib/cygtermd/main.c index acf35dd9..84e6c75e 100644 --- a/contrib/cygtermd/main.c +++ b/contrib/cygtermd/main.c @@ -111,7 +111,7 @@ void sig_readdata(sel_rfd *rfd, void *data, size_t len) while (len > 0) { if (*p == 'C') { int status; - pid_t pid = waitpid(-1, &status, WNOHANG); + waitpid(-1, &status, WNOHANG); if (WIFEXITED(status) || WIFSIGNALED(status)) exit(0); /* child process vanished */ } diff --git a/contrib/cygtermd/telnet.c b/contrib/cygtermd/telnet.c index 7aec0aab..08487374 100644 --- a/contrib/cygtermd/telnet.c +++ b/contrib/cygtermd/telnet.c @@ -325,9 +325,7 @@ static void proc_rec_opt(Telnet telnet, int cmd, int option) static void process_subneg(Telnet telnet) { - unsigned char b[2048], *p, *q; int var, value, n; - char *e; switch (telnet->sb_opt) { case TELOPT_OLD_ENVIRON: diff --git a/sshcrc.c b/sshcrc.c index ed20395b..782d04bb 100644 --- a/sshcrc.c +++ b/sshcrc.c @@ -198,7 +198,6 @@ static const unsigned long crc32_table[256] = { #ifdef GENPROGRAM int main(void) { - unsigned long crcword; int i; crc32_init(); diff --git a/windows/winsecur.c b/windows/winsecur.c index 8d0b223a..91ce7e92 100644 --- a/windows/winsecur.c +++ b/windows/winsecur.c @@ -140,8 +140,6 @@ int make_private_security_descriptor(DWORD permissions, PACL *acl, char **error) { - SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; - SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; EXPLICIT_ACCESS ea[3]; int acl_err; int ret = FALSE; @@ -225,8 +223,6 @@ int make_private_security_descriptor(DWORD permissions, int setprocessacl(char *error) { - SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; - SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; EXPLICIT_ACCESS ea[2]; int acl_err; int ret=FALSE; From 4dbf2ea85ce0239d26f318cd9553a40f2444f5d2 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Tue, 26 Jan 2016 22:09:01 +0000 Subject: [PATCH 28/31] Fix a 64-bit-cleanness error in sshcrc's generator. Not that anyone actually needs to use that conditioned-out main(), since it only generates the table already present in the same source file, but since @ch3root's unused-variable patch touched it I tried compiling it and noticed in passing that I'd also got the wrong printf format directive for an unsigned long. (cherry picked from commit 9351a5bfe4b1630227581d77f1aff4ca729ab8c1) --- sshcrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sshcrc.c b/sshcrc.c index 782d04bb..a993e98b 100644 --- a/sshcrc.c +++ b/sshcrc.c @@ -202,7 +202,7 @@ int main(void) crc32_init(); for (i = 0; i < 256; i++) { - printf("%s0x%08XL%s", + printf("%s0x%08lXL%s", (i % 4 == 0 ? " " : " "), crc32_table[i], (i % 4 == 3 ? (i == 255 ? "\n" : ",\n") : ",")); From e71ec6bf9fc5094171faecb70f597cf60653b488 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Thu, 28 Jan 2016 21:19:41 +0000 Subject: [PATCH 29/31] Avoid -Wmisleading-indentation warnings with GCC 6. GCC 6 warns about potentially misleading indentation, such as: if (condition) stmt1; stmt2; Chaining multiple ifs on a single line runs into this warning, even if it's probably not actually misleading to a human eye, so just add a couple of newlines to pacify the compiler. (cherry picked from commit d700c33422926dda1b4af90bf1fcd262b03cfca8) --- cproxy.c | 3 ++- proxy.c | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cproxy.c b/cproxy.c index 934ce3dc..8c8a50cb 100644 --- a/cproxy.c +++ b/cproxy.c @@ -173,7 +173,8 @@ int proxy_socks5_selectchap(Proxy_Socket p) chapbuf[5] = '\x02'; /* Second attribute - username */ ulen = strlen(username); - if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; + if (ulen > 255) ulen = 255; + if (ulen < 1) ulen = 1; chapbuf[6] = ulen; memcpy(chapbuf+7, username, ulen); diff --git a/proxy.c b/proxy.c index 51754dc3..cf45e1b7 100644 --- a/proxy.c +++ b/proxy.c @@ -1188,9 +1188,11 @@ int proxy_socks5_negotiate (Proxy_Socket p, int change) char userpwbuf[255 + 255 + 3]; int ulen, plen; ulen = strlen(username); - if (ulen > 255) ulen = 255; if (ulen < 1) ulen = 1; + if (ulen > 255) ulen = 255; + if (ulen < 1) ulen = 1; plen = strlen(password); - if (plen > 255) plen = 255; if (plen < 1) plen = 1; + if (plen > 255) plen = 255; + if (plen < 1) plen = 1; userpwbuf[0] = 1; /* version number of subnegotiation */ userpwbuf[1] = ulen; memcpy(userpwbuf+2, username, ulen); From eb74369b24584e12fdd251cbbf2af17f4c543550 Mon Sep 17 00:00:00 2001 From: Colin Watson Date: Thu, 28 Jan 2016 21:22:07 +0000 Subject: [PATCH 30/31] Fix strict-aliasing warnings in sk_tcp_peer_info. GCC 6 emits strict-aliasing warnings here, so use the existing sockaddr_union arrangements to avoid those. As a prerequisite for being able to express sk_tcp_peer_info in terms of sockaddr_union, I fixed up the union elements to be a bit less odd in the NO_IPV6 case. (cherry picked from commit c026b48c537250ac03573845ff9da6fd9f45776d) --- unix/uxnet.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/unix/uxnet.c b/unix/uxnet.c index 5058d8ab..b15031f4 100644 --- a/unix/uxnet.c +++ b/unix/uxnet.c @@ -37,14 +37,12 @@ * Access to sockaddr types without breaking C strict aliasing rules. */ union sockaddr_union { -#ifdef NO_IPV6 - struct sockaddr_in storage; -#else struct sockaddr_storage storage; - struct sockaddr_in6 sin6; -#endif struct sockaddr sa; struct sockaddr_in sin; +#ifndef NO_IPV6 + struct sockaddr_in6 sin6; +#endif struct sockaddr_un su; }; @@ -1426,26 +1424,27 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen) static char *sk_tcp_peer_info(Socket sock) { Actual_Socket s = (Actual_Socket) sock; - struct sockaddr_storage addr; + union sockaddr_union addr; socklen_t addrlen = sizeof(addr); +#ifndef NO_IPV6 char buf[INET6_ADDRSTRLEN]; +#endif - if (getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0) + if (getpeername(s->s, &addr.sa, &addrlen) < 0) return NULL; - if (addr.ss_family == AF_INET) { + if (addr.storage.ss_family == AF_INET) { return dupprintf ("%s:%d", - inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr), - (int)ntohs(((struct sockaddr_in *)&addr)->sin_port)); + inet_ntoa(addr.sin.sin_addr), + (int)ntohs(addr.sin.sin_port)); #ifndef NO_IPV6 - } else if (addr.ss_family == AF_INET6) { + } else if (addr.storage.ss_family == AF_INET6) { return dupprintf ("[%s]:%d", - inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr, - buf, sizeof(buf)), - (int)ntohs(((struct sockaddr_in6 *)&addr)->sin6_port)); + inet_ntop(AF_INET6, &addr.sin6.sin6_addr, buf, sizeof(buf)), + (int)ntohs(addr.sin6.sin6_port)); #endif - } else if (addr.ss_family == AF_UNIX) { + } else if (addr.storage.ss_family == AF_UNIX) { /* * For Unix sockets, the source address is unlikely to be * helpful. Instead, we try SO_PEERCRED and try to get the From 51586b6f26e81673b85b1840411983865359c156 Mon Sep 17 00:00:00 2001 From: Jacob Nevins Date: Sat, 27 Feb 2016 10:38:48 +0000 Subject: [PATCH 31/31] It's a new year. (cherry picked from commit cfbe604d068ea8761eeb5da0138e4bef50dd077f) --- LICENCE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENCE b/LICENCE index eab8817e..e0bf1f21 100644 --- a/LICENCE +++ b/LICENCE @@ -1,4 +1,4 @@ -PuTTY is copyright 1997-2015 Simon Tatham. +PuTTY is copyright 1997-2016 Simon Tatham. Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry,