mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
New library-style 'utils' subdirectories.
Now that the new CMake build system is encouraging us to lay out the code like a set of libraries, it seems like a good idea to make them look more _like_ libraries, by putting things into separate modules as far as possible. This fixes several previous annoyances in which you had to link against some object in order to get a function you needed, but that object also contained other functions you didn't need which included link-time symbol references you didn't want to have to deal with. The usual offender was subsidiary supporting programs including misc.c for some innocuous function and then finding they had to deal with the requirements of buildinfo(). This big reorganisation introduces three new subdirectories called 'utils', one at the top level and one in each platform subdir. In each case, the directory contains basically the same files that were previously placed in the 'utils' build-time library, except that the ones that were extremely miscellaneous (misc.c, utils.c, uxmisc.c, winmisc.c, winmiscs.c, winutils.c) have been split up into much smaller pieces.
This commit is contained in:
parent
0881c9d2b8
commit
3396c97da9
@ -4,8 +4,54 @@ project(putty LANGUAGES C)
|
|||||||
include(cmake/setup.cmake)
|
include(cmake/setup.cmake)
|
||||||
|
|
||||||
add_library(utils STATIC
|
add_library(utils STATIC
|
||||||
memory.c marshal.c utils.c conf.c sshutils.c tree234.c version.c
|
utils/base64_decode_atom.c
|
||||||
wildcard.c wcwidth.c misc.c miscucs.c stripctrl.c sessprep.c
|
utils/base64_encode_atom.c
|
||||||
|
utils/bufchain.c
|
||||||
|
utils/buildinfo.c
|
||||||
|
utils/burnstr.c
|
||||||
|
utils/chomp.c
|
||||||
|
utils/conf.c
|
||||||
|
utils/conf_dest.c
|
||||||
|
utils/conf_launchable.c
|
||||||
|
utils/ctrlparse.c
|
||||||
|
utils/debug.c
|
||||||
|
utils/dupcat.c
|
||||||
|
utils/dupprintf.c
|
||||||
|
utils/dupstr.c
|
||||||
|
utils/encode_utf8.c
|
||||||
|
utils/fgetline.c
|
||||||
|
utils/host_strchr.c
|
||||||
|
utils/host_strchr_internal.c
|
||||||
|
utils/host_strcspn.c
|
||||||
|
utils/host_strduptrim.c
|
||||||
|
utils/host_strrchr.c
|
||||||
|
utils/marshal.c
|
||||||
|
utils/memory.c
|
||||||
|
utils/memxor.c
|
||||||
|
utils/miscucs.c
|
||||||
|
utils/null_lp.c
|
||||||
|
utils/nullseat.c
|
||||||
|
utils/nullstrcmp.c
|
||||||
|
utils/out_of_memory.c
|
||||||
|
utils/parse_blocksize.c
|
||||||
|
utils/prompts.c
|
||||||
|
utils/ptrlen.c
|
||||||
|
utils/read_file_into.c
|
||||||
|
utils/seat_connection_fatal.c
|
||||||
|
utils/sessprep.c
|
||||||
|
utils/sk_free_peer_info.c
|
||||||
|
utils/smemeq.c
|
||||||
|
utils/ssh2_pick_fingerprint.c
|
||||||
|
utils/sshutils.c
|
||||||
|
utils/strbuf.c
|
||||||
|
utils/string_length_for_printf.c
|
||||||
|
utils/stripctrl.c
|
||||||
|
utils/tree234.c
|
||||||
|
utils/validate_manual_hostkey.c
|
||||||
|
utils/version.c
|
||||||
|
utils/wcwidth.c
|
||||||
|
utils/wildcard.c
|
||||||
|
utils/write_c_string_literal.c
|
||||||
${GENERATED_COMMIT_C})
|
${GENERATED_COMMIT_C})
|
||||||
|
|
||||||
add_library(logging OBJECT
|
add_library(logging OBJECT
|
||||||
|
419
misc.c
419
misc.c
@ -1,419 +0,0 @@
|
|||||||
/*
|
|
||||||
* Platform-independent routines shared between all PuTTY programs.
|
|
||||||
*
|
|
||||||
* This file contains functions that use the kind of infrastructure
|
|
||||||
* like conf.c that tends to only live in the main applications, or
|
|
||||||
* that do things that only something like a main PuTTY application
|
|
||||||
* would need. So standalone test programs should generally be able to
|
|
||||||
* avoid linking against it.
|
|
||||||
*
|
|
||||||
* More standalone functions that depend on nothing but the C library
|
|
||||||
* live in utils.c.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "defs.h"
|
|
||||||
#include "putty.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#define BASE64_CHARS_NOEQ \
|
|
||||||
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
|
|
||||||
#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
|
|
||||||
|
|
||||||
void seat_connection_fatal(Seat *seat, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
char *msg;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
msg = dupvprintf(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
seat->vt->connection_fatal(seat, msg);
|
|
||||||
sfree(msg); /* if we return */
|
|
||||||
}
|
|
||||||
|
|
||||||
prompts_t *new_prompts(void)
|
|
||||||
{
|
|
||||||
prompts_t *p = snew(prompts_t);
|
|
||||||
p->prompts = NULL;
|
|
||||||
p->n_prompts = p->prompts_size = 0;
|
|
||||||
p->data = NULL;
|
|
||||||
p->to_server = true; /* to be on the safe side */
|
|
||||||
p->name = p->instruction = NULL;
|
|
||||||
p->name_reqd = p->instr_reqd = false;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
void add_prompt(prompts_t *p, char *promptstr, bool echo)
|
|
||||||
{
|
|
||||||
prompt_t *pr = snew(prompt_t);
|
|
||||||
pr->prompt = promptstr;
|
|
||||||
pr->echo = echo;
|
|
||||||
pr->result = strbuf_new_nm();
|
|
||||||
sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
|
|
||||||
p->prompts[p->n_prompts++] = pr;
|
|
||||||
}
|
|
||||||
void prompt_set_result(prompt_t *pr, const char *newstr)
|
|
||||||
{
|
|
||||||
strbuf_clear(pr->result);
|
|
||||||
put_datapl(pr->result, ptrlen_from_asciz(newstr));
|
|
||||||
}
|
|
||||||
const char *prompt_get_result_ref(prompt_t *pr)
|
|
||||||
{
|
|
||||||
return pr->result->s;
|
|
||||||
}
|
|
||||||
char *prompt_get_result(prompt_t *pr)
|
|
||||||
{
|
|
||||||
return dupstr(pr->result->s);
|
|
||||||
}
|
|
||||||
void free_prompts(prompts_t *p)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
for (i=0; i < p->n_prompts; i++) {
|
|
||||||
prompt_t *pr = p->prompts[i];
|
|
||||||
strbuf_free(pr->result);
|
|
||||||
sfree(pr->prompt);
|
|
||||||
sfree(pr);
|
|
||||||
}
|
|
||||||
sfree(p->prompts);
|
|
||||||
sfree(p->name);
|
|
||||||
sfree(p->instruction);
|
|
||||||
sfree(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Determine whether or not a Conf represents a session which can
|
|
||||||
* sensibly be launched right now.
|
|
||||||
*/
|
|
||||||
bool conf_launchable(Conf *conf)
|
|
||||||
{
|
|
||||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
|
||||||
return conf_get_str(conf, CONF_serline)[0] != 0;
|
|
||||||
else
|
|
||||||
return conf_get_str(conf, CONF_host)[0] != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *conf_dest(Conf *conf)
|
|
||||||
{
|
|
||||||
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
|
||||||
return conf_get_str(conf, CONF_serline);
|
|
||||||
else
|
|
||||||
return conf_get_str(conf, CONF_host);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Validate a manual host key specification (either entered in the
|
|
||||||
* GUI, or via -hostkey). If valid, we return true, and update 'key'
|
|
||||||
* to contain a canonicalised version of the key string in 'key'
|
|
||||||
* (which is guaranteed to take up at most as much space as the
|
|
||||||
* original version), suitable for putting into the Conf. If not
|
|
||||||
* valid, we return false.
|
|
||||||
*/
|
|
||||||
bool validate_manual_hostkey(char *key)
|
|
||||||
{
|
|
||||||
char *p, *q, *r, *s;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Step through the string word by word, looking for a word that's
|
|
||||||
* in one of the formats we like.
|
|
||||||
*/
|
|
||||||
p = key;
|
|
||||||
while ((p += strspn(p, " \t"))[0]) {
|
|
||||||
q = p;
|
|
||||||
p += strcspn(p, " \t");
|
|
||||||
if (*p) *p++ = '\0';
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now q is our word.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (strstartswith(q, "SHA256:")) {
|
|
||||||
/* Test for a valid SHA256 key fingerprint. */
|
|
||||||
r = q + 7;
|
|
||||||
if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = q;
|
|
||||||
if (strstartswith(r, "MD5:"))
|
|
||||||
r += 4;
|
|
||||||
if (strlen(r) == 16*3 - 1 &&
|
|
||||||
r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
|
|
||||||
/*
|
|
||||||
* Test for a valid MD5 key fingerprint. Check the colons
|
|
||||||
* are in the right places, and if so, return the same
|
|
||||||
* fingerprint canonicalised into lowercase.
|
|
||||||
*/
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
if (r[3*i] == ':' || r[3*i+1] == ':')
|
|
||||||
goto not_fingerprint; /* sorry */
|
|
||||||
for (i = 0; i < 15; i++)
|
|
||||||
if (r[3*i+2] != ':')
|
|
||||||
goto not_fingerprint; /* sorry */
|
|
||||||
for (i = 0; i < 16*3 - 1; i++)
|
|
||||||
key[i] = tolower(r[i]);
|
|
||||||
key[16*3 - 1] = '\0';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
not_fingerprint:;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Before we check for a public-key blob, trim newlines out of
|
|
||||||
* the middle of the word, in case someone's managed to paste
|
|
||||||
* in a public-key blob _with_ them.
|
|
||||||
*/
|
|
||||||
for (r = s = q; *r; r++)
|
|
||||||
if (*r != '\n' && *r != '\r')
|
|
||||||
*s++ = *r;
|
|
||||||
*s = '\0';
|
|
||||||
|
|
||||||
if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
|
|
||||||
q[strspn(q, BASE64_CHARS_ALL)] == 0) {
|
|
||||||
/*
|
|
||||||
* Might be a base64-encoded SSH-2 public key blob. Check
|
|
||||||
* that it starts with a sensible algorithm string. No
|
|
||||||
* canonicalisation is necessary for this string type.
|
|
||||||
*
|
|
||||||
* The algorithm string must be at most 64 characters long
|
|
||||||
* (RFC 4251 section 6).
|
|
||||||
*/
|
|
||||||
unsigned char decoded[6];
|
|
||||||
unsigned alglen;
|
|
||||||
int minlen;
|
|
||||||
int len = 0;
|
|
||||||
|
|
||||||
len += base64_decode_atom(q, decoded+len);
|
|
||||||
if (len < 3)
|
|
||||||
goto not_ssh2_blob; /* sorry */
|
|
||||||
len += base64_decode_atom(q+4, decoded+len);
|
|
||||||
if (len < 4)
|
|
||||||
goto not_ssh2_blob; /* sorry */
|
|
||||||
|
|
||||||
alglen = GET_32BIT_MSB_FIRST(decoded);
|
|
||||||
if (alglen > 64)
|
|
||||||
goto not_ssh2_blob; /* sorry */
|
|
||||||
|
|
||||||
minlen = ((alglen + 4) + 2) / 3;
|
|
||||||
if (strlen(q) < minlen)
|
|
||||||
goto not_ssh2_blob; /* sorry */
|
|
||||||
|
|
||||||
strcpy(key, q);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
not_ssh2_blob:;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *buildinfo(const char *newline)
|
|
||||||
{
|
|
||||||
strbuf *buf = strbuf_new();
|
|
||||||
|
|
||||||
strbuf_catf(buf, "Build platform: %d-bit %s",
|
|
||||||
(int)(CHAR_BIT * sizeof(void *)),
|
|
||||||
BUILDINFO_PLATFORM);
|
|
||||||
|
|
||||||
#ifdef __clang_version__
|
|
||||||
#define FOUND_COMPILER
|
|
||||||
strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
|
|
||||||
#elif defined __GNUC__ && defined __VERSION__
|
|
||||||
#define FOUND_COMPILER
|
|
||||||
strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined _MSC_VER
|
|
||||||
#ifndef FOUND_COMPILER
|
|
||||||
#define FOUND_COMPILER
|
|
||||||
strbuf_catf(buf, "%sCompiler: ", newline);
|
|
||||||
#else
|
|
||||||
strbuf_catf(buf, ", emulating ");
|
|
||||||
#endif
|
|
||||||
strbuf_catf(buf, "Visual Studio");
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/*
|
|
||||||
* List of _MSC_VER values and their translations taken from
|
|
||||||
* https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
|
||||||
*
|
|
||||||
* The pointless #if 0 branch containing this comment is there so
|
|
||||||
* that every real clause can start with #elif and there's no
|
|
||||||
* anomalous first clause. That way the patch looks nicer when you
|
|
||||||
* add extra ones.
|
|
||||||
*/
|
|
||||||
#elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500
|
|
||||||
/*
|
|
||||||
* 16.9 and 16.8 have the same _MSC_VER value, and have to be
|
|
||||||
* distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not
|
|
||||||
* mentioned on the above page, but see e.g.
|
|
||||||
* https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120
|
|
||||||
* which says that 16.9 builds will have versions starting at
|
|
||||||
* 19.28.29500.* and going up. Hence, 19 28 29500 is what we
|
|
||||||
* compare _MSC_FULL_VER against above.
|
|
||||||
*/
|
|
||||||
strbuf_catf(buf, " 2019 (16.9)");
|
|
||||||
#elif _MSC_VER == 1928
|
|
||||||
strbuf_catf(buf, " 2019 (16.8)");
|
|
||||||
#elif _MSC_VER == 1927
|
|
||||||
strbuf_catf(buf, " 2019 (16.7)");
|
|
||||||
#elif _MSC_VER == 1926
|
|
||||||
strbuf_catf(buf, " 2019 (16.6)");
|
|
||||||
#elif _MSC_VER == 1925
|
|
||||||
strbuf_catf(buf, " 2019 (16.5)");
|
|
||||||
#elif _MSC_VER == 1924
|
|
||||||
strbuf_catf(buf, " 2019 (16.4)");
|
|
||||||
#elif _MSC_VER == 1923
|
|
||||||
strbuf_catf(buf, " 2019 (16.3)");
|
|
||||||
#elif _MSC_VER == 1922
|
|
||||||
strbuf_catf(buf, " 2019 (16.2)");
|
|
||||||
#elif _MSC_VER == 1921
|
|
||||||
strbuf_catf(buf, " 2019 (16.1)");
|
|
||||||
#elif _MSC_VER == 1920
|
|
||||||
strbuf_catf(buf, " 2019 (16.0)");
|
|
||||||
#elif _MSC_VER == 1916
|
|
||||||
strbuf_catf(buf, " 2017 version 15.9");
|
|
||||||
#elif _MSC_VER == 1915
|
|
||||||
strbuf_catf(buf, " 2017 version 15.8");
|
|
||||||
#elif _MSC_VER == 1914
|
|
||||||
strbuf_catf(buf, " 2017 version 15.7");
|
|
||||||
#elif _MSC_VER == 1913
|
|
||||||
strbuf_catf(buf, " 2017 version 15.6");
|
|
||||||
#elif _MSC_VER == 1912
|
|
||||||
strbuf_catf(buf, " 2017 version 15.5");
|
|
||||||
#elif _MSC_VER == 1911
|
|
||||||
strbuf_catf(buf, " 2017 version 15.3");
|
|
||||||
#elif _MSC_VER == 1910
|
|
||||||
strbuf_catf(buf, " 2017 RTW (15.0)");
|
|
||||||
#elif _MSC_VER == 1900
|
|
||||||
strbuf_catf(buf, " 2015 (14.0)");
|
|
||||||
#elif _MSC_VER == 1800
|
|
||||||
strbuf_catf(buf, " 2013 (12.0)");
|
|
||||||
#elif _MSC_VER == 1700
|
|
||||||
strbuf_catf(buf, " 2012 (11.0)");
|
|
||||||
#elif _MSC_VER == 1600
|
|
||||||
strbuf_catf(buf, " 2010 (10.0)");
|
|
||||||
#elif _MSC_VER == 1500
|
|
||||||
strbuf_catf(buf, " 2008 (9.0)");
|
|
||||||
#elif _MSC_VER == 1400
|
|
||||||
strbuf_catf(buf, " 2005 (8.0)");
|
|
||||||
#elif _MSC_VER == 1310
|
|
||||||
strbuf_catf(buf, " .NET 2003 (7.1)");
|
|
||||||
#elif _MSC_VER == 1300
|
|
||||||
strbuf_catf(buf, " .NET 2002 (7.0)");
|
|
||||||
#elif _MSC_VER == 1200
|
|
||||||
strbuf_catf(buf, " 6.0");
|
|
||||||
#else
|
|
||||||
strbuf_catf(buf, ", unrecognised version");
|
|
||||||
#endif
|
|
||||||
strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef BUILDINFO_GTK
|
|
||||||
{
|
|
||||||
char *gtk_buildinfo = buildinfo_gtk_version();
|
|
||||||
if (gtk_buildinfo) {
|
|
||||||
strbuf_catf(buf, "%sCompiled against GTK version %s",
|
|
||||||
newline, gtk_buildinfo);
|
|
||||||
sfree(gtk_buildinfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if defined _WINDOWS
|
|
||||||
{
|
|
||||||
int echm = has_embedded_chm();
|
|
||||||
if (echm >= 0)
|
|
||||||
strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
|
|
||||||
echm ? "yes" : "no");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined _WINDOWS && defined MINEFIELD
|
|
||||||
strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef NO_SECUREZEROMEMORY
|
|
||||||
strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef NO_IPV6
|
|
||||||
strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef NO_GSSAPI
|
|
||||||
strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef STATIC_GSSAPI
|
|
||||||
strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef UNPROTECT
|
|
||||||
strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef FUZZING
|
|
||||||
strbuf_catf(buf, "%sBuild option: FUZZING", newline);
|
|
||||||
#endif
|
|
||||||
#ifdef DEBUG
|
|
||||||
strbuf_catf(buf, "%sBuild option: DEBUG", newline);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
|
|
||||||
|
|
||||||
return strbuf_to_str(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t nullseat_output(
|
|
||||||
Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
|
|
||||||
bool nullseat_eof(Seat *seat) { return true; }
|
|
||||||
int nullseat_get_userpass_input(
|
|
||||||
Seat *seat, prompts_t *p, bufchain *input) { return 0; }
|
|
||||||
void nullseat_notify_remote_exit(Seat *seat) {}
|
|
||||||
void nullseat_connection_fatal(Seat *seat, const char *message) {}
|
|
||||||
void nullseat_update_specials_menu(Seat *seat) {}
|
|
||||||
char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
|
|
||||||
void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
|
|
||||||
int nullseat_verify_ssh_host_key(
|
|
||||||
Seat *seat, const char *host, int port, const char *keytype,
|
|
||||||
char *keystr, const char *keydisp, char **key_fingerprints,
|
|
||||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
|
||||||
int nullseat_confirm_weak_crypto_primitive(
|
|
||||||
Seat *seat, const char *algtype, const char *algname,
|
|
||||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
|
||||||
int nullseat_confirm_weak_cached_hostkey(
|
|
||||||
Seat *seat, const char *algname, const char *betteralgs,
|
|
||||||
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
|
||||||
bool nullseat_is_never_utf8(Seat *seat) { return false; }
|
|
||||||
bool nullseat_is_always_utf8(Seat *seat) { return true; }
|
|
||||||
void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
|
|
||||||
const char *nullseat_get_x_display(Seat *seat) { return NULL; }
|
|
||||||
bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
|
|
||||||
bool nullseat_get_window_pixel_size(
|
|
||||||
Seat *seat, int *width, int *height) { return false; }
|
|
||||||
StripCtrlChars *nullseat_stripctrl_new(
|
|
||||||
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
|
|
||||||
bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
|
|
||||||
bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
|
|
||||||
bool nullseat_verbose_no(Seat *seat) { return false; }
|
|
||||||
bool nullseat_verbose_yes(Seat *seat) { return true; }
|
|
||||||
bool nullseat_interactive_no(Seat *seat) { return false; }
|
|
||||||
bool nullseat_interactive_yes(Seat *seat) { return true; }
|
|
||||||
bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; }
|
|
||||||
|
|
||||||
bool null_lp_verbose_no(LogPolicy *lp) { return false; }
|
|
||||||
bool null_lp_verbose_yes(LogPolicy *lp) { return true; }
|
|
||||||
|
|
||||||
void sk_free_peer_info(SocketPeerInfo *pi)
|
|
||||||
{
|
|
||||||
if (pi) {
|
|
||||||
sfree((char *)pi->addr_text);
|
|
||||||
sfree((char *)pi->log_text);
|
|
||||||
sfree(pi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void out_of_memory(void)
|
|
||||||
{
|
|
||||||
modalfatalbox("Out of memory");
|
|
||||||
}
|
|
@ -1,8 +1,29 @@
|
|||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
add_platform_sources_to_library(utils
|
add_platform_sources_to_library(utils
|
||||||
uxutils.c uxsignal.c uxpoll.c xkeysym.c uxmisc.c xpmpucfg.c
|
utils/arm_arch_queries.c
|
||||||
xpmputty.c xpmptcfg.c xpmpterm.c x11misc.c ../time.c)
|
utils/block_signal.c
|
||||||
|
utils/cloexec.c
|
||||||
|
utils/dputs.c
|
||||||
|
utils/filename.c
|
||||||
|
utils/fontspec.c
|
||||||
|
utils/getticks.c
|
||||||
|
utils/get_username.c
|
||||||
|
utils/keysym_to_unicode.c
|
||||||
|
utils/make_dir_and_check_ours.c
|
||||||
|
utils/make_dir_path.c
|
||||||
|
utils/nonblock.c
|
||||||
|
utils/open_for_write_would_lose_data.c
|
||||||
|
utils/pgp_fingerprints.c
|
||||||
|
utils/pollwrap.c
|
||||||
|
utils/signal.c
|
||||||
|
utils/x11_ignore_error.c
|
||||||
|
../utils/smemclr.c
|
||||||
|
# Compiled icon pixmap files
|
||||||
|
xpmpucfg.c xpmputty.c xpmptcfg.c xpmpterm.c
|
||||||
|
# We want the ISO C implementation of ltime(), because we don't have
|
||||||
|
# a local better alternative
|
||||||
|
../utils/ltime.c)
|
||||||
add_platform_sources_to_library(eventloop
|
add_platform_sources_to_library(eventloop
|
||||||
uxcliloop.c uxsel.c)
|
uxcliloop.c uxsel.c)
|
||||||
add_platform_sources_to_library(console
|
add_platform_sources_to_library(console
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Unix implementation of the OS query functions that detect Arm
|
||||||
|
* architecture extensions.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "putty.h"
|
#include "putty.h"
|
||||||
#include "ssh.h"
|
#include "ssh.h"
|
||||||
|
|
||||||
#include "uxutils.h"
|
#include "utils/arm_arch_queries.h"
|
||||||
|
|
||||||
#if defined __arm__ || defined __aarch64__
|
#if defined __arm__ || defined __aarch64__
|
||||||
|
|
||||||
@ -62,4 +67,14 @@ bool platform_sha512_hw_available(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else /* defined __arm__ || defined __aarch64__ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include _something_ in this file to prevent an annoying compiler
|
||||||
|
* warning, and to avoid having to condition out this file in
|
||||||
|
* CMakeLists. It's in a library, so this variable shouldn't end up in
|
||||||
|
* any actual program, because nothing will refer to it.
|
||||||
|
*/
|
||||||
|
const int arm_arch_queries_dummy_variable = 0;
|
||||||
|
|
||||||
#endif /* defined __arm__ || defined __aarch64__ */
|
#endif /* defined __arm__ || defined __aarch64__ */
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* uxutils.h: header included only by uxutils.c.
|
* Header included only by arm_arch_queries.c.
|
||||||
*
|
*
|
||||||
* The only reason this is a header file instead of a source file is
|
* The only reason this is a header file instead of a source file is
|
||||||
* so that I can define 'static inline' functions which may or may not
|
* so that I can define 'static inline' functions which may or may not
|
||||||
@ -7,8 +7,8 @@
|
|||||||
* to use them in the subsequent source file.
|
* to use them in the subsequent source file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PUTTY_UXUTILS_H
|
#ifndef PUTTY_ARM_ARCH_QUERIES_H
|
||||||
#define PUTTY_UXUTILS_H
|
#define PUTTY_ARM_ARCH_QUERIES_H
|
||||||
|
|
||||||
#if defined __APPLE__
|
#if defined __APPLE__
|
||||||
#if HAVE_SYS_SYSCTL_H
|
#if HAVE_SYS_SYSCTL_H
|
||||||
@ -62,4 +62,4 @@ static inline bool test_sysctl_flag(const char *flagname)
|
|||||||
}
|
}
|
||||||
#endif /* defined __APPLE__ */
|
#endif /* defined __APPLE__ */
|
||||||
|
|
||||||
#endif /* PUTTY_UXUTILS_H */
|
#endif /* PUTTY_ARM_ARCH_QUERIES_H */
|
21
unix/utils/block_signal.c
Normal file
21
unix/utils/block_signal.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Handy function to block or unblock a signal, which does all the
|
||||||
|
* messing about with sigset_t for you.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
void block_signal(int sig, bool block_it)
|
||||||
|
{
|
||||||
|
sigset_t ss;
|
||||||
|
|
||||||
|
sigemptyset(&ss);
|
||||||
|
sigaddset(&ss, sig);
|
||||||
|
if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) {
|
||||||
|
perror("sigprocmask");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
49
unix/utils/cloexec.c
Normal file
49
unix/utils/cloexec.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Set and clear the FD_CLOEXEC fcntl option on a file descriptor.
|
||||||
|
*
|
||||||
|
* We don't realistically expect these operations to fail (the most
|
||||||
|
* plausible error condition is EBADF, but we always believe ourselves
|
||||||
|
* to be passing a valid fd so even that's an assertion-fail sort of
|
||||||
|
* response), so we don't make any effort to return sensible error
|
||||||
|
* codes to the caller - we just log to standard error and die
|
||||||
|
* unceremoniously.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void cloexec(int fd)
|
||||||
|
{
|
||||||
|
int fdflags;
|
||||||
|
|
||||||
|
fdflags = fcntl(fd, F_GETFD);
|
||||||
|
if (fdflags < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void noncloexec(int fd)
|
||||||
|
{
|
||||||
|
int fdflags;
|
||||||
|
|
||||||
|
fdflags = fcntl(fd, F_GETFD);
|
||||||
|
if (fdflags < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
24
unix/utils/dputs.c
Normal file
24
unix/utils/dputs.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of dputs() for Unix.
|
||||||
|
*
|
||||||
|
* The debug messages are written to standard output, and also into a
|
||||||
|
* file called debug.log.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
static FILE *debug_fp = NULL;
|
||||||
|
|
||||||
|
void dputs(const char *buf)
|
||||||
|
{
|
||||||
|
if (!debug_fp) {
|
||||||
|
debug_fp = fopen("debug.log", "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write(1, buf, strlen(buf)) < 0) {} /* 'error check' to placate gcc */
|
||||||
|
|
||||||
|
fputs(buf, debug_fp);
|
||||||
|
fflush(debug_fp);
|
||||||
|
}
|
72
unix/utils/filename.c
Normal file
72
unix/utils/filename.c
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of Filename for Unix, including f_open().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
Filename *filename_from_str(const char *str)
|
||||||
|
{
|
||||||
|
Filename *ret = snew(Filename);
|
||||||
|
ret->path = dupstr(str);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename *filename_copy(const Filename *fn)
|
||||||
|
{
|
||||||
|
return filename_from_str(fn->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *filename_to_str(const Filename *fn)
|
||||||
|
{
|
||||||
|
return fn->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool filename_equal(const Filename *f1, const Filename *f2)
|
||||||
|
{
|
||||||
|
return !strcmp(f1->path, f2->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool filename_is_null(const Filename *fn)
|
||||||
|
{
|
||||||
|
return !fn->path[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
void filename_free(Filename *fn)
|
||||||
|
{
|
||||||
|
sfree(fn->path);
|
||||||
|
sfree(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void filename_serialise(BinarySink *bs, const Filename *f)
|
||||||
|
{
|
||||||
|
put_asciz(bs, f->path);
|
||||||
|
}
|
||||||
|
Filename *filename_deserialise(BinarySource *src)
|
||||||
|
{
|
||||||
|
return filename_from_str(get_asciz(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename_char_sanitise(char c)
|
||||||
|
{
|
||||||
|
if (c == '/')
|
||||||
|
return '.';
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *f_open(const Filename *filename, char const *mode, bool is_private)
|
||||||
|
{
|
||||||
|
if (!is_private) {
|
||||||
|
return fopen(filename->path, mode);
|
||||||
|
} else {
|
||||||
|
int fd;
|
||||||
|
assert(mode[0] == 'w'); /* is_private is meaningless for read,
|
||||||
|
and tricky for append */
|
||||||
|
fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||||
|
if (fd < 0)
|
||||||
|
return NULL;
|
||||||
|
return fdopen(fd, mode);
|
||||||
|
}
|
||||||
|
}
|
35
unix/utils/fontspec.c
Normal file
35
unix/utils/fontspec.c
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of FontSpec for Unix. This is more or less
|
||||||
|
* degenerate - on this platform a font specification is just a
|
||||||
|
* string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
FontSpec *fontspec_new(const char *name)
|
||||||
|
{
|
||||||
|
FontSpec *f = snew(FontSpec);
|
||||||
|
f->name = dupstr(name);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontSpec *fontspec_copy(const FontSpec *f)
|
||||||
|
{
|
||||||
|
return fontspec_new(f->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fontspec_free(FontSpec *f)
|
||||||
|
{
|
||||||
|
sfree(f->name);
|
||||||
|
sfree(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fontspec_serialise(BinarySink *bs, FontSpec *f)
|
||||||
|
{
|
||||||
|
put_asciz(bs, f->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontSpec *fontspec_deserialise(BinarySource *src)
|
||||||
|
{
|
||||||
|
return fontspec_new(get_asciz(src));
|
||||||
|
}
|
52
unix/utils/get_username.c
Normal file
52
unix/utils/get_username.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of get_username() for Unix.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *get_username(void)
|
||||||
|
{
|
||||||
|
struct passwd *p;
|
||||||
|
uid_t uid = getuid();
|
||||||
|
char *user, *ret = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, find who we think we are using getlogin. If this
|
||||||
|
* agrees with our uid, we'll go along with it. This should
|
||||||
|
* allow sharing of uids between several login names whilst
|
||||||
|
* coping correctly with people who have su'ed.
|
||||||
|
*/
|
||||||
|
user = getlogin();
|
||||||
|
#if HAVE_SETPWENT
|
||||||
|
setpwent();
|
||||||
|
#endif
|
||||||
|
if (user)
|
||||||
|
p = getpwnam(user);
|
||||||
|
else
|
||||||
|
p = NULL;
|
||||||
|
if (p && p->pw_uid == uid) {
|
||||||
|
/*
|
||||||
|
* The result of getlogin() really does correspond to
|
||||||
|
* our uid. Fine.
|
||||||
|
*/
|
||||||
|
ret = user;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If that didn't work, for whatever reason, we'll do
|
||||||
|
* the simpler version: look up our uid in the password
|
||||||
|
* file and map it straight to a name.
|
||||||
|
*/
|
||||||
|
p = getpwuid(uid);
|
||||||
|
if (!p)
|
||||||
|
return NULL;
|
||||||
|
ret = p->pw_name;
|
||||||
|
}
|
||||||
|
#if HAVE_ENDPWENT
|
||||||
|
endpwent();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return dupstr(ret);
|
||||||
|
}
|
33
unix/utils/getticks.c
Normal file
33
unix/utils/getticks.c
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Implement getticks() for Unix.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
unsigned long getticks(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We want to use milliseconds rather than the microseconds or
|
||||||
|
* nanoseconds given by the underlying clock functions, because we
|
||||||
|
* need a decent number of them to fit into a 32-bit word so it
|
||||||
|
* can be used for keepalives.
|
||||||
|
*/
|
||||||
|
#if HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC
|
||||||
|
{
|
||||||
|
/* Use CLOCK_MONOTONIC if available, so as to be unconfused if
|
||||||
|
* the system clock changes. */
|
||||||
|
struct timespec ts;
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
|
||||||
|
return ts.tv_sec * TICKSPERSEC +
|
||||||
|
ts.tv_nsec / (1000000000 / TICKSPERSEC);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC);
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* xkeysym.c: mapping from X keysyms to Unicode values
|
* Map X keysyms to Unicode values.
|
||||||
*
|
*
|
||||||
* The basic idea of this is shamelessly cribbed from xterm. The
|
* The basic idea of this is shamelessly cribbed from xterm. The
|
||||||
* actual character data is generated from Markus Kuhn's proposed
|
* actual character data is generated from Markus Kuhn's proposed
|
60
unix/utils/make_dir_and_check_ours.c
Normal file
60
unix/utils/make_dir_and_check_ours.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Create a directory accessible only to us, and then check afterwards
|
||||||
|
* that we really did end up with a directory with the right ownership
|
||||||
|
* and permissions.
|
||||||
|
*
|
||||||
|
* The idea is that this is a directory in which we're about to create
|
||||||
|
* something sensitive, like a listening Unix-domain socket for SSH
|
||||||
|
* connection sharing or an SSH agent. We want to be protected against
|
||||||
|
* somebody else previously having created the directory in a way
|
||||||
|
* that's writable to us, and thus manipulating us into creating the
|
||||||
|
* actual socket in a directory they can see so that they can connect
|
||||||
|
* to it and (say) use our authenticated SSH sessions.
|
||||||
|
*
|
||||||
|
* NOTE: The strategy used in this function is not safe if the enemy
|
||||||
|
* has unrestricted write access to the containing directory. In that
|
||||||
|
* case, they could move our directory out of the way and make a new
|
||||||
|
* one, after this function returns and before we create our socket
|
||||||
|
* (or whatever) inside it.
|
||||||
|
*
|
||||||
|
* But this should be OK for temp directories (which modify the
|
||||||
|
* default world-write behaviour by also setting the 't' bit,
|
||||||
|
* preventing people from renaming or deleting things in there that
|
||||||
|
* they don't own). And of course it's also safe if the directory is
|
||||||
|
* writable only by our _own_ uid.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *make_dir_and_check_ours(const char *dirname)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the directory. We might have created it before, so
|
||||||
|
* EEXIST is an OK error; but anything else is doom.
|
||||||
|
*/
|
||||||
|
if (mkdir(dirname, 0700) < 0 && errno != EEXIST)
|
||||||
|
return dupprintf("%s: mkdir: %s", dirname, strerror(errno));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stat the directory and check its ownership and permissions.
|
||||||
|
*/
|
||||||
|
if (stat(dirname, &st) < 0)
|
||||||
|
return dupprintf("%s: stat: %s", dirname, strerror(errno));
|
||||||
|
if (st.st_uid != getuid())
|
||||||
|
return dupprintf("%s: directory owned by uid %d, not by us",
|
||||||
|
dirname, st.st_uid);
|
||||||
|
if ((st.st_mode & 077) != 0)
|
||||||
|
return dupprintf("%s: directory has overgenerous permissions %03o"
|
||||||
|
" (expected 700)", dirname, st.st_mode & 0777);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
39
unix/utils/make_dir_path.c
Normal file
39
unix/utils/make_dir_path.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Make a path of subdirectories, tolerating EEXIST at every step.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *make_dir_path(const char *path, mode_t mode)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
char *prefix;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
pos += strcspn(path + pos, "/");
|
||||||
|
|
||||||
|
if (pos > 0) {
|
||||||
|
prefix = dupprintf("%.*s", pos, path);
|
||||||
|
|
||||||
|
if (mkdir(prefix, mode) < 0 && errno != EEXIST) {
|
||||||
|
char *ret = dupprintf("%s: mkdir: %s",
|
||||||
|
prefix, strerror(errno));
|
||||||
|
sfree(prefix);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
sfree(prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path[pos])
|
||||||
|
return NULL;
|
||||||
|
pos += strspn(path + pos, "/");
|
||||||
|
}
|
||||||
|
}
|
55
unix/utils/nonblock.c
Normal file
55
unix/utils/nonblock.c
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Set and clear the O_NONBLOCK fcntl option on an open file.
|
||||||
|
*
|
||||||
|
* We don't realistically expect these operations to fail (the most
|
||||||
|
* plausible error condition is EBADF, but we always believe ourselves
|
||||||
|
* to be passing a valid fd so even that's an assertion-fail sort of
|
||||||
|
* response), so we don't make any effort to return sensible error
|
||||||
|
* codes to the caller - we just log to standard error and die
|
||||||
|
* unceremoniously.
|
||||||
|
*
|
||||||
|
* Returns the previous state of O_NONBLOCK.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
bool nonblock(int fd)
|
||||||
|
{
|
||||||
|
int fdflags;
|
||||||
|
|
||||||
|
fdflags = fcntl(fd, F_GETFL);
|
||||||
|
if (fdflags < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fdflags & O_NONBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool no_nonblock(int fd)
|
||||||
|
{
|
||||||
|
int fdflags;
|
||||||
|
|
||||||
|
fdflags = fcntl(fd, F_GETFL);
|
||||||
|
if (fdflags < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) {
|
||||||
|
fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fdflags & O_NONBLOCK;
|
||||||
|
}
|
44
unix/utils/open_for_write_would_lose_data.c
Normal file
44
unix/utils/open_for_write_would_lose_data.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Unix implementation of open_for_write_would_lose_data().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
bool open_for_write_would_lose_data(const Filename *fn)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (stat(fn->path, &st) < 0) {
|
||||||
|
/*
|
||||||
|
* If the file doesn't even exist, we obviously want to return
|
||||||
|
* false. If we failed to stat it for any other reason,
|
||||||
|
* ignoring the precise error code and returning false still
|
||||||
|
* doesn't seem too unreasonable, because then we'll try to
|
||||||
|
* open the file for writing and report _that_ error, which is
|
||||||
|
* likely to be more to the point.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* OK, something exists at this pathname and we've found out
|
||||||
|
* something about it. But an open-for-write will only
|
||||||
|
* destructively truncate it if it's a regular file with nonzero
|
||||||
|
* size. If it's empty, or some other kind of special thing like a
|
||||||
|
* character device (e.g. /dev/tty) or a named pipe, then opening
|
||||||
|
* it for write is already non-destructive and it's pointless and
|
||||||
|
* annoying to warn about it just because the same file can be
|
||||||
|
* opened for reading. (Indeed, if it's a named pipe, opening it
|
||||||
|
* for reading actually _causes inconvenience_ in its own right,
|
||||||
|
* even before the question of whether it gives misleading
|
||||||
|
* information.)
|
||||||
|
*/
|
||||||
|
if (S_ISREG(st.st_mode) && st.st_size > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
23
unix/utils/pgp_fingerprints.c
Normal file
23
unix/utils/pgp_fingerprints.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Display the fingerprints of the PGP Master Keys to the user.
|
||||||
|
*
|
||||||
|
* (This is in its own file rather than in uxcons.c, because it's
|
||||||
|
* appropriate even for Unix GUI apps.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void pgp_fingerprints(void)
|
||||||
|
{
|
||||||
|
fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
|
||||||
|
"be used to establish a trust path from this executable to another\n"
|
||||||
|
"one. See the manual for more information.\n"
|
||||||
|
"(Note: these fingerprints have nothing to do with SSH!)\n"
|
||||||
|
"\n"
|
||||||
|
"PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
|
||||||
|
" (" PGP_MASTER_KEY_DETAILS "):\n"
|
||||||
|
" " PGP_MASTER_KEY_FP "\n\n"
|
||||||
|
"Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
|
||||||
|
", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
|
||||||
|
" " PGP_PREV_MASTER_KEY_FP "\n", stdout);
|
||||||
|
}
|
@ -1,3 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Wrapper system around poll() that lets me treat it more or less
|
||||||
|
* like select(), but avoiding the inherent limitation of select()
|
||||||
|
* that it can't handle the full range of fds that are capable of
|
||||||
|
* existing.
|
||||||
|
*
|
||||||
|
* The pollwrapper structure contains the 'struct pollfd' array passed
|
||||||
|
* to poll() itself, and also a tree234 that maps each fd to its
|
||||||
|
* location in the list, which makes it convenient to add or remove
|
||||||
|
* individual fds from the system or change what events you're
|
||||||
|
* watching for on them. So the API is _shaped_ basically like select,
|
||||||
|
* even if none of the details are identical: from outside this
|
||||||
|
* module, a pollwrapper can be used wherever you'd otherwise have had
|
||||||
|
* an fd_set.
|
||||||
|
*
|
||||||
|
* Also, this module translate between the simple select r/w/x
|
||||||
|
* classification and the richer poll flags. We have to stick to r/w/x
|
||||||
|
* in this code base, because it ports to other systems where that's
|
||||||
|
* all you get.
|
||||||
|
*/
|
||||||
|
|
||||||
/* On some systems this is needed to get poll.h to define eg.. POLLRDNORM */
|
/* On some systems this is needed to get poll.h to define eg.. POLLRDNORM */
|
||||||
#define _XOPEN_SOURCE
|
#define _XOPEN_SOURCE
|
||||||
|
|
30
unix/utils/signal.c
Normal file
30
unix/utils/signal.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* PuTTY's wrapper on signal(2).
|
||||||
|
*
|
||||||
|
* Calling signal() is non-portable, as it varies in meaning between
|
||||||
|
* platforms and depending on feature macros, and has stupid semantics
|
||||||
|
* at least some of the time.
|
||||||
|
*
|
||||||
|
* This function provides the same interface as the libc function, but
|
||||||
|
* provides consistent semantics. It assumes POSIX semantics for
|
||||||
|
* sigaction() (so you might need to do some more work if you port to
|
||||||
|
* something ancient like SunOS 4).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
void (*putty_signal(int sig, void (*func)(int)))(int)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
struct sigaction old;
|
||||||
|
|
||||||
|
sa.sa_handler = func;
|
||||||
|
if(sigemptyset(&sa.sa_mask) < 0)
|
||||||
|
return SIG_ERR;
|
||||||
|
sa.sa_flags = SA_RESTART;
|
||||||
|
if(sigaction(sig, &sa, &old) < 0)
|
||||||
|
return SIG_ERR;
|
||||||
|
return old.sa_handler;
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* x11misc.c: miscellaneous stuff for dealing directly with X servers.
|
* Error handling mechanism which permits us to ignore specific X11
|
||||||
|
* errors from particular requests. We maintain a list of upcoming
|
||||||
|
* potential error events that we want to not treat as fatal errors.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
@ -18,12 +20,6 @@
|
|||||||
|
|
||||||
#include "x11misc.h"
|
#include "x11misc.h"
|
||||||
|
|
||||||
/* ----------------------------------------------------------------------
|
|
||||||
* Error handling mechanism which permits us to ignore specific X11
|
|
||||||
* errors from particular requests. We maintain a list of upcoming
|
|
||||||
* potential error events that we want to not treat as fatal errors.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int (*orig_x11_error_handler)(Display *thisdisp, XErrorEvent *err);
|
static int (*orig_x11_error_handler)(Display *thisdisp, XErrorEvent *err);
|
||||||
|
|
||||||
struct x11_err_to_ignore {
|
struct x11_err_to_ignore {
|
||||||
@ -79,5 +75,14 @@ void x11_ignore_error(Display *disp, unsigned char errcode)
|
|||||||
nerrs++;
|
nerrs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#else /* NOT_X_WINDOWS */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Include _something_ in this file to prevent an annoying compiler
|
||||||
|
* warning, and to avoid having to condition out this file in
|
||||||
|
* CMakeLists. It's in a library, so this variable shouldn't end up in
|
||||||
|
* any actual program, because nothing will refer to it.
|
||||||
|
*/
|
||||||
|
const int x11_ignore_error_dummy_variable = 0;
|
||||||
|
|
||||||
|
#endif /* NOT_X_WINDOWS */
|
371
unix/uxmisc.c
371
unix/uxmisc.c
@ -1,371 +0,0 @@
|
|||||||
/*
|
|
||||||
* PuTTY miscellaneous Unix stuff
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
|
|
||||||
#include "putty.h"
|
|
||||||
|
|
||||||
unsigned long getticks(void)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We want to use milliseconds rather than the microseconds or
|
|
||||||
* nanoseconds given by the underlying clock functions, because we
|
|
||||||
* need a decent number of them to fit into a 32-bit word so it
|
|
||||||
* can be used for keepalives.
|
|
||||||
*/
|
|
||||||
#if HAVE_CLOCK_GETTIME && HAVE_CLOCK_MONOTONIC
|
|
||||||
{
|
|
||||||
/* Use CLOCK_MONOTONIC if available, so as to be unconfused if
|
|
||||||
* the system clock changes. */
|
|
||||||
struct timespec ts;
|
|
||||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
|
|
||||||
return ts.tv_sec * TICKSPERSEC +
|
|
||||||
ts.tv_nsec / (1000000000 / TICKSPERSEC);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
struct timeval tv;
|
|
||||||
gettimeofday(&tv, NULL);
|
|
||||||
return tv.tv_sec * TICKSPERSEC + tv.tv_usec / (1000000 / TICKSPERSEC);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Filename *filename_from_str(const char *str)
|
|
||||||
{
|
|
||||||
Filename *ret = snew(Filename);
|
|
||||||
ret->path = dupstr(str);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
Filename *filename_copy(const Filename *fn)
|
|
||||||
{
|
|
||||||
return filename_from_str(fn->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *filename_to_str(const Filename *fn)
|
|
||||||
{
|
|
||||||
return fn->path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool filename_equal(const Filename *f1, const Filename *f2)
|
|
||||||
{
|
|
||||||
return !strcmp(f1->path, f2->path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool filename_is_null(const Filename *fn)
|
|
||||||
{
|
|
||||||
return !fn->path[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void filename_free(Filename *fn)
|
|
||||||
{
|
|
||||||
sfree(fn->path);
|
|
||||||
sfree(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void filename_serialise(BinarySink *bs, const Filename *f)
|
|
||||||
{
|
|
||||||
put_asciz(bs, f->path);
|
|
||||||
}
|
|
||||||
Filename *filename_deserialise(BinarySource *src)
|
|
||||||
{
|
|
||||||
return filename_from_str(get_asciz(src));
|
|
||||||
}
|
|
||||||
|
|
||||||
char filename_char_sanitise(char c)
|
|
||||||
{
|
|
||||||
if (c == '/')
|
|
||||||
return '.';
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
static FILE *debug_fp = NULL;
|
|
||||||
|
|
||||||
void dputs(const char *buf)
|
|
||||||
{
|
|
||||||
if (!debug_fp) {
|
|
||||||
debug_fp = fopen("debug.log", "w");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (write(1, buf, strlen(buf)) < 0) {} /* 'error check' to placate gcc */
|
|
||||||
|
|
||||||
fputs(buf, debug_fp);
|
|
||||||
fflush(debug_fp);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *get_username(void)
|
|
||||||
{
|
|
||||||
struct passwd *p;
|
|
||||||
uid_t uid = getuid();
|
|
||||||
char *user, *ret = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* First, find who we think we are using getlogin. If this
|
|
||||||
* agrees with our uid, we'll go along with it. This should
|
|
||||||
* allow sharing of uids between several login names whilst
|
|
||||||
* coping correctly with people who have su'ed.
|
|
||||||
*/
|
|
||||||
user = getlogin();
|
|
||||||
#if HAVE_SETPWENT
|
|
||||||
setpwent();
|
|
||||||
#endif
|
|
||||||
if (user)
|
|
||||||
p = getpwnam(user);
|
|
||||||
else
|
|
||||||
p = NULL;
|
|
||||||
if (p && p->pw_uid == uid) {
|
|
||||||
/*
|
|
||||||
* The result of getlogin() really does correspond to
|
|
||||||
* our uid. Fine.
|
|
||||||
*/
|
|
||||||
ret = user;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* If that didn't work, for whatever reason, we'll do
|
|
||||||
* the simpler version: look up our uid in the password
|
|
||||||
* file and map it straight to a name.
|
|
||||||
*/
|
|
||||||
p = getpwuid(uid);
|
|
||||||
if (!p)
|
|
||||||
return NULL;
|
|
||||||
ret = p->pw_name;
|
|
||||||
}
|
|
||||||
#if HAVE_ENDPWENT
|
|
||||||
endpwent();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return dupstr(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Display the fingerprints of the PGP Master Keys to the user.
|
|
||||||
* (This is here rather than in uxcons because it's appropriate even for
|
|
||||||
* Unix GUI apps.)
|
|
||||||
*/
|
|
||||||
void pgp_fingerprints(void)
|
|
||||||
{
|
|
||||||
fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
|
|
||||||
"be used to establish a trust path from this executable to another\n"
|
|
||||||
"one. See the manual for more information.\n"
|
|
||||||
"(Note: these fingerprints have nothing to do with SSH!)\n"
|
|
||||||
"\n"
|
|
||||||
"PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
|
|
||||||
" (" PGP_MASTER_KEY_DETAILS "):\n"
|
|
||||||
" " PGP_MASTER_KEY_FP "\n\n"
|
|
||||||
"Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
|
|
||||||
", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
|
|
||||||
" " PGP_PREV_MASTER_KEY_FP "\n", stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set and clear fcntl options on a file descriptor. We don't
|
|
||||||
* realistically expect any of these operations to fail (the most
|
|
||||||
* plausible error condition is EBADF, but we always believe ourselves
|
|
||||||
* to be passing a valid fd so even that's an assertion-fail sort of
|
|
||||||
* response), so we don't make any effort to return sensible error
|
|
||||||
* codes to the caller - we just log to standard error and die
|
|
||||||
* unceremoniously. However, nonblock and no_nonblock do return the
|
|
||||||
* previous state of O_NONBLOCK.
|
|
||||||
*/
|
|
||||||
void cloexec(int fd) {
|
|
||||||
int fdflags;
|
|
||||||
|
|
||||||
fdflags = fcntl(fd, F_GETFD);
|
|
||||||
if (fdflags < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fcntl(fd, F_SETFD, fdflags | FD_CLOEXEC) < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void noncloexec(int fd) {
|
|
||||||
int fdflags;
|
|
||||||
|
|
||||||
fdflags = fcntl(fd, F_GETFD);
|
|
||||||
if (fdflags < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_GETFD): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fcntl(fd, F_SETFD, fdflags & ~FD_CLOEXEC) < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_SETFD): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool nonblock(int fd) {
|
|
||||||
int fdflags;
|
|
||||||
|
|
||||||
fdflags = fcntl(fd, F_GETFL);
|
|
||||||
if (fdflags < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fcntl(fd, F_SETFL, fdflags | O_NONBLOCK) < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fdflags & O_NONBLOCK;
|
|
||||||
}
|
|
||||||
bool no_nonblock(int fd) {
|
|
||||||
int fdflags;
|
|
||||||
|
|
||||||
fdflags = fcntl(fd, F_GETFL);
|
|
||||||
if (fdflags < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_GETFL): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (fcntl(fd, F_SETFL, fdflags & ~O_NONBLOCK) < 0) {
|
|
||||||
fprintf(stderr, "%d: fcntl(F_SETFL): %s\n", fd, strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fdflags & O_NONBLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *f_open(const Filename *filename, char const *mode, bool is_private)
|
|
||||||
{
|
|
||||||
if (!is_private) {
|
|
||||||
return fopen(filename->path, mode);
|
|
||||||
} else {
|
|
||||||
int fd;
|
|
||||||
assert(mode[0] == 'w'); /* is_private is meaningless for read,
|
|
||||||
and tricky for append */
|
|
||||||
fd = open(filename->path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
|
||||||
if (fd < 0)
|
|
||||||
return NULL;
|
|
||||||
return fdopen(fd, mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FontSpec *fontspec_new(const char *name)
|
|
||||||
{
|
|
||||||
FontSpec *f = snew(FontSpec);
|
|
||||||
f->name = dupstr(name);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
FontSpec *fontspec_copy(const FontSpec *f)
|
|
||||||
{
|
|
||||||
return fontspec_new(f->name);
|
|
||||||
}
|
|
||||||
void fontspec_free(FontSpec *f)
|
|
||||||
{
|
|
||||||
sfree(f->name);
|
|
||||||
sfree(f);
|
|
||||||
}
|
|
||||||
void fontspec_serialise(BinarySink *bs, FontSpec *f)
|
|
||||||
{
|
|
||||||
put_asciz(bs, f->name);
|
|
||||||
}
|
|
||||||
FontSpec *fontspec_deserialise(BinarySource *src)
|
|
||||||
{
|
|
||||||
return fontspec_new(get_asciz(src));
|
|
||||||
}
|
|
||||||
|
|
||||||
char *make_dir_and_check_ours(const char *dirname)
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Create the directory. We might have created it before, so
|
|
||||||
* EEXIST is an OK error; but anything else is doom.
|
|
||||||
*/
|
|
||||||
if (mkdir(dirname, 0700) < 0 && errno != EEXIST)
|
|
||||||
return dupprintf("%s: mkdir: %s", dirname, strerror(errno));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Now check that that directory is _owned by us_ and not writable
|
|
||||||
* by anybody else. This protects us against somebody else
|
|
||||||
* previously having created the directory in a way that's
|
|
||||||
* writable to us, and thus manipulating us into creating the
|
|
||||||
* actual socket in a directory they can see so that they can
|
|
||||||
* connect to it and use our authenticated SSH sessions.
|
|
||||||
*/
|
|
||||||
if (stat(dirname, &st) < 0)
|
|
||||||
return dupprintf("%s: stat: %s", dirname, strerror(errno));
|
|
||||||
if (st.st_uid != getuid())
|
|
||||||
return dupprintf("%s: directory owned by uid %d, not by us",
|
|
||||||
dirname, st.st_uid);
|
|
||||||
if ((st.st_mode & 077) != 0)
|
|
||||||
return dupprintf("%s: directory has overgenerous permissions %03o"
|
|
||||||
" (expected 700)", dirname, st.st_mode & 0777);
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *make_dir_path(const char *path, mode_t mode)
|
|
||||||
{
|
|
||||||
int pos = 0;
|
|
||||||
char *prefix;
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
pos += strcspn(path + pos, "/");
|
|
||||||
|
|
||||||
if (pos > 0) {
|
|
||||||
prefix = dupprintf("%.*s", pos, path);
|
|
||||||
|
|
||||||
if (mkdir(prefix, mode) < 0 && errno != EEXIST) {
|
|
||||||
char *ret = dupprintf("%s: mkdir: %s",
|
|
||||||
prefix, strerror(errno));
|
|
||||||
sfree(prefix);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
sfree(prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path[pos])
|
|
||||||
return NULL;
|
|
||||||
pos += strspn(path + pos, "/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool open_for_write_would_lose_data(const Filename *fn)
|
|
||||||
{
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (stat(fn->path, &st) < 0) {
|
|
||||||
/*
|
|
||||||
* If the file doesn't even exist, we obviously want to return
|
|
||||||
* false. If we failed to stat it for any other reason,
|
|
||||||
* ignoring the precise error code and returning false still
|
|
||||||
* doesn't seem too unreasonable, because then we'll try to
|
|
||||||
* open the file for writing and report _that_ error, which is
|
|
||||||
* likely to be more to the point.
|
|
||||||
*/
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* OK, something exists at this pathname and we've found out
|
|
||||||
* something about it. But an open-for-write will only
|
|
||||||
* destructively truncate it if it's a regular file with nonzero
|
|
||||||
* size. If it's empty, or some other kind of special thing like a
|
|
||||||
* character device (e.g. /dev/tty) or a named pipe, then opening
|
|
||||||
* it for write is already non-destructive and it's pointless and
|
|
||||||
* annoying to warn about it just because the same file can be
|
|
||||||
* opened for reading. (Indeed, if it's a named pipe, opening it
|
|
||||||
* for reading actually _causes inconvenience_ in its own right,
|
|
||||||
* even before the question of whether it gives misleading
|
|
||||||
* information.)
|
|
||||||
*/
|
|
||||||
if (S_ISREG(st.st_mode) && st.st_size > 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "defs.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calling signal() is non-portable, as it varies in meaning
|
|
||||||
* between platforms and depending on feature macros, and has
|
|
||||||
* stupid semantics at least some of the time.
|
|
||||||
*
|
|
||||||
* This function provides the same interface as the libc function,
|
|
||||||
* but provides consistent semantics. It assumes POSIX semantics
|
|
||||||
* for sigaction() (so you might need to do some more work if you
|
|
||||||
* port to something ancient like SunOS 4)
|
|
||||||
*/
|
|
||||||
void (*putty_signal(int sig, void (*func)(int)))(int) {
|
|
||||||
struct sigaction sa;
|
|
||||||
struct sigaction old;
|
|
||||||
|
|
||||||
sa.sa_handler = func;
|
|
||||||
if(sigemptyset(&sa.sa_mask) < 0)
|
|
||||||
return SIG_ERR;
|
|
||||||
sa.sa_flags = SA_RESTART;
|
|
||||||
if(sigaction(sig, &sa, &old) < 0)
|
|
||||||
return SIG_ERR;
|
|
||||||
return old.sa_handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void block_signal(int sig, bool block_it)
|
|
||||||
{
|
|
||||||
sigset_t ss;
|
|
||||||
|
|
||||||
sigemptyset(&ss);
|
|
||||||
sigaddset(&ss, sig);
|
|
||||||
if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) {
|
|
||||||
perror("sigprocmask");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Local Variables:
|
|
||||||
c-basic-offset:4
|
|
||||||
comment-column:40
|
|
||||||
End:
|
|
||||||
*/
|
|
54
utils/base64_decode_atom.c
Normal file
54
utils/base64_decode_atom.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Core routine to decode a single atomic base64 chunk.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
int base64_decode_atom(const char *atom, unsigned char *out)
|
||||||
|
{
|
||||||
|
int vals[4];
|
||||||
|
int i, v, len;
|
||||||
|
unsigned word;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
c = atom[i];
|
||||||
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
v = c - 'A';
|
||||||
|
else if (c >= 'a' && c <= 'z')
|
||||||
|
v = c - 'a' + 26;
|
||||||
|
else if (c >= '0' && c <= '9')
|
||||||
|
v = c - '0' + 52;
|
||||||
|
else if (c == '+')
|
||||||
|
v = 62;
|
||||||
|
else if (c == '/')
|
||||||
|
v = 63;
|
||||||
|
else if (c == '=')
|
||||||
|
v = -1;
|
||||||
|
else
|
||||||
|
return 0; /* invalid atom */
|
||||||
|
vals[i] = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vals[0] == -1 || vals[1] == -1)
|
||||||
|
return 0;
|
||||||
|
if (vals[2] == -1 && vals[3] != -1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (vals[3] != -1)
|
||||||
|
len = 3;
|
||||||
|
else if (vals[2] != -1)
|
||||||
|
len = 2;
|
||||||
|
else
|
||||||
|
len = 1;
|
||||||
|
|
||||||
|
word = ((vals[0] << 18) |
|
||||||
|
(vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
|
||||||
|
out[0] = (word >> 16) & 0xFF;
|
||||||
|
if (len > 1)
|
||||||
|
out[1] = (word >> 8) & 0xFF;
|
||||||
|
if (len > 2)
|
||||||
|
out[2] = word & 0xFF;
|
||||||
|
return len;
|
||||||
|
}
|
30
utils/base64_encode_atom.c
Normal file
30
utils/base64_encode_atom.c
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Core routine to encode a single atomic base64 chunk.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
void base64_encode_atom(const unsigned char *data, int n, char *out)
|
||||||
|
{
|
||||||
|
static const char base64_chars[] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
|
||||||
|
unsigned word;
|
||||||
|
|
||||||
|
word = data[0] << 16;
|
||||||
|
if (n > 1)
|
||||||
|
word |= data[1] << 8;
|
||||||
|
if (n > 2)
|
||||||
|
word |= data[2];
|
||||||
|
out[0] = base64_chars[(word >> 18) & 0x3F];
|
||||||
|
out[1] = base64_chars[(word >> 12) & 0x3F];
|
||||||
|
if (n > 1)
|
||||||
|
out[2] = base64_chars[(word >> 6) & 0x3F];
|
||||||
|
else
|
||||||
|
out[2] = '=';
|
||||||
|
if (n > 2)
|
||||||
|
out[3] = base64_chars[word & 0x3F];
|
||||||
|
else
|
||||||
|
out[3] = '=';
|
||||||
|
}
|
173
utils/bufchain.c
Normal file
173
utils/bufchain.c
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* Generic routines to deal with send buffers: a linked list of
|
||||||
|
* smallish blocks, with the operations
|
||||||
|
*
|
||||||
|
* - add an arbitrary amount of data to the end of the list
|
||||||
|
* - remove the first N bytes from the list
|
||||||
|
* - return a (pointer,length) pair giving some initial data in
|
||||||
|
* the list, suitable for passing to a send or write system
|
||||||
|
* call
|
||||||
|
* - retrieve a larger amount of initial data from the list
|
||||||
|
* - return the current size of the buffer chain in bytes
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#define BUFFER_MIN_GRANULE 512
|
||||||
|
|
||||||
|
struct bufchain_granule {
|
||||||
|
struct bufchain_granule *next;
|
||||||
|
char *bufpos, *bufend, *bufmax;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic)
|
||||||
|
{
|
||||||
|
unreachable("bufchain callback used while uninitialised");
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_init(bufchain *ch)
|
||||||
|
{
|
||||||
|
ch->head = ch->tail = NULL;
|
||||||
|
ch->buffersize = 0;
|
||||||
|
ch->ic = NULL;
|
||||||
|
ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_clear(bufchain *ch)
|
||||||
|
{
|
||||||
|
struct bufchain_granule *b;
|
||||||
|
while (ch->head) {
|
||||||
|
b = ch->head;
|
||||||
|
ch->head = ch->head->next;
|
||||||
|
smemclr(b, sizeof(*b));
|
||||||
|
sfree(b);
|
||||||
|
}
|
||||||
|
ch->tail = NULL;
|
||||||
|
ch->buffersize = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bufchain_size(bufchain *ch)
|
||||||
|
{
|
||||||
|
return ch->buffersize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_set_callback_inner(
|
||||||
|
bufchain *ch, IdempotentCallback *ic,
|
||||||
|
void (*queue_idempotent_callback)(IdempotentCallback *ic))
|
||||||
|
{
|
||||||
|
ch->queue_idempotent_callback = queue_idempotent_callback;
|
||||||
|
ch->ic = ic;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_add(bufchain *ch, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
const char *buf = (const char *)data;
|
||||||
|
|
||||||
|
if (len == 0) return;
|
||||||
|
|
||||||
|
ch->buffersize += len;
|
||||||
|
|
||||||
|
while (len > 0) {
|
||||||
|
if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
|
||||||
|
size_t copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
|
||||||
|
memcpy(ch->tail->bufend, buf, copylen);
|
||||||
|
buf += copylen;
|
||||||
|
len -= copylen;
|
||||||
|
ch->tail->bufend += copylen;
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
size_t grainlen =
|
||||||
|
max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
|
||||||
|
struct bufchain_granule *newbuf;
|
||||||
|
newbuf = smalloc(grainlen);
|
||||||
|
newbuf->bufpos = newbuf->bufend =
|
||||||
|
(char *)newbuf + sizeof(struct bufchain_granule);
|
||||||
|
newbuf->bufmax = (char *)newbuf + grainlen;
|
||||||
|
newbuf->next = NULL;
|
||||||
|
if (ch->tail)
|
||||||
|
ch->tail->next = newbuf;
|
||||||
|
else
|
||||||
|
ch->head = newbuf;
|
||||||
|
ch->tail = newbuf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch->ic)
|
||||||
|
ch->queue_idempotent_callback(ch->ic);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_consume(bufchain *ch, size_t len)
|
||||||
|
{
|
||||||
|
struct bufchain_granule *tmp;
|
||||||
|
|
||||||
|
assert(ch->buffersize >= len);
|
||||||
|
while (len > 0) {
|
||||||
|
int remlen = len;
|
||||||
|
assert(ch->head != NULL);
|
||||||
|
if (remlen >= ch->head->bufend - ch->head->bufpos) {
|
||||||
|
remlen = ch->head->bufend - ch->head->bufpos;
|
||||||
|
tmp = ch->head;
|
||||||
|
ch->head = tmp->next;
|
||||||
|
if (!ch->head)
|
||||||
|
ch->tail = NULL;
|
||||||
|
smemclr(tmp, sizeof(*tmp));
|
||||||
|
sfree(tmp);
|
||||||
|
} else
|
||||||
|
ch->head->bufpos += remlen;
|
||||||
|
ch->buffersize -= remlen;
|
||||||
|
len -= remlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrlen bufchain_prefix(bufchain *ch)
|
||||||
|
{
|
||||||
|
return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_fetch(bufchain *ch, void *data, size_t len)
|
||||||
|
{
|
||||||
|
struct bufchain_granule *tmp;
|
||||||
|
char *data_c = (char *)data;
|
||||||
|
|
||||||
|
tmp = ch->head;
|
||||||
|
|
||||||
|
assert(ch->buffersize >= len);
|
||||||
|
while (len > 0) {
|
||||||
|
int remlen = len;
|
||||||
|
|
||||||
|
assert(tmp != NULL);
|
||||||
|
if (remlen >= tmp->bufend - tmp->bufpos)
|
||||||
|
remlen = tmp->bufend - tmp->bufpos;
|
||||||
|
memcpy(data_c, tmp->bufpos, remlen);
|
||||||
|
|
||||||
|
tmp = tmp->next;
|
||||||
|
len -= remlen;
|
||||||
|
data_c += remlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bufchain_fetch_consume(bufchain *ch, void *data, size_t len)
|
||||||
|
{
|
||||||
|
bufchain_fetch(ch, data, len);
|
||||||
|
bufchain_consume(ch, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len)
|
||||||
|
{
|
||||||
|
if (ch->buffersize >= len) {
|
||||||
|
bufchain_fetch_consume(ch, data, len);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len)
|
||||||
|
{
|
||||||
|
if (len > ch->buffersize)
|
||||||
|
len = ch->buffersize;
|
||||||
|
if (len)
|
||||||
|
bufchain_fetch_consume(ch, data, len);
|
||||||
|
return len;
|
||||||
|
}
|
158
utils/buildinfo.c
Normal file
158
utils/buildinfo.c
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* Return a string describing everything we know about how this
|
||||||
|
* particular binary was built: from what source, for what target
|
||||||
|
* platform, using what tools, with what settings, etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *buildinfo(const char *newline)
|
||||||
|
{
|
||||||
|
strbuf *buf = strbuf_new();
|
||||||
|
|
||||||
|
strbuf_catf(buf, "Build platform: %d-bit %s",
|
||||||
|
(int)(CHAR_BIT * sizeof(void *)),
|
||||||
|
BUILDINFO_PLATFORM);
|
||||||
|
|
||||||
|
#ifdef __clang_version__
|
||||||
|
#define FOUND_COMPILER
|
||||||
|
strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__);
|
||||||
|
#elif defined __GNUC__ && defined __VERSION__
|
||||||
|
#define FOUND_COMPILER
|
||||||
|
strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _MSC_VER
|
||||||
|
#ifndef FOUND_COMPILER
|
||||||
|
#define FOUND_COMPILER
|
||||||
|
strbuf_catf(buf, "%sCompiler: ", newline);
|
||||||
|
#else
|
||||||
|
strbuf_catf(buf, ", emulating ");
|
||||||
|
#endif
|
||||||
|
strbuf_catf(buf, "Visual Studio");
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/*
|
||||||
|
* List of _MSC_VER values and their translations taken from
|
||||||
|
* https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||||
|
*
|
||||||
|
* The pointless #if 0 branch containing this comment is there so
|
||||||
|
* that every real clause can start with #elif and there's no
|
||||||
|
* anomalous first clause. That way the patch looks nicer when you
|
||||||
|
* add extra ones.
|
||||||
|
*/
|
||||||
|
#elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500
|
||||||
|
/*
|
||||||
|
* 16.9 and 16.8 have the same _MSC_VER value, and have to be
|
||||||
|
* distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not
|
||||||
|
* mentioned on the above page, but see e.g.
|
||||||
|
* https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120
|
||||||
|
* which says that 16.9 builds will have versions starting at
|
||||||
|
* 19.28.29500.* and going up. Hence, 19 28 29500 is what we
|
||||||
|
* compare _MSC_FULL_VER against above.
|
||||||
|
*/
|
||||||
|
strbuf_catf(buf, " 2019 (16.9)");
|
||||||
|
#elif _MSC_VER == 1928
|
||||||
|
strbuf_catf(buf, " 2019 (16.8)");
|
||||||
|
#elif _MSC_VER == 1927
|
||||||
|
strbuf_catf(buf, " 2019 (16.7)");
|
||||||
|
#elif _MSC_VER == 1926
|
||||||
|
strbuf_catf(buf, " 2019 (16.6)");
|
||||||
|
#elif _MSC_VER == 1925
|
||||||
|
strbuf_catf(buf, " 2019 (16.5)");
|
||||||
|
#elif _MSC_VER == 1924
|
||||||
|
strbuf_catf(buf, " 2019 (16.4)");
|
||||||
|
#elif _MSC_VER == 1923
|
||||||
|
strbuf_catf(buf, " 2019 (16.3)");
|
||||||
|
#elif _MSC_VER == 1922
|
||||||
|
strbuf_catf(buf, " 2019 (16.2)");
|
||||||
|
#elif _MSC_VER == 1921
|
||||||
|
strbuf_catf(buf, " 2019 (16.1)");
|
||||||
|
#elif _MSC_VER == 1920
|
||||||
|
strbuf_catf(buf, " 2019 (16.0)");
|
||||||
|
#elif _MSC_VER == 1916
|
||||||
|
strbuf_catf(buf, " 2017 version 15.9");
|
||||||
|
#elif _MSC_VER == 1915
|
||||||
|
strbuf_catf(buf, " 2017 version 15.8");
|
||||||
|
#elif _MSC_VER == 1914
|
||||||
|
strbuf_catf(buf, " 2017 version 15.7");
|
||||||
|
#elif _MSC_VER == 1913
|
||||||
|
strbuf_catf(buf, " 2017 version 15.6");
|
||||||
|
#elif _MSC_VER == 1912
|
||||||
|
strbuf_catf(buf, " 2017 version 15.5");
|
||||||
|
#elif _MSC_VER == 1911
|
||||||
|
strbuf_catf(buf, " 2017 version 15.3");
|
||||||
|
#elif _MSC_VER == 1910
|
||||||
|
strbuf_catf(buf, " 2017 RTW (15.0)");
|
||||||
|
#elif _MSC_VER == 1900
|
||||||
|
strbuf_catf(buf, " 2015 (14.0)");
|
||||||
|
#elif _MSC_VER == 1800
|
||||||
|
strbuf_catf(buf, " 2013 (12.0)");
|
||||||
|
#elif _MSC_VER == 1700
|
||||||
|
strbuf_catf(buf, " 2012 (11.0)");
|
||||||
|
#elif _MSC_VER == 1600
|
||||||
|
strbuf_catf(buf, " 2010 (10.0)");
|
||||||
|
#elif _MSC_VER == 1500
|
||||||
|
strbuf_catf(buf, " 2008 (9.0)");
|
||||||
|
#elif _MSC_VER == 1400
|
||||||
|
strbuf_catf(buf, " 2005 (8.0)");
|
||||||
|
#elif _MSC_VER == 1310
|
||||||
|
strbuf_catf(buf, " .NET 2003 (7.1)");
|
||||||
|
#elif _MSC_VER == 1300
|
||||||
|
strbuf_catf(buf, " .NET 2002 (7.0)");
|
||||||
|
#elif _MSC_VER == 1200
|
||||||
|
strbuf_catf(buf, " 6.0");
|
||||||
|
#else
|
||||||
|
strbuf_catf(buf, ", unrecognised version");
|
||||||
|
#endif
|
||||||
|
strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef BUILDINFO_GTK
|
||||||
|
{
|
||||||
|
char *gtk_buildinfo = buildinfo_gtk_version();
|
||||||
|
if (gtk_buildinfo) {
|
||||||
|
strbuf_catf(buf, "%sCompiled against GTK version %s",
|
||||||
|
newline, gtk_buildinfo);
|
||||||
|
sfree(gtk_buildinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined _WINDOWS
|
||||||
|
{
|
||||||
|
int echm = has_embedded_chm();
|
||||||
|
if (echm >= 0)
|
||||||
|
strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline,
|
||||||
|
echm ? "yes" : "no");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _WINDOWS && defined MINEFIELD
|
||||||
|
strbuf_catf(buf, "%sBuild option: MINEFIELD", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef NO_SECUREZEROMEMORY
|
||||||
|
strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef NO_IPV6
|
||||||
|
strbuf_catf(buf, "%sBuild option: NO_IPV6", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef NO_GSSAPI
|
||||||
|
strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef STATIC_GSSAPI
|
||||||
|
strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef UNPROTECT
|
||||||
|
strbuf_catf(buf, "%sBuild option: UNPROTECT", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef FUZZING
|
||||||
|
strbuf_catf(buf, "%sBuild option: FUZZING", newline);
|
||||||
|
#endif
|
||||||
|
#ifdef DEBUG
|
||||||
|
strbuf_catf(buf, "%sBuild option: DEBUG", newline);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
strbuf_catf(buf, "%sSource commit: %s", newline, commitid);
|
||||||
|
|
||||||
|
return strbuf_to_str(buf);
|
||||||
|
}
|
15
utils/burnstr.c
Normal file
15
utils/burnstr.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* 'Burn' a dynamically allocated string, in the sense of destroying
|
||||||
|
* it beyond recovery: overwrite it with zeroes and then free it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
void burnstr(char *string)
|
||||||
|
{
|
||||||
|
if (string) {
|
||||||
|
smemclr(string, strlen(string));
|
||||||
|
sfree(string);
|
||||||
|
}
|
||||||
|
}
|
26
utils/chomp.c
Normal file
26
utils/chomp.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Perl-style 'chomp', for a line we just read with fgetline.
|
||||||
|
*
|
||||||
|
* Unlike Perl chomp, however, we're deliberately forgiving of strange
|
||||||
|
* line-ending conventions.
|
||||||
|
*
|
||||||
|
* Also we forgive NULL on input, so you can just write 'line =
|
||||||
|
* chomp(fgetline(fp));' and not bother checking for NULL until
|
||||||
|
* afterwards.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
char *chomp(char *str)
|
||||||
|
{
|
||||||
|
if (str) {
|
||||||
|
int len = strlen(str);
|
||||||
|
while (len > 0 && (str[len-1] == '\r' || str[len-1] == '\n'))
|
||||||
|
len--;
|
||||||
|
str[len] = '\0';
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
15
utils/conf_dest.c
Normal file
15
utils/conf_dest.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/*
|
||||||
|
* Decide whether the 'host name' or 'serial line' field of a Conf is
|
||||||
|
* important, based on which protocol it has selected.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char const *conf_dest(Conf *conf)
|
||||||
|
{
|
||||||
|
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||||
|
return conf_get_str(conf, CONF_serline);
|
||||||
|
else
|
||||||
|
return conf_get_str(conf, CONF_host);
|
||||||
|
}
|
||||||
|
|
14
utils/conf_launchable.c
Normal file
14
utils/conf_launchable.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Determine whether or not a Conf represents a session which can
|
||||||
|
* sensibly be launched right now.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
bool conf_launchable(Conf *conf)
|
||||||
|
{
|
||||||
|
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
|
||||||
|
return conf_get_str(conf, CONF_serline)[0] != 0;
|
||||||
|
else
|
||||||
|
return conf_get_str(conf, CONF_host)[0] != 0;
|
||||||
|
}
|
49
utils/ctrlparse.c
Normal file
49
utils/ctrlparse.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Parse a ^C style character specification.
|
||||||
|
* Returns NULL in `next' if we didn't recognise it as a control character,
|
||||||
|
* in which case `c' should be ignored.
|
||||||
|
* The precise current parsing is an oddity inherited from the terminal
|
||||||
|
* answerback-string parsing code. All sequences start with ^; all except
|
||||||
|
* ^<123> are two characters. The ones that are worth keeping are probably:
|
||||||
|
* ^? 127
|
||||||
|
* ^@A-Z[\]^_ 0-31
|
||||||
|
* a-z 1-26
|
||||||
|
* <num> specified by number (decimal, 0octal, 0xHEX)
|
||||||
|
* ~ ^ escape
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
char ctrlparse(char *s, char **next)
|
||||||
|
{
|
||||||
|
char c = 0;
|
||||||
|
if (*s != '^') {
|
||||||
|
*next = NULL;
|
||||||
|
} else {
|
||||||
|
s++;
|
||||||
|
if (*s == '\0') {
|
||||||
|
*next = NULL;
|
||||||
|
} else if (*s == '<') {
|
||||||
|
s++;
|
||||||
|
c = (char)strtol(s, next, 0);
|
||||||
|
if ((*next == s) || (**next != '>')) {
|
||||||
|
c = 0;
|
||||||
|
*next = NULL;
|
||||||
|
} else
|
||||||
|
(*next)++;
|
||||||
|
} else if (*s >= 'a' && *s <= 'z') {
|
||||||
|
c = (*s - ('a' - 1));
|
||||||
|
*next = s+1;
|
||||||
|
} else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) {
|
||||||
|
c = ('@' ^ *s);
|
||||||
|
*next = s+1;
|
||||||
|
} else if (*s == '~') {
|
||||||
|
c = '^';
|
||||||
|
*next = s+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
56
utils/debug.c
Normal file
56
utils/debug.c
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Debugging routines used by the debug() macros, at least if you
|
||||||
|
* compiled with -DDEBUG (aka the PUTTY_DEBUG cmake option) so that
|
||||||
|
* those macros don't optimise down to nothing.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
void debug_printf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *buf;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
buf = dupvprintf(fmt, ap);
|
||||||
|
dputs(buf);
|
||||||
|
sfree(buf);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_memdump(const void *buf, int len, bool L)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
const unsigned char *p = buf;
|
||||||
|
char foo[17];
|
||||||
|
if (L) {
|
||||||
|
int delta;
|
||||||
|
debug_printf("\t%d (0x%x) bytes:\n", len, len);
|
||||||
|
delta = 15 & (uintptr_t)p;
|
||||||
|
p -= delta;
|
||||||
|
len += delta;
|
||||||
|
}
|
||||||
|
for (; 0 < len; p += 16, len -= 16) {
|
||||||
|
dputs(" ");
|
||||||
|
if (L)
|
||||||
|
debug_printf("%p: ", p);
|
||||||
|
strcpy(foo, "................"); /* sixteen dots */
|
||||||
|
for (i = 0; i < 16 && i < len; ++i) {
|
||||||
|
if (&p[i] < (unsigned char *) buf) {
|
||||||
|
dputs(" "); /* 3 spaces */
|
||||||
|
foo[i] = ' ';
|
||||||
|
} else {
|
||||||
|
debug_printf("%c%2.2x",
|
||||||
|
&p[i] != (unsigned char *) buf
|
||||||
|
&& i % 4 ? '.' : ' ', p[i]
|
||||||
|
);
|
||||||
|
if (p[i] >= ' ' && p[i] <= '~')
|
||||||
|
foo[i] = (char) p[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foo[i] = '\0';
|
||||||
|
debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
|
||||||
|
}
|
||||||
|
}
|
48
utils/dupcat.c
Normal file
48
utils/dupcat.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Implementation function behind dupcat() in misc.h.
|
||||||
|
*
|
||||||
|
* This function is called with an arbitrary number of 'const char *'
|
||||||
|
* parameters, of which the last one is a null pointer. The wrapper
|
||||||
|
* macro puts on the null pointer itself, so normally callers don't
|
||||||
|
* have to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
char *dupcat_fn(const char *s1, ...)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
char *p, *q, *sn;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
len = strlen(s1);
|
||||||
|
va_start(ap, s1);
|
||||||
|
while (1) {
|
||||||
|
sn = va_arg(ap, char *);
|
||||||
|
if (!sn)
|
||||||
|
break;
|
||||||
|
len += strlen(sn);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
p = snewn(len + 1, char);
|
||||||
|
strcpy(p, s1);
|
||||||
|
q = p + strlen(p);
|
||||||
|
|
||||||
|
va_start(ap, s1);
|
||||||
|
while (1) {
|
||||||
|
sn = va_arg(ap, char *);
|
||||||
|
if (!sn)
|
||||||
|
break;
|
||||||
|
strcpy(q, sn);
|
||||||
|
q += strlen(q);
|
||||||
|
}
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
100
utils/dupprintf.c
Normal file
100
utils/dupprintf.c
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Do an sprintf(), but into a custom-allocated buffer.
|
||||||
|
*
|
||||||
|
* Currently I'm doing this via vsnprintf. This has worked so far,
|
||||||
|
* but it's not good, because vsnprintf is not available on all
|
||||||
|
* platforms. There's an ifdef to use `_vsnprintf', which seems
|
||||||
|
* to be the local name for it on Windows. Other platforms may
|
||||||
|
* lack it completely, in which case it'll be time to rewrite
|
||||||
|
* this function in a totally different way.
|
||||||
|
*
|
||||||
|
* The only `properly' portable solution I can think of is to
|
||||||
|
* implement my own format string scanner, which figures out an
|
||||||
|
* upper bound for the length of each formatting directive,
|
||||||
|
* allocates the buffer as it goes along, and calls sprintf() to
|
||||||
|
* actually process each directive. If I ever need to actually do
|
||||||
|
* this, some caveats:
|
||||||
|
*
|
||||||
|
* - It's very hard to find a reliable upper bound for
|
||||||
|
* floating-point values. %f, in particular, when supplied with
|
||||||
|
* a number near to the upper or lower limit of representable
|
||||||
|
* numbers, could easily take several hundred characters. It's
|
||||||
|
* probably feasible to predict this statically using the
|
||||||
|
* constants in <float.h>, or even to predict it dynamically by
|
||||||
|
* looking at the exponent of the specific float provided, but
|
||||||
|
* it won't be fun.
|
||||||
|
*
|
||||||
|
* - Don't forget to _check_, after calling sprintf, that it's
|
||||||
|
* used at most the amount of space we had available.
|
||||||
|
*
|
||||||
|
* - Fault any formatting directive we don't fully understand. The
|
||||||
|
* aim here is to _guarantee_ that we never overflow the buffer,
|
||||||
|
* because this is a security-critical function. If we see a
|
||||||
|
* directive we don't know about, we should panic and die rather
|
||||||
|
* than run any risk.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
/* Work around lack of va_copy in old MSC */
|
||||||
|
#if defined _MSC_VER && !defined va_copy
|
||||||
|
#define va_copy(a, b) TYPECHECK( \
|
||||||
|
(va_list *)0 == &(a) && (va_list *)0 == &(b), \
|
||||||
|
memcpy(&a, &b, sizeof(va_list)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Also lack of vsnprintf before VS2015 */
|
||||||
|
#if defined _WINDOWS && \
|
||||||
|
!defined __MINGW32__ && \
|
||||||
|
!defined __WINE__ && \
|
||||||
|
_MSC_VER < 1900
|
||||||
|
#define vsnprintf _vsnprintf
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
|
||||||
|
const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
size_t size = *sizeptr;
|
||||||
|
sgrowarrayn_nm(buf, size, oldlen, 512);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
va_list aq;
|
||||||
|
va_copy(aq, ap);
|
||||||
|
int len = vsnprintf(buf + oldlen, size - oldlen, fmt, aq);
|
||||||
|
va_end(aq);
|
||||||
|
|
||||||
|
if (len >= 0 && len < size) {
|
||||||
|
/* This is the C99-specified criterion for snprintf to have
|
||||||
|
* been completely successful. */
|
||||||
|
*sizeptr = size;
|
||||||
|
return buf;
|
||||||
|
} else if (len > 0) {
|
||||||
|
/* This is the C99 error condition: the returned length is
|
||||||
|
* the required buffer size not counting the NUL. */
|
||||||
|
sgrowarrayn_nm(buf, size, oldlen + 1, len);
|
||||||
|
} else {
|
||||||
|
/* This is the pre-C99 glibc error condition: <0 means the
|
||||||
|
* buffer wasn't big enough, so we enlarge it a bit and hope. */
|
||||||
|
sgrowarray_nm(buf, size, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char *dupvprintf(const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
size_t size = 0;
|
||||||
|
return dupvprintf_inner(NULL, 0, &size, fmt, ap);
|
||||||
|
}
|
||||||
|
char *dupprintf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
char *ret;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
ret = dupvprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return ret;
|
||||||
|
}
|
19
utils/dupstr.c
Normal file
19
utils/dupstr.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Allocate a duplicate of an ordinary C NUL-terminated string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
char *dupstr(const char *s)
|
||||||
|
{
|
||||||
|
char *p = NULL;
|
||||||
|
if (s) {
|
||||||
|
int len = strlen(s);
|
||||||
|
p = snewn(len + 1, char);
|
||||||
|
strcpy(p, s);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
29
utils/encode_utf8.c
Normal file
29
utils/encode_utf8.c
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* Encode a single code point as UTF-8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
size_t encode_utf8(void *output, unsigned long ch)
|
||||||
|
{
|
||||||
|
unsigned char *start = (unsigned char *)output, *p = start;
|
||||||
|
|
||||||
|
if (ch < 0x80) {
|
||||||
|
*p++ = ch;
|
||||||
|
} else if (ch < 0x800) {
|
||||||
|
*p++ = 0xC0 | (ch >> 6);
|
||||||
|
*p++ = 0x80 | (ch & 0x3F);
|
||||||
|
} else if (ch < 0x10000) {
|
||||||
|
*p++ = 0xE0 | (ch >> 12);
|
||||||
|
*p++ = 0x80 | ((ch >> 6) & 0x3F);
|
||||||
|
*p++ = 0x80 | (ch & 0x3F);
|
||||||
|
} else {
|
||||||
|
assert(ch <= 0x10FFFF);
|
||||||
|
*p++ = 0xF0 | (ch >> 18);
|
||||||
|
*p++ = 0x80 | ((ch >> 12) & 0x3F);
|
||||||
|
*p++ = 0x80 | ((ch >> 6) & 0x3F);
|
||||||
|
*p++ = 0x80 | (ch & 0x3F);
|
||||||
|
}
|
||||||
|
return p - start;
|
||||||
|
}
|
25
utils/fgetline.c
Normal file
25
utils/fgetline.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Read an entire line of text from a file. Return a buffer
|
||||||
|
* malloced to be as big as necessary (caller must free).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
char *fgetline(FILE *fp)
|
||||||
|
{
|
||||||
|
char *ret = snewn(512, char);
|
||||||
|
size_t size = 512, len = 0;
|
||||||
|
while (fgets(ret + len, size - len, fp)) {
|
||||||
|
len += strlen(ret + len);
|
||||||
|
if (len > 0 && ret[len-1] == '\n')
|
||||||
|
break; /* got a newline, we're done */
|
||||||
|
sgrowarrayn_nm(ret, size, len, 512);
|
||||||
|
}
|
||||||
|
if (len == 0) { /* first fgets returned NULL */
|
||||||
|
sfree(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ret[len] = '\0';
|
||||||
|
return ret;
|
||||||
|
}
|
18
utils/host_strchr.c
Normal file
18
utils/host_strchr.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* strchr-like wrapper around host_strchr_internal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
char *host_strchr(const char *s, int c)
|
||||||
|
{
|
||||||
|
char set[2];
|
||||||
|
set[0] = c;
|
||||||
|
set[1] = '\0';
|
||||||
|
return (char *) host_strchr_internal(s, set, true);
|
||||||
|
}
|
80
utils/host_strchr_internal.c
Normal file
80
utils/host_strchr_internal.c
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Find a character in a string, unless it's a colon contained within
|
||||||
|
* square brackets. Used for untangling strings of the form
|
||||||
|
* 'host:port', where host can be an IPv6 literal.
|
||||||
|
*
|
||||||
|
* This internal function provides an API that's a bit like strchr (in
|
||||||
|
* that it returns a pointer to the character it found, or NULL), and
|
||||||
|
* a bit like strcspn (in that you can give it a set of characters to
|
||||||
|
* look for, not just one). Also it has an option to return the first
|
||||||
|
* or last character it finds. Other functions in the utils directory
|
||||||
|
* provide wrappers on it with APIs more like familiar <string.h>
|
||||||
|
* functions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
const char *host_strchr_internal(const char *s, const char *set, bool first)
|
||||||
|
{
|
||||||
|
int brackets = 0;
|
||||||
|
const char *ret = NULL;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
if (!*s)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (*s == '[')
|
||||||
|
brackets++;
|
||||||
|
else if (*s == ']' && brackets > 0)
|
||||||
|
brackets--;
|
||||||
|
else if (brackets && *s == ':')
|
||||||
|
/* never match */ ;
|
||||||
|
else if (strchr(set, *s)) {
|
||||||
|
ret = s;
|
||||||
|
if (first)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef TEST
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
int passes = 0, fails = 0;
|
||||||
|
|
||||||
|
#define TEST1(func, string, arg2, suffix, result) do \
|
||||||
|
{ \
|
||||||
|
const char *str = string; \
|
||||||
|
unsigned ret = func(string, arg2) suffix; \
|
||||||
|
if (ret == result) { \
|
||||||
|
passes++; \
|
||||||
|
} else { \
|
||||||
|
printf("fail: %s(%s,%s)%s = %u, expected %u\n", \
|
||||||
|
#func, #string, #arg2, #suffix, ret, \
|
||||||
|
(unsigned)result); \
|
||||||
|
fails++; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
TEST1(host_strchr, "[1:2:3]:4:5", ':', -str, 7);
|
||||||
|
TEST1(host_strrchr, "[1:2:3]:4:5", ':', -str, 9);
|
||||||
|
TEST1(host_strcspn, "[1:2:3]:4:5", "/:",, 7);
|
||||||
|
TEST1(host_strchr, "[1:2:3]", ':', == NULL, 1);
|
||||||
|
TEST1(host_strrchr, "[1:2:3]", ':', == NULL, 1);
|
||||||
|
TEST1(host_strcspn, "[1:2:3]", "/:",, 7);
|
||||||
|
TEST1(host_strcspn, "[1:2/3]", "/:",, 4);
|
||||||
|
TEST1(host_strcspn, "[1:2:3]/", "/:",, 7);
|
||||||
|
|
||||||
|
printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
|
||||||
|
return fails != 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* TEST */
|
19
utils/host_strcspn.c
Normal file
19
utils/host_strcspn.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* strcspn-like wrapper around host_strchr_internal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
size_t host_strcspn(const char *s, const char *set)
|
||||||
|
{
|
||||||
|
const char *answer = host_strchr_internal(s, set, true);
|
||||||
|
if (answer)
|
||||||
|
return answer - s;
|
||||||
|
else
|
||||||
|
return strlen(s);
|
||||||
|
}
|
51
utils/host_strduptrim.c
Normal file
51
utils/host_strduptrim.c
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Trim square brackets off the outside of an IPv6 address literal.
|
||||||
|
* Leave all other strings unchanged. Returns a fresh dynamically
|
||||||
|
* allocated string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
char *host_strduptrim(const char *s)
|
||||||
|
{
|
||||||
|
if (s[0] == '[') {
|
||||||
|
const char *p = s+1;
|
||||||
|
int colons = 0;
|
||||||
|
while (*p && *p != ']') {
|
||||||
|
if (isxdigit((unsigned char)*p))
|
||||||
|
/* OK */;
|
||||||
|
else if (*p == ':')
|
||||||
|
colons++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
if (*p == '%') {
|
||||||
|
/*
|
||||||
|
* This delimiter character introduces an RFC 4007 scope
|
||||||
|
* id suffix (e.g. suffixing the address literal with
|
||||||
|
* %eth1 or %2 or some such). There's no syntax
|
||||||
|
* specification for the scope id, so just accept anything
|
||||||
|
* except the closing ].
|
||||||
|
*/
|
||||||
|
p += strcspn(p, "]");
|
||||||
|
}
|
||||||
|
if (*p == ']' && !p[1] && colons > 1) {
|
||||||
|
/*
|
||||||
|
* This looks like an IPv6 address literal (hex digits and
|
||||||
|
* at least two colons, plus optional scope id, contained
|
||||||
|
* in square brackets). Trim off the brackets.
|
||||||
|
*/
|
||||||
|
return dupprintf("%.*s", (int)(p - (s+1)), s+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Any other shape of string is simply duplicated.
|
||||||
|
*/
|
||||||
|
return dupstr(s);
|
||||||
|
}
|
18
utils/host_strrchr.c
Normal file
18
utils/host_strrchr.c
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* strchr-like wrapper around host_strchr_internal.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
char *host_strrchr(const char *s, int c)
|
||||||
|
{
|
||||||
|
char set[2];
|
||||||
|
set[0] = c;
|
||||||
|
set[1] = '\0';
|
||||||
|
return (char *) host_strchr_internal(s, set, false);
|
||||||
|
}
|
34
utils/memxor.c
Normal file
34
utils/memxor.c
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* XOR two pieces of memory, copying the result into a third, which
|
||||||
|
* may precisely alias one of the input pair (but no guarantees if it
|
||||||
|
* partially overlaps).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size)
|
||||||
|
{
|
||||||
|
switch (size & 15) {
|
||||||
|
case 0:
|
||||||
|
while (size >= 16) {
|
||||||
|
size -= 16;
|
||||||
|
*out++ = *in1++ ^ *in2++;
|
||||||
|
case 15: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 14: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 13: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 12: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 11: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 10: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 9: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 8: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 7: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 6: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 5: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 4: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 3: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 2: *out++ = *in1++ ^ *in2++;
|
||||||
|
case 1: *out++ = *in1++ ^ *in2++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
utils/null_lp.c
Normal file
8
utils/null_lp.c
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* Stub methods usable by LogPolicy implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
bool null_lp_verbose_no(LogPolicy *lp) { return false; }
|
||||||
|
bool null_lp_verbose_yes(LogPolicy *lp) { return true; }
|
42
utils/nullseat.c
Normal file
42
utils/nullseat.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Stub methods usable by Seat implementations.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
size_t nullseat_output(
|
||||||
|
Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; }
|
||||||
|
bool nullseat_eof(Seat *seat) { return true; }
|
||||||
|
int nullseat_get_userpass_input(
|
||||||
|
Seat *seat, prompts_t *p, bufchain *input) { return 0; }
|
||||||
|
void nullseat_notify_remote_exit(Seat *seat) {}
|
||||||
|
void nullseat_connection_fatal(Seat *seat, const char *message) {}
|
||||||
|
void nullseat_update_specials_menu(Seat *seat) {}
|
||||||
|
char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; }
|
||||||
|
void nullseat_set_busy_status(Seat *seat, BusyStatus status) {}
|
||||||
|
int nullseat_verify_ssh_host_key(
|
||||||
|
Seat *seat, const char *host, int port, const char *keytype,
|
||||||
|
char *keystr, const char *keydisp, char **key_fingerprints,
|
||||||
|
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||||
|
int nullseat_confirm_weak_crypto_primitive(
|
||||||
|
Seat *seat, const char *algtype, const char *algname,
|
||||||
|
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||||
|
int nullseat_confirm_weak_cached_hostkey(
|
||||||
|
Seat *seat, const char *algname, const char *betteralgs,
|
||||||
|
void (*callback)(void *ctx, int result), void *ctx) { return 0; }
|
||||||
|
bool nullseat_is_never_utf8(Seat *seat) { return false; }
|
||||||
|
bool nullseat_is_always_utf8(Seat *seat) { return true; }
|
||||||
|
void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {}
|
||||||
|
const char *nullseat_get_x_display(Seat *seat) { return NULL; }
|
||||||
|
bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; }
|
||||||
|
bool nullseat_get_window_pixel_size(
|
||||||
|
Seat *seat, int *width, int *height) { return false; }
|
||||||
|
StripCtrlChars *nullseat_stripctrl_new(
|
||||||
|
Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;}
|
||||||
|
bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; }
|
||||||
|
bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; }
|
||||||
|
bool nullseat_verbose_no(Seat *seat) { return false; }
|
||||||
|
bool nullseat_verbose_yes(Seat *seat) { return true; }
|
||||||
|
bool nullseat_interactive_no(Seat *seat) { return false; }
|
||||||
|
bool nullseat_interactive_yes(Seat *seat) { return true; }
|
||||||
|
bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; }
|
21
utils/nullstrcmp.c
Normal file
21
utils/nullstrcmp.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Compare two strings, just like strcmp, except that we tolerate null
|
||||||
|
* pointers as a legal input, and define them to compare before any
|
||||||
|
* non-null string (even the empty string).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
int nullstrcmp(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
if (a == NULL && b == NULL)
|
||||||
|
return 0;
|
||||||
|
if (a == NULL)
|
||||||
|
return -1;
|
||||||
|
if (b == NULL)
|
||||||
|
return +1;
|
||||||
|
return strcmp(a, b);
|
||||||
|
}
|
11
utils/out_of_memory.c
Normal file
11
utils/out_of_memory.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* Standard implementation of the out_of_memory function called by our
|
||||||
|
* malloc wrappers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void out_of_memory(void)
|
||||||
|
{
|
||||||
|
modalfatalbox("Out of memory");
|
||||||
|
}
|
40
utils/parse_blocksize.c
Normal file
40
utils/parse_blocksize.c
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Parse a string block size specification. This is approximately a
|
||||||
|
* subset of the block size specs supported by GNU fileutils:
|
||||||
|
* "nk" = n kilobytes
|
||||||
|
* "nM" = n megabytes
|
||||||
|
* "nG" = n gigabytes
|
||||||
|
* All numbers are decimal, and suffixes refer to powers of two.
|
||||||
|
* Case-insensitive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
unsigned long parse_blocksize(const char *bs)
|
||||||
|
{
|
||||||
|
char *suf;
|
||||||
|
unsigned long r = strtoul(bs, &suf, 10);
|
||||||
|
if (*suf != '\0') {
|
||||||
|
while (*suf && isspace((unsigned char)*suf)) suf++;
|
||||||
|
switch (*suf) {
|
||||||
|
case 'k': case 'K':
|
||||||
|
r *= 1024ul;
|
||||||
|
break;
|
||||||
|
case 'm': case 'M':
|
||||||
|
r *= 1024ul * 1024ul;
|
||||||
|
break;
|
||||||
|
case 'g': case 'G':
|
||||||
|
r *= 1024ul * 1024ul * 1024ul;
|
||||||
|
break;
|
||||||
|
case '\0':
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
59
utils/prompts.c
Normal file
59
utils/prompts.c
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Functions for making, destroying, and manipulating prompts_t
|
||||||
|
* structures.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
prompts_t *new_prompts(void)
|
||||||
|
{
|
||||||
|
prompts_t *p = snew(prompts_t);
|
||||||
|
p->prompts = NULL;
|
||||||
|
p->n_prompts = p->prompts_size = 0;
|
||||||
|
p->data = NULL;
|
||||||
|
p->to_server = true; /* to be on the safe side */
|
||||||
|
p->name = p->instruction = NULL;
|
||||||
|
p->name_reqd = p->instr_reqd = false;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_prompt(prompts_t *p, char *promptstr, bool echo)
|
||||||
|
{
|
||||||
|
prompt_t *pr = snew(prompt_t);
|
||||||
|
pr->prompt = promptstr;
|
||||||
|
pr->echo = echo;
|
||||||
|
pr->result = strbuf_new_nm();
|
||||||
|
sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
|
||||||
|
p->prompts[p->n_prompts++] = pr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void prompt_set_result(prompt_t *pr, const char *newstr)
|
||||||
|
{
|
||||||
|
strbuf_clear(pr->result);
|
||||||
|
put_datapl(pr->result, ptrlen_from_asciz(newstr));
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *prompt_get_result_ref(prompt_t *pr)
|
||||||
|
{
|
||||||
|
return pr->result->s;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *prompt_get_result(prompt_t *pr)
|
||||||
|
{
|
||||||
|
return dupstr(pr->result->s);
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_prompts(prompts_t *p)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
for (i=0; i < p->n_prompts; i++) {
|
||||||
|
prompt_t *pr = p->prompts[i];
|
||||||
|
strbuf_free(pr->result);
|
||||||
|
sfree(pr->prompt);
|
||||||
|
sfree(pr);
|
||||||
|
}
|
||||||
|
sfree(p->prompts);
|
||||||
|
sfree(p->name);
|
||||||
|
sfree(p->instruction);
|
||||||
|
sfree(p);
|
||||||
|
}
|
95
utils/ptrlen.c
Normal file
95
utils/ptrlen.c
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Functions to deal with ptrlens.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
|
||||||
|
bool ptrlen_eq_string(ptrlen pl, const char *str)
|
||||||
|
{
|
||||||
|
size_t len = strlen(str);
|
||||||
|
return (pl.len == len && !memcmp(pl.ptr, str, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2)
|
||||||
|
{
|
||||||
|
return (pl1.len == pl2.len && !memcmp(pl1.ptr, pl2.ptr, pl1.len));
|
||||||
|
}
|
||||||
|
|
||||||
|
int ptrlen_strcmp(ptrlen pl1, ptrlen pl2)
|
||||||
|
{
|
||||||
|
size_t minlen = pl1.len < pl2.len ? pl1.len : pl2.len;
|
||||||
|
if (minlen) { /* tolerate plX.ptr==NULL as long as plX.len==0 */
|
||||||
|
int cmp = memcmp(pl1.ptr, pl2.ptr, minlen);
|
||||||
|
if (cmp)
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
return pl1.len < pl2.len ? -1 : pl1.len > pl2.len ? +1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail)
|
||||||
|
{
|
||||||
|
if (whole.len >= prefix.len &&
|
||||||
|
!memcmp(whole.ptr, prefix.ptr, prefix.len)) {
|
||||||
|
if (tail) {
|
||||||
|
tail->ptr = (const char *)whole.ptr + prefix.len;
|
||||||
|
tail->len = whole.len - prefix.len;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail)
|
||||||
|
{
|
||||||
|
if (whole.len >= suffix.len &&
|
||||||
|
!memcmp((char *)whole.ptr + (whole.len - suffix.len),
|
||||||
|
suffix.ptr, suffix.len)) {
|
||||||
|
if (tail) {
|
||||||
|
tail->ptr = whole.ptr;
|
||||||
|
tail->len = whole.len - suffix.len;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrlen ptrlen_get_word(ptrlen *input, const char *separators)
|
||||||
|
{
|
||||||
|
const char *p = input->ptr, *end = p + input->len;
|
||||||
|
ptrlen toret;
|
||||||
|
|
||||||
|
while (p < end && strchr(separators, *p))
|
||||||
|
p++;
|
||||||
|
toret.ptr = p;
|
||||||
|
while (p < end && !strchr(separators, *p))
|
||||||
|
p++;
|
||||||
|
toret.len = p - (const char *)toret.ptr;
|
||||||
|
|
||||||
|
size_t to_consume = p - (const char *)input->ptr;
|
||||||
|
assert(to_consume <= input->len);
|
||||||
|
input->ptr = (const char *)input->ptr + to_consume;
|
||||||
|
input->len -= to_consume;
|
||||||
|
|
||||||
|
return toret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *mkstr(ptrlen pl)
|
||||||
|
{
|
||||||
|
char *p = snewn(pl.len + 1, char);
|
||||||
|
memcpy(p, pl.ptr, pl.len);
|
||||||
|
p[pl.len] = '\0';
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool strstartswith(const char *s, const char *t)
|
||||||
|
{
|
||||||
|
return !strncmp(s, t, strlen(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool strendswith(const char *s, const char *t)
|
||||||
|
{
|
||||||
|
size_t slen = strlen(s), tlen = strlen(t);
|
||||||
|
return slen >= tlen && !strcmp(s + (slen - tlen), t);
|
||||||
|
}
|
19
utils/read_file_into.c
Normal file
19
utils/read_file_into.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Read an entire file into a BinarySink.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
bool read_file_into(BinarySink *bs, FILE *fp)
|
||||||
|
{
|
||||||
|
char buf[4096];
|
||||||
|
while (1) {
|
||||||
|
size_t retd = fread(buf, 1, sizeof(buf), fp);
|
||||||
|
if (retd == 0)
|
||||||
|
return !ferror(fp);
|
||||||
|
put_data(bs, buf, retd);
|
||||||
|
}
|
||||||
|
}
|
20
utils/seat_connection_fatal.c
Normal file
20
utils/seat_connection_fatal.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Wrapper function for the connection_fatal() method of a Seat,
|
||||||
|
* providing printf-style formatting.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void seat_connection_fatal(Seat *seat, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char *msg;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
msg = dupvprintf(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
seat->vt->connection_fatal(seat, msg);
|
||||||
|
sfree(msg); /* if we return */
|
||||||
|
}
|
||||||
|
|
14
utils/sk_free_peer_info.c
Normal file
14
utils/sk_free_peer_info.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* Free a SocketPeerInfo, and everything that dangles off it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void sk_free_peer_info(SocketPeerInfo *pi)
|
||||||
|
{
|
||||||
|
if (pi) {
|
||||||
|
sfree((char *)pi->addr_text);
|
||||||
|
sfree((char *)pi->log_text);
|
||||||
|
sfree(pi);
|
||||||
|
}
|
||||||
|
}
|
42
utils/smemclr.c
Normal file
42
utils/smemclr.c
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Securely wipe memory.
|
||||||
|
*
|
||||||
|
* The actual wiping is no different from what memset would do: the
|
||||||
|
* point of 'securely' is to try to be sure over-clever compilers
|
||||||
|
* won't optimise away memsets on variables that are about to be freed
|
||||||
|
* or go out of scope. See
|
||||||
|
* https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html
|
||||||
|
*
|
||||||
|
* Some platforms (e.g. Windows) may provide their own version of this
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
void smemclr(void *b, size_t n)
|
||||||
|
{
|
||||||
|
volatile char *vp;
|
||||||
|
|
||||||
|
if (b && n > 0) {
|
||||||
|
/*
|
||||||
|
* Zero out the memory.
|
||||||
|
*/
|
||||||
|
memset(b, 0, n);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Perform a volatile access to the object, forcing the
|
||||||
|
* compiler to admit that the previous memset was important.
|
||||||
|
*
|
||||||
|
* This while loop should in practice run for zero iterations
|
||||||
|
* (since we know we just zeroed the object out), but in
|
||||||
|
* theory (as far as the compiler knows) it might range over
|
||||||
|
* the whole object. (If we had just written, say, '*vp =
|
||||||
|
* *vp;', a compiler could in principle have 'helpfully'
|
||||||
|
* optimised the memset into only zeroing out the first byte.
|
||||||
|
* This should be robust.)
|
||||||
|
*/
|
||||||
|
vp = b;
|
||||||
|
while (*vp) vp++;
|
||||||
|
}
|
||||||
|
}
|
25
utils/smemeq.c
Normal file
25
utils/smemeq.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Compare two fixed-size regions of memory, in a crypto-safe way,
|
||||||
|
* i.e. without timing or cache side channels indicating anything
|
||||||
|
* about what the answer was or where the first difference (if any)
|
||||||
|
* might have been.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
bool smemeq(const void *av, const void *bv, size_t len)
|
||||||
|
{
|
||||||
|
const unsigned char *a = (const unsigned char *)av;
|
||||||
|
const unsigned char *b = (const unsigned char *)bv;
|
||||||
|
unsigned val = 0;
|
||||||
|
|
||||||
|
while (len-- > 0) {
|
||||||
|
val |= *a++ ^ *b++;
|
||||||
|
}
|
||||||
|
/* Now val is 0 iff we want to return 1, and in the range
|
||||||
|
* 0x01..0xFF iff we want to return 0. So subtracting from 0x100
|
||||||
|
* will clear bit 8 iff we want to return 0, and leave it set iff
|
||||||
|
* we want to return 1, so then we can just shift down. */
|
||||||
|
return (0x100 - val) >> 8;
|
||||||
|
}
|
27
utils/ssh2_pick_fingerprint.c
Normal file
27
utils/ssh2_pick_fingerprint.c
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Choose an SSH-2 fingerprint type, out of an array of possible ones.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "ssh.h"
|
||||||
|
|
||||||
|
FingerprintType ssh2_pick_fingerprint(
|
||||||
|
char **fingerprints, FingerprintType preferred_type)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Keys are either SSH-2, in which case we have all fingerprint
|
||||||
|
* types, or SSH-1, in which case we have only MD5. So we return
|
||||||
|
* the default type if we can, or MD5 if that's all we have; no
|
||||||
|
* need for a fully general preference-list system.
|
||||||
|
*/
|
||||||
|
FingerprintType fptype = fingerprints[preferred_type] ?
|
||||||
|
preferred_type : SSH_FPTYPE_MD5;
|
||||||
|
assert(fingerprints[fptype]);
|
||||||
|
return fptype;
|
||||||
|
}
|
||||||
|
|
||||||
|
FingerprintType ssh2_pick_default_fingerprint(char **fingerprints)
|
||||||
|
{
|
||||||
|
return ssh2_pick_fingerprint(fingerprints, SSH_FPTYPE_DEFAULT);
|
||||||
|
}
|
118
utils/strbuf.c
Normal file
118
utils/strbuf.c
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Functions to work with strbufs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
struct strbuf_impl {
|
||||||
|
size_t size;
|
||||||
|
struct strbuf visible;
|
||||||
|
bool nm; /* true if we insist on non-moving buffer resizes */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define STRBUF_SET_UPTR(buf) \
|
||||||
|
((buf)->visible.u = (unsigned char *)(buf)->visible.s)
|
||||||
|
#define STRBUF_SET_PTR(buf, ptr) \
|
||||||
|
((buf)->visible.s = (ptr), STRBUF_SET_UPTR(buf))
|
||||||
|
|
||||||
|
void *strbuf_append(strbuf *buf_o, size_t len)
|
||||||
|
{
|
||||||
|
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||||
|
char *toret;
|
||||||
|
sgrowarray_general(
|
||||||
|
buf->visible.s, buf->size, buf->visible.len + 1, len, buf->nm);
|
||||||
|
STRBUF_SET_UPTR(buf);
|
||||||
|
toret = buf->visible.s + buf->visible.len;
|
||||||
|
buf->visible.len += len;
|
||||||
|
buf->visible.s[buf->visible.len] = '\0';
|
||||||
|
return toret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_shrink_to(strbuf *buf, size_t new_len)
|
||||||
|
{
|
||||||
|
assert(new_len <= buf->len);
|
||||||
|
buf->len = new_len;
|
||||||
|
buf->s[buf->len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove)
|
||||||
|
{
|
||||||
|
assert(amount_to_remove <= buf->len);
|
||||||
|
buf->len -= amount_to_remove;
|
||||||
|
buf->s[buf->len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool strbuf_chomp(strbuf *buf, char char_to_remove)
|
||||||
|
{
|
||||||
|
if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) {
|
||||||
|
strbuf_shrink_by(buf, 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void strbuf_BinarySink_write(
|
||||||
|
BinarySink *bs, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
strbuf *buf_o = BinarySink_DOWNCAST(bs, strbuf);
|
||||||
|
memcpy(strbuf_append(buf_o, len), data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static strbuf *strbuf_new_general(bool nm)
|
||||||
|
{
|
||||||
|
struct strbuf_impl *buf = snew(struct strbuf_impl);
|
||||||
|
BinarySink_INIT(&buf->visible, strbuf_BinarySink_write);
|
||||||
|
buf->visible.len = 0;
|
||||||
|
buf->size = 512;
|
||||||
|
buf->nm = nm;
|
||||||
|
STRBUF_SET_PTR(buf, snewn(buf->size, char));
|
||||||
|
*buf->visible.s = '\0';
|
||||||
|
return &buf->visible;
|
||||||
|
}
|
||||||
|
strbuf *strbuf_new(void) { return strbuf_new_general(false); }
|
||||||
|
strbuf *strbuf_new_nm(void) { return strbuf_new_general(true); }
|
||||||
|
void strbuf_free(strbuf *buf_o)
|
||||||
|
{
|
||||||
|
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||||
|
if (buf->visible.s) {
|
||||||
|
smemclr(buf->visible.s, buf->size);
|
||||||
|
sfree(buf->visible.s);
|
||||||
|
}
|
||||||
|
sfree(buf);
|
||||||
|
}
|
||||||
|
char *strbuf_to_str(strbuf *buf_o)
|
||||||
|
{
|
||||||
|
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||||
|
char *ret = buf->visible.s;
|
||||||
|
sfree(buf);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void strbuf_catfv(strbuf *buf_o, const char *fmt, va_list ap)
|
||||||
|
{
|
||||||
|
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||||
|
STRBUF_SET_PTR(buf, dupvprintf_inner(buf->visible.s, buf->visible.len,
|
||||||
|
&buf->size, fmt, ap));
|
||||||
|
buf->visible.len += strlen(buf->visible.s + buf->visible.len);
|
||||||
|
}
|
||||||
|
void strbuf_catf(strbuf *buf_o, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
strbuf_catfv(buf_o, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
strbuf *strbuf_new_for_agent_query(void)
|
||||||
|
{
|
||||||
|
strbuf *buf = strbuf_new();
|
||||||
|
strbuf_append(buf, 4);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
void strbuf_finalise_agent_query(strbuf *buf_o)
|
||||||
|
{
|
||||||
|
struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible);
|
||||||
|
assert(buf->visible.len >= 5);
|
||||||
|
PUT_32BIT_MSB_FIRST(buf->visible.u, buf->visible.len - 4);
|
||||||
|
}
|
21
utils/string_length_for_printf.c
Normal file
21
utils/string_length_for_printf.c
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Convert a size_t value to int, by saturating it at INT_MAX. Useful
|
||||||
|
* if you want to use the printf idiom "%.*s", where the '*' precision
|
||||||
|
* specifier expects an int in the variadic argument list, but what
|
||||||
|
* you have is not an int but a size_t. This method of converting to
|
||||||
|
* int will at least do something _safe_ with overlong values, even if
|
||||||
|
* (due to the limitation of printf itself) the whole string still
|
||||||
|
* won't be printed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
int string_length_for_printf(size_t s)
|
||||||
|
{
|
||||||
|
if (s > INT_MAX)
|
||||||
|
return INT_MAX;
|
||||||
|
return s;
|
||||||
|
}
|
12
utils/utils.h
Normal file
12
utils/utils.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Internal header to the utils subdirectory, for definitions shared
|
||||||
|
* between the library implementations but not intended to be exposed
|
||||||
|
* further than that.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void dputs(const char *);
|
||||||
|
|
||||||
|
char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr,
|
||||||
|
const char *fmt, va_list ap);
|
||||||
|
|
||||||
|
const char *host_strchr_internal(const char *s, const char *set, bool first);
|
116
utils/validate_manual_hostkey.c
Normal file
116
utils/validate_manual_hostkey.c
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Validate a manual host key specification (either entered in the
|
||||||
|
* GUI, or via -hostkey). If valid, we return true, and update 'key'
|
||||||
|
* to contain a canonicalised version of the key string in 'key'
|
||||||
|
* (which is guaranteed to take up at most as much space as the
|
||||||
|
* original version), suitable for putting into the Conf. If not
|
||||||
|
* valid, we return false.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
#define BASE64_CHARS_NOEQ \
|
||||||
|
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/"
|
||||||
|
#define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "="
|
||||||
|
|
||||||
|
bool validate_manual_hostkey(char *key)
|
||||||
|
{
|
||||||
|
char *p, *q, *r, *s;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Step through the string word by word, looking for a word that's
|
||||||
|
* in one of the formats we like.
|
||||||
|
*/
|
||||||
|
p = key;
|
||||||
|
while ((p += strspn(p, " \t"))[0]) {
|
||||||
|
q = p;
|
||||||
|
p += strcspn(p, " \t");
|
||||||
|
if (*p) *p++ = '\0';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now q is our word.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (strstartswith(q, "SHA256:")) {
|
||||||
|
/* Test for a valid SHA256 key fingerprint. */
|
||||||
|
r = q + 7;
|
||||||
|
if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = q;
|
||||||
|
if (strstartswith(r, "MD5:"))
|
||||||
|
r += 4;
|
||||||
|
if (strlen(r) == 16*3 - 1 &&
|
||||||
|
r[strspn(r, "0123456789abcdefABCDEF:")] == 0) {
|
||||||
|
/*
|
||||||
|
* Test for a valid MD5 key fingerprint. Check the colons
|
||||||
|
* are in the right places, and if so, return the same
|
||||||
|
* fingerprint canonicalised into lowercase.
|
||||||
|
*/
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 16; i++)
|
||||||
|
if (r[3*i] == ':' || r[3*i+1] == ':')
|
||||||
|
goto not_fingerprint; /* sorry */
|
||||||
|
for (i = 0; i < 15; i++)
|
||||||
|
if (r[3*i+2] != ':')
|
||||||
|
goto not_fingerprint; /* sorry */
|
||||||
|
for (i = 0; i < 16*3 - 1; i++)
|
||||||
|
key[i] = tolower(r[i]);
|
||||||
|
key[16*3 - 1] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
not_fingerprint:;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Before we check for a public-key blob, trim newlines out of
|
||||||
|
* the middle of the word, in case someone's managed to paste
|
||||||
|
* in a public-key blob _with_ them.
|
||||||
|
*/
|
||||||
|
for (r = s = q; *r; r++)
|
||||||
|
if (*r != '\n' && *r != '\r')
|
||||||
|
*s++ = *r;
|
||||||
|
*s = '\0';
|
||||||
|
|
||||||
|
if (strlen(q) % 4 == 0 && strlen(q) > 2*4 &&
|
||||||
|
q[strspn(q, BASE64_CHARS_ALL)] == 0) {
|
||||||
|
/*
|
||||||
|
* Might be a base64-encoded SSH-2 public key blob. Check
|
||||||
|
* that it starts with a sensible algorithm string. No
|
||||||
|
* canonicalisation is necessary for this string type.
|
||||||
|
*
|
||||||
|
* The algorithm string must be at most 64 characters long
|
||||||
|
* (RFC 4251 section 6).
|
||||||
|
*/
|
||||||
|
unsigned char decoded[6];
|
||||||
|
unsigned alglen;
|
||||||
|
int minlen;
|
||||||
|
int len = 0;
|
||||||
|
|
||||||
|
len += base64_decode_atom(q, decoded+len);
|
||||||
|
if (len < 3)
|
||||||
|
goto not_ssh2_blob; /* sorry */
|
||||||
|
len += base64_decode_atom(q+4, decoded+len);
|
||||||
|
if (len < 4)
|
||||||
|
goto not_ssh2_blob; /* sorry */
|
||||||
|
|
||||||
|
alglen = GET_32BIT_MSB_FIRST(decoded);
|
||||||
|
if (alglen > 64)
|
||||||
|
goto not_ssh2_blob; /* sorry */
|
||||||
|
|
||||||
|
minlen = ((alglen + 4) + 2) / 3;
|
||||||
|
if (strlen(q) < minlen)
|
||||||
|
goto not_ssh2_blob; /* sorry */
|
||||||
|
|
||||||
|
strcpy(key, q);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
not_ssh2_blob:;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
31
utils/write_c_string_literal.c
Normal file
31
utils/write_c_string_literal.c
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Write data to a file in the form of a C string literal, with any
|
||||||
|
* non-printable-ASCII character escaped appropriately.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
#include "misc.h"
|
||||||
|
|
||||||
|
void write_c_string_literal(FILE *fp, ptrlen str)
|
||||||
|
{
|
||||||
|
for (const char *p = str.ptr; p < (const char *)str.ptr + str.len; p++) {
|
||||||
|
char c = *p;
|
||||||
|
|
||||||
|
if (c == '\n')
|
||||||
|
fputs("\\n", fp);
|
||||||
|
else if (c == '\r')
|
||||||
|
fputs("\\r", fp);
|
||||||
|
else if (c == '\t')
|
||||||
|
fputs("\\t", fp);
|
||||||
|
else if (c == '\b')
|
||||||
|
fputs("\\b", fp);
|
||||||
|
else if (c == '\\')
|
||||||
|
fputs("\\\\", fp);
|
||||||
|
else if (c == '"')
|
||||||
|
fputs("\\\"", fp);
|
||||||
|
else if (c >= 32 && c <= 126)
|
||||||
|
fputc(c, fp);
|
||||||
|
else
|
||||||
|
fprintf(fp, "\\%03o", (unsigned char)c);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,38 @@
|
|||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
add_platform_sources_to_library(utils
|
add_platform_sources_to_library(utils
|
||||||
wincapi.c winutils.c winucs.c winmisc.c winmiscs.c wintime.c windefs.c
|
utils/arm_arch_queries.c
|
||||||
winsecur.c)
|
utils/capi.c
|
||||||
|
utils/defaults.c
|
||||||
|
utils/dll_hijacking_protection.c
|
||||||
|
utils/dputs.c
|
||||||
|
utils/escape_registry_key.c
|
||||||
|
utils/filename.c
|
||||||
|
utils/fontspec.c
|
||||||
|
utils/getdlgitemtext_alloc.c
|
||||||
|
utils/get_username.c
|
||||||
|
utils/is_console_handle.c
|
||||||
|
utils/load_system32_dll.c
|
||||||
|
utils/ltime.c
|
||||||
|
utils/makedlgitemborderless.c
|
||||||
|
utils/message_box.c
|
||||||
|
utils/minefield.c
|
||||||
|
utils/open_for_write_would_lose_data.c
|
||||||
|
utils/pgp_fingerprints_msgbox.c
|
||||||
|
utils/platform_get_x_display.c
|
||||||
|
utils/registry_get_string.c
|
||||||
|
utils/request_file.c
|
||||||
|
utils/security.c
|
||||||
|
utils/split_into_argv.c
|
||||||
|
utils/version.c
|
||||||
|
utils/win_strerror.c
|
||||||
|
winucs.c)
|
||||||
|
if(NOT HAVE_SECUREZEROMEMORY)
|
||||||
|
add_platform_sources_to_library(utils ../utils/smemclr.c)
|
||||||
|
endif()
|
||||||
|
if(NOT HAVE_STRTOUMAX)
|
||||||
|
add_platform_sources_to_library(utils utils/strtoumax.c)
|
||||||
|
endif()
|
||||||
add_platform_sources_to_library(eventloop
|
add_platform_sources_to_library(eventloop
|
||||||
wincliloop.c winhandl.c)
|
wincliloop.c winhandl.c)
|
||||||
add_platform_sources_to_library(console
|
add_platform_sources_to_library(console
|
||||||
|
39
windows/utils/arm_arch_queries.c
Normal file
39
windows/utils/arm_arch_queries.c
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Windows implementation of the OS query functions that detect Arm
|
||||||
|
* architecture extensions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
#if !(defined _M_ARM || defined _M_ARM64)
|
||||||
|
/*
|
||||||
|
* For non-Arm, stub out these functions. This module shouldn't be
|
||||||
|
* _called_ in that situation anyway, but it will still be compiled
|
||||||
|
* (because that's easier than getting CMake to identify the
|
||||||
|
* architecture in all cases).
|
||||||
|
*/
|
||||||
|
#define IsProcessorFeaturePresent(...) false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool platform_aes_hw_available(void)
|
||||||
|
{
|
||||||
|
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool platform_sha256_hw_available(void)
|
||||||
|
{
|
||||||
|
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool platform_sha1_hw_available(void)
|
||||||
|
{
|
||||||
|
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool platform_sha512_hw_available(void)
|
||||||
|
{
|
||||||
|
/* As of 2020-12-24, as far as I can tell from docs.microsoft.com,
|
||||||
|
* Windows on Arm does not yet provide a PF_ARM_V8_* flag for the
|
||||||
|
* SHA-512 architecture extension. */
|
||||||
|
return false;
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* wincapi.c: implementation of wincapi.h.
|
* windows/utils/capi.c: implementation of wincapi.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "putty.h"
|
#include "putty.h"
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* windefs.c: default settings that are specific to Windows.
|
* windows/utils/defaults.c: default settings that are specific to Windows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "putty.h"
|
#include "putty.h"
|
43
windows/utils/dll_hijacking_protection.c
Normal file
43
windows/utils/dll_hijacking_protection.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* If the OS provides it, call SetDefaultDllDirectories() to prevent
|
||||||
|
* DLLs from being loaded from the directory containing our own
|
||||||
|
* binary, and instead only load from system32.
|
||||||
|
*
|
||||||
|
* This is a protection against hijacking attacks, if someone runs
|
||||||
|
* PuTTY directly from their web browser's download directory having
|
||||||
|
* previously been enticed into clicking on an unwise link that
|
||||||
|
* downloaded a malicious DLL to the same directory under one of
|
||||||
|
* various magic names that seem to be things that standard Windows
|
||||||
|
* DLLs delegate to.
|
||||||
|
*
|
||||||
|
* It shouldn't break deliberate loading of user-provided DLLs such as
|
||||||
|
* GSSAPI providers, because those are specified by their full
|
||||||
|
* pathname by the user-provided configuration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void dll_hijacking_protection(void)
|
||||||
|
{
|
||||||
|
static HMODULE kernel32_module;
|
||||||
|
DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD));
|
||||||
|
|
||||||
|
if (!kernel32_module) {
|
||||||
|
kernel32_module = load_system32_dll("kernel32.dll");
|
||||||
|
#if !HAVE_SETDEFAULTDLLDIRECTORIES
|
||||||
|
/* For older Visual Studio, this function isn't available in
|
||||||
|
* the header files to type-check */
|
||||||
|
GET_WINDOWS_FUNCTION_NO_TYPECHECK(
|
||||||
|
kernel32_module, SetDefaultDllDirectories);
|
||||||
|
#else
|
||||||
|
GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_SetDefaultDllDirectories) {
|
||||||
|
/* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified
|
||||||
|
* directories only */
|
||||||
|
p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 |
|
||||||
|
LOAD_LIBRARY_SEARCH_USER_DIRS);
|
||||||
|
}
|
||||||
|
}
|
37
windows/utils/dputs.c
Normal file
37
windows/utils/dputs.c
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of dputs() for Windows.
|
||||||
|
*
|
||||||
|
* The debug messages are written to STD_OUTPUT_HANDLE, except that
|
||||||
|
* first it has to make sure that handle _exists_, by calling
|
||||||
|
* AllocConsole first if necessary.
|
||||||
|
*
|
||||||
|
* They also go into a file called debug.log.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
#include "utils/utils.h"
|
||||||
|
|
||||||
|
static FILE *debug_fp = NULL;
|
||||||
|
static HANDLE debug_hdl = INVALID_HANDLE_VALUE;
|
||||||
|
static int debug_got_console = 0;
|
||||||
|
|
||||||
|
void dputs(const char *buf)
|
||||||
|
{
|
||||||
|
DWORD dw;
|
||||||
|
|
||||||
|
if (!debug_got_console) {
|
||||||
|
if (AllocConsole()) {
|
||||||
|
debug_got_console = 1;
|
||||||
|
debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!debug_fp) {
|
||||||
|
debug_fp = fopen("debug.log", "w");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug_hdl != INVALID_HANDLE_VALUE) {
|
||||||
|
WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL);
|
||||||
|
}
|
||||||
|
fputs(buf, debug_fp);
|
||||||
|
fflush(debug_fp);
|
||||||
|
}
|
48
windows/utils/escape_registry_key.c
Normal file
48
windows/utils/escape_registry_key.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Escaping/unescaping functions to translate between a saved session
|
||||||
|
* name, and the key name used to represent it in the Registry area
|
||||||
|
* where we store saved sessions.
|
||||||
|
*
|
||||||
|
* The basic technique is to %-escape characters we can't use in
|
||||||
|
* Registry keys.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void escape_registry_key(const char *in, strbuf *out)
|
||||||
|
{
|
||||||
|
bool candot = false;
|
||||||
|
static const char hex[16] = "0123456789ABCDEF";
|
||||||
|
|
||||||
|
while (*in) {
|
||||||
|
if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' ||
|
||||||
|
*in == '%' || *in < ' ' || *in > '~' || (*in == '.'
|
||||||
|
&& !candot)) {
|
||||||
|
put_byte(out, '%');
|
||||||
|
put_byte(out, hex[((unsigned char) *in) >> 4]);
|
||||||
|
put_byte(out, hex[((unsigned char) *in) & 15]);
|
||||||
|
} else
|
||||||
|
put_byte(out, *in);
|
||||||
|
in++;
|
||||||
|
candot = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void unescape_registry_key(const char *in, strbuf *out)
|
||||||
|
{
|
||||||
|
while (*in) {
|
||||||
|
if (*in == '%' && in[1] && in[2]) {
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
i = in[1] - '0';
|
||||||
|
i -= (i > 9 ? 7 : 0);
|
||||||
|
j = in[2] - '0';
|
||||||
|
j -= (j > 9 ? 7 : 0);
|
||||||
|
|
||||||
|
put_byte(out, (i << 4) + j);
|
||||||
|
in += 3;
|
||||||
|
} else {
|
||||||
|
put_byte(out, *in++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
windows/utils/filename.c
Normal file
54
windows/utils/filename.c
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of Filename for Windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
Filename *filename_from_str(const char *str)
|
||||||
|
{
|
||||||
|
Filename *ret = snew(Filename);
|
||||||
|
ret->path = dupstr(str);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Filename *filename_copy(const Filename *fn)
|
||||||
|
{
|
||||||
|
return filename_from_str(fn->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *filename_to_str(const Filename *fn)
|
||||||
|
{
|
||||||
|
return fn->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool filename_equal(const Filename *f1, const Filename *f2)
|
||||||
|
{
|
||||||
|
return !strcmp(f1->path, f2->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool filename_is_null(const Filename *fn)
|
||||||
|
{
|
||||||
|
return !*fn->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
void filename_free(Filename *fn)
|
||||||
|
{
|
||||||
|
sfree(fn->path);
|
||||||
|
sfree(fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
void filename_serialise(BinarySink *bs, const Filename *f)
|
||||||
|
{
|
||||||
|
put_asciz(bs, f->path);
|
||||||
|
}
|
||||||
|
Filename *filename_deserialise(BinarySource *src)
|
||||||
|
{
|
||||||
|
return filename_from_str(get_asciz(src));
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename_char_sanitise(char c)
|
||||||
|
{
|
||||||
|
if (strchr("<>:\"/\\|?*", c))
|
||||||
|
return '.';
|
||||||
|
return c;
|
||||||
|
}
|
43
windows/utils/fontspec.c
Normal file
43
windows/utils/fontspec.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of FontSpec for Windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
FontSpec *fontspec_new(const char *name, bool bold, int height, int charset)
|
||||||
|
{
|
||||||
|
FontSpec *f = snew(FontSpec);
|
||||||
|
f->name = dupstr(name);
|
||||||
|
f->isbold = bold;
|
||||||
|
f->height = height;
|
||||||
|
f->charset = charset;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FontSpec *fontspec_copy(const FontSpec *f)
|
||||||
|
{
|
||||||
|
return fontspec_new(f->name, f->isbold, f->height, f->charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fontspec_free(FontSpec *f)
|
||||||
|
{
|
||||||
|
sfree(f->name);
|
||||||
|
sfree(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fontspec_serialise(BinarySink *bs, FontSpec *f)
|
||||||
|
{
|
||||||
|
put_asciz(bs, f->name);
|
||||||
|
put_uint32(bs, f->isbold);
|
||||||
|
put_uint32(bs, f->height);
|
||||||
|
put_uint32(bs, f->charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
FontSpec *fontspec_deserialise(BinarySource *src)
|
||||||
|
{
|
||||||
|
const char *name = get_asciz(src);
|
||||||
|
unsigned isbold = get_uint32(src);
|
||||||
|
unsigned height = get_uint32(src);
|
||||||
|
unsigned charset = get_uint32(src);
|
||||||
|
return fontspec_new(name, isbold, height, charset);
|
||||||
|
}
|
77
windows/utils/get_username.c
Normal file
77
windows/utils/get_username.c
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of get_username() for Windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
#ifndef SECURITY_WIN32
|
||||||
|
#define SECURITY_WIN32
|
||||||
|
#endif
|
||||||
|
#include <security.h>
|
||||||
|
|
||||||
|
char *get_username(void)
|
||||||
|
{
|
||||||
|
DWORD namelen;
|
||||||
|
char *user;
|
||||||
|
bool got_username = false;
|
||||||
|
DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA,
|
||||||
|
(EXTENDED_NAME_FORMAT, LPSTR, PULONG));
|
||||||
|
|
||||||
|
{
|
||||||
|
static bool tried_usernameex = false;
|
||||||
|
if (!tried_usernameex) {
|
||||||
|
/* Not available on Win9x, so load dynamically */
|
||||||
|
HMODULE secur32 = load_system32_dll("secur32.dll");
|
||||||
|
/* If MIT Kerberos is installed, the following call to
|
||||||
|
GET_WINDOWS_FUNCTION makes Windows implicitly load
|
||||||
|
sspicli.dll WITHOUT proper path sanitizing, so better
|
||||||
|
load it properly before */
|
||||||
|
HMODULE sspicli = load_system32_dll("sspicli.dll");
|
||||||
|
(void)sspicli; /* squash compiler warning about unused variable */
|
||||||
|
GET_WINDOWS_FUNCTION(secur32, GetUserNameExA);
|
||||||
|
tried_usernameex = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_GetUserNameExA) {
|
||||||
|
/*
|
||||||
|
* If available, use the principal -- this avoids the problem
|
||||||
|
* that the local username is case-insensitive but Kerberos
|
||||||
|
* usernames are case-sensitive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Get the length */
|
||||||
|
namelen = 0;
|
||||||
|
(void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen);
|
||||||
|
|
||||||
|
user = snewn(namelen, char);
|
||||||
|
got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen);
|
||||||
|
if (got_username) {
|
||||||
|
char *p = strchr(user, '@');
|
||||||
|
if (p) *p = 0;
|
||||||
|
} else {
|
||||||
|
sfree(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!got_username) {
|
||||||
|
/* Fall back to local user name */
|
||||||
|
namelen = 0;
|
||||||
|
if (!GetUserName(NULL, &namelen)) {
|
||||||
|
/*
|
||||||
|
* Apparently this doesn't work at least on Windows XP SP2.
|
||||||
|
* Thus assume a maximum of 256. It will fail again if it
|
||||||
|
* doesn't fit.
|
||||||
|
*/
|
||||||
|
namelen = 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
user = snewn(namelen, char);
|
||||||
|
got_username = GetUserName(user, &namelen);
|
||||||
|
if (!got_username) {
|
||||||
|
sfree(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return got_username ? user : NULL;
|
||||||
|
}
|
20
windows/utils/getdlgitemtext_alloc.c
Normal file
20
windows/utils/getdlgitemtext_alloc.c
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Handy wrapper around GetDlgItemText which doesn't make you invent
|
||||||
|
* an arbitrary length limit on the output string. Returned string is
|
||||||
|
* dynamically allocated; caller must free.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *GetDlgItemText_alloc(HWND hwnd, int id)
|
||||||
|
{
|
||||||
|
char *ret = NULL;
|
||||||
|
size_t size = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
sgrowarray_nm(ret, size, size);
|
||||||
|
GetDlgItemText(hwnd, id, ret, size);
|
||||||
|
} while (!memchr(ret, '\0', size-1));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
13
windows/utils/is_console_handle.c
Normal file
13
windows/utils/is_console_handle.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* Determine whether a Windows HANDLE points at a console device.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
bool is_console_handle(HANDLE handle)
|
||||||
|
{
|
||||||
|
DWORD ignored_output;
|
||||||
|
if (GetConsoleMode(handle, &ignored_output))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
26
windows/utils/load_system32_dll.c
Normal file
26
windows/utils/load_system32_dll.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Wrapper function to load a DLL out of c:\windows\system32 without
|
||||||
|
* going through the full DLL search path. (Hence no attack is
|
||||||
|
* possible by placing a substitute DLL earlier on that path.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
HMODULE load_system32_dll(const char *libname)
|
||||||
|
{
|
||||||
|
static char *sysdir = NULL;
|
||||||
|
static size_t sysdirsize = 0;
|
||||||
|
char *fullpath;
|
||||||
|
HMODULE ret;
|
||||||
|
|
||||||
|
if (!sysdir) {
|
||||||
|
size_t len;
|
||||||
|
while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize)
|
||||||
|
sgrowarray(sysdir, sysdirsize, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
fullpath = dupcat(sysdir, "\\", libname);
|
||||||
|
ret = LoadLibrary(fullpath);
|
||||||
|
sfree(fullpath);
|
||||||
|
return ret;
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* wintime.c - Avoid trouble with time() returning (time_t)-1 on Windows.
|
* Implementation of ltime() that avoids trouble with time() returning
|
||||||
|
* (time_t)-1 on Windows.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "putty.h"
|
#include "putty.h"
|
19
windows/utils/makedlgitemborderless.c
Normal file
19
windows/utils/makedlgitemborderless.c
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Helper function to remove the border around a dialog item such as
|
||||||
|
* a read-only edit control.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void MakeDlgItemBorderless(HWND parent, int id)
|
||||||
|
{
|
||||||
|
HWND child = GetDlgItem(parent, id);
|
||||||
|
LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE);
|
||||||
|
LONG_PTR exstyle = GetWindowLongPtr(child, GWL_EXSTYLE);
|
||||||
|
style &= ~WS_BORDER;
|
||||||
|
exstyle &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE);
|
||||||
|
SetWindowLongPtr(child, GWL_STYLE, style);
|
||||||
|
SetWindowLongPtr(child, GWL_EXSTYLE, exstyle);
|
||||||
|
SetWindowPos(child, NULL, 0, 0, 0, 0,
|
||||||
|
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||||
|
}
|
49
windows/utils/message_box.c
Normal file
49
windows/utils/message_box.c
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Message box with optional context help.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
static HWND message_box_owner;
|
||||||
|
|
||||||
|
/* Callback function to launch context help. */
|
||||||
|
static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo)
|
||||||
|
{
|
||||||
|
const char *context = NULL;
|
||||||
|
#define CHECK_CTX(name) \
|
||||||
|
do { \
|
||||||
|
if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \
|
||||||
|
context = WINHELP_CTX_ ## name; \
|
||||||
|
} while (0)
|
||||||
|
CHECK_CTX(errors_hostkey_absent);
|
||||||
|
CHECK_CTX(errors_hostkey_changed);
|
||||||
|
CHECK_CTX(errors_cantloadkey);
|
||||||
|
CHECK_CTX(option_cleanup);
|
||||||
|
CHECK_CTX(pgp_fingerprints);
|
||||||
|
#undef CHECK_CTX
|
||||||
|
if (context)
|
||||||
|
launch_help(message_box_owner, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
int message_box(HWND owner, LPCTSTR text, LPCTSTR caption,
|
||||||
|
DWORD style, DWORD helpctxid)
|
||||||
|
{
|
||||||
|
MSGBOXPARAMS mbox;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use MessageBoxIndirect() because it allows us to specify a
|
||||||
|
* callback function for the Help button.
|
||||||
|
*/
|
||||||
|
mbox.cbSize = sizeof(mbox);
|
||||||
|
/* Assumes the globals `hinst' and `hwnd' have sensible values. */
|
||||||
|
mbox.hInstance = hinst;
|
||||||
|
mbox.hwndOwner = message_box_owner = owner;
|
||||||
|
mbox.lpfnMsgBoxCallback = &message_box_help_callback;
|
||||||
|
mbox.dwLanguageId = LANG_NEUTRAL;
|
||||||
|
mbox.lpszText = text;
|
||||||
|
mbox.lpszCaption = caption;
|
||||||
|
mbox.dwContextHelpId = helpctxid;
|
||||||
|
mbox.dwStyle = style;
|
||||||
|
if (helpctxid != 0 && has_help()) mbox.dwStyle |= MB_HELP;
|
||||||
|
return MessageBoxIndirect(&mbox);
|
||||||
|
}
|
@ -1,26 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* winmiscs.c: Windows-specific standalone functions. Has the same
|
* 'Minefield' - a crude Windows memory debugger, similar in concept
|
||||||
* relationship to winmisc.c that utils.c does to misc.c, but the
|
* to the old Unix 'Electric Fence'. The main difference is that
|
||||||
* corresponding name 'winutils.c' was already taken.
|
* Electric Fence can be imposed on a program from outside, via
|
||||||
|
* LD_PRELOAD, whereas this has to be included in the program at
|
||||||
|
* compile time with its own cooperation.
|
||||||
|
*
|
||||||
|
* This module provides the Minefield allocator. Actually enabling it
|
||||||
|
* is done by a #define in force when the main utils/memory.c is
|
||||||
|
* compiled.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "putty.h"
|
#include "putty.h"
|
||||||
|
|
||||||
#ifndef NO_SECUREZEROMEMORY
|
|
||||||
/*
|
|
||||||
* Windows implementation of smemclr (see misc.c) using SecureZeroMemory.
|
|
||||||
*/
|
|
||||||
void smemclr(void *b, size_t n) {
|
|
||||||
if (b && n > 0)
|
|
||||||
SecureZeroMemory(b, n);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef MINEFIELD
|
|
||||||
/*
|
|
||||||
* Minefield - a Windows equivalent for Electric Fence
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define PAGESIZE 4096
|
#define PAGESIZE 4096
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -234,52 +225,3 @@ void *minefield_c_realloc(void *p, size_t size)
|
|||||||
minefield_free(p);
|
minefield_free(p);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* MINEFIELD */
|
|
||||||
|
|
||||||
#if !HAVE_STRTOUMAX
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Work around lack of strtoumax in older MSVC libraries
|
|
||||||
*/
|
|
||||||
uintmax_t strtoumax(const char *nptr, char **endptr, int base)
|
|
||||||
{
|
|
||||||
return _strtoui64(nptr, endptr, base);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined _M_ARM || defined _M_ARM64
|
|
||||||
|
|
||||||
bool platform_aes_hw_available(void)
|
|
||||||
{
|
|
||||||
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platform_sha256_hw_available(void)
|
|
||||||
{
|
|
||||||
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platform_sha1_hw_available(void)
|
|
||||||
{
|
|
||||||
return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool platform_sha512_hw_available(void)
|
|
||||||
{
|
|
||||||
/* As of 2020-12-24, as far as I can tell from docs.microsoft.com,
|
|
||||||
* Windows on Arm does not yet provide a PF_ARM_V8_* flag for the
|
|
||||||
* SHA-512 architecture extension. */
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool is_console_handle(HANDLE handle)
|
|
||||||
{
|
|
||||||
DWORD ignored_output;
|
|
||||||
if (GetConsoleMode(handle, &ignored_output))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
38
windows/utils/open_for_write_would_lose_data.c
Normal file
38
windows/utils/open_for_write_would_lose_data.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of open_for_write_would_lose_data for Windows.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
bool open_for_write_would_lose_data(const Filename *fn)
|
||||||
|
{
|
||||||
|
WIN32_FILE_ATTRIBUTE_DATA attrs;
|
||||||
|
if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) {
|
||||||
|
/*
|
||||||
|
* Generally, if we don't identify a specific reason why we
|
||||||
|
* should return true from this function, we return false, and
|
||||||
|
* let the subsequent attempt to open the file for real give a
|
||||||
|
* more useful error message.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE |
|
||||||
|
FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
/*
|
||||||
|
* File is something other than an ordinary disk file, so
|
||||||
|
* opening it for writing will not cause truncation. (It may
|
||||||
|
* not _succeed_ either, but that's not our problem here!)
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) {
|
||||||
|
/*
|
||||||
|
* File is zero-length (or may be a named pipe, which
|
||||||
|
* dwFileAttributes can't tell apart from a regular file), so
|
||||||
|
* opening it for writing won't truncate any data away because
|
||||||
|
* there's nothing to truncate anyway.
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
25
windows/utils/pgp_fingerprints_msgbox.c
Normal file
25
windows/utils/pgp_fingerprints_msgbox.c
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Display the fingerprints of the PGP Master Keys to the user as a
|
||||||
|
* GUI message box.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
void pgp_fingerprints_msgbox(HWND owner)
|
||||||
|
{
|
||||||
|
message_box(
|
||||||
|
owner,
|
||||||
|
"These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
|
||||||
|
"be used to establish a trust path from this executable to another\n"
|
||||||
|
"one. See the manual for more information.\n"
|
||||||
|
"(Note: these fingerprints have nothing to do with SSH!)\n"
|
||||||
|
"\n"
|
||||||
|
"PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
|
||||||
|
" (" PGP_MASTER_KEY_DETAILS "):\n"
|
||||||
|
" " PGP_MASTER_KEY_FP "\n\n"
|
||||||
|
"Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
|
||||||
|
", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
|
||||||
|
" " PGP_PREV_MASTER_KEY_FP,
|
||||||
|
"PGP fingerprints", MB_ICONINFORMATION | MB_OK,
|
||||||
|
HELPCTXID(pgp_fingerprints));
|
||||||
|
}
|
12
windows/utils/platform_get_x_display.c
Normal file
12
windows/utils/platform_get_x_display.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Implementation of platform_get_x_display for Windows, common to all
|
||||||
|
* tools.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *platform_get_x_display(void)
|
||||||
|
{
|
||||||
|
/* We may as well check for DISPLAY in case it's useful. */
|
||||||
|
return dupstr(getenv("DISPLAY"));
|
||||||
|
}
|
43
windows/utils/registry_get_string.c
Normal file
43
windows/utils/registry_get_string.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Self-contained function to try to fetch a single string value from
|
||||||
|
* the Registry, and return it as a dynamically allocated C string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
char *registry_get_string(HKEY root, const char *path, const char *leaf)
|
||||||
|
{
|
||||||
|
HKEY key = root;
|
||||||
|
bool need_close_key = false;
|
||||||
|
char *toret = NULL, *str = NULL;
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
if (RegCreateKey(key, path, &key) != ERROR_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
need_close_key = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD type, size;
|
||||||
|
if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
if (type != REG_SZ)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
str = snewn(size + 1, char);
|
||||||
|
DWORD size_got = size;
|
||||||
|
if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str,
|
||||||
|
&size_got) != ERROR_SUCCESS)
|
||||||
|
goto out;
|
||||||
|
if (type != REG_SZ || size_got > size)
|
||||||
|
goto out;
|
||||||
|
str[size_got] = '\0';
|
||||||
|
|
||||||
|
toret = str;
|
||||||
|
str = NULL;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (need_close_key)
|
||||||
|
RegCloseKey(key);
|
||||||
|
sfree(str);
|
||||||
|
return toret;
|
||||||
|
}
|
71
windows/utils/request_file.c
Normal file
71
windows/utils/request_file.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/*
|
||||||
|
* GetOpenFileName/GetSaveFileName tend to muck around with the process'
|
||||||
|
* working directory on at least some versions of Windows.
|
||||||
|
* Here's a wrapper that gives more control over this, and hides a little
|
||||||
|
* bit of other grottiness.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
|
struct filereq_tag {
|
||||||
|
TCHAR cwd[MAX_PATH];
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* `of' is expected to be initialised with most interesting fields, but
|
||||||
|
* this function does some administrivia. (assume `of' was memset to 0)
|
||||||
|
* save==1 -> GetSaveFileName; save==0 -> GetOpenFileName
|
||||||
|
* `state' is optional.
|
||||||
|
*/
|
||||||
|
bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save)
|
||||||
|
{
|
||||||
|
TCHAR cwd[MAX_PATH]; /* process CWD */
|
||||||
|
bool ret;
|
||||||
|
|
||||||
|
/* Get process CWD */
|
||||||
|
if (preserve) {
|
||||||
|
DWORD r = GetCurrentDirectory(lenof(cwd), cwd);
|
||||||
|
if (r == 0 || r >= lenof(cwd))
|
||||||
|
/* Didn't work, oh well. Stop trying to be clever. */
|
||||||
|
preserve = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the file requester, maybe setting lpstrInitialDir */
|
||||||
|
{
|
||||||
|
#ifdef OPENFILENAME_SIZE_VERSION_400
|
||||||
|
of->lStructSize = OPENFILENAME_SIZE_VERSION_400;
|
||||||
|
#else
|
||||||
|
of->lStructSize = sizeof(*of);
|
||||||
|
#endif
|
||||||
|
of->lpstrInitialDir = (state && state->cwd[0]) ? state->cwd : NULL;
|
||||||
|
/* Actually put up the requester. */
|
||||||
|
ret = save ? GetSaveFileName(of) : GetOpenFileName(of);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get CWD left by requester */
|
||||||
|
if (state) {
|
||||||
|
DWORD r = GetCurrentDirectory(lenof(state->cwd), state->cwd);
|
||||||
|
if (r == 0 || r >= lenof(state->cwd))
|
||||||
|
/* Didn't work, oh well. */
|
||||||
|
state->cwd[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore process CWD */
|
||||||
|
if (preserve)
|
||||||
|
/* If it fails, there's not much we can do. */
|
||||||
|
(void) SetCurrentDirectory(cwd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
filereq *filereq_new(void)
|
||||||
|
{
|
||||||
|
filereq *ret = snew(filereq);
|
||||||
|
ret->cwd[0] = '\0';
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void filereq_free(filereq *state)
|
||||||
|
{
|
||||||
|
sfree(state);
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* winsecur.c: implementation of winsecur.h.
|
* windows/utils/security.c: implementation of winsecur.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
@ -1,197 +1,3 @@
|
|||||||
/*
|
|
||||||
* winutils.c: miscellaneous Windows utilities for GUI apps
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
#include "putty.h"
|
|
||||||
#include "misc.h"
|
|
||||||
|
|
||||||
#ifdef TESTMODE
|
|
||||||
/* Definitions to allow this module to be compiled standalone for testing
|
|
||||||
* split_into_argv(). */
|
|
||||||
#define smalloc malloc
|
|
||||||
#define srealloc realloc
|
|
||||||
#define sfree free
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GetOpenFileName/GetSaveFileName tend to muck around with the process'
|
|
||||||
* working directory on at least some versions of Windows.
|
|
||||||
* Here's a wrapper that gives more control over this, and hides a little
|
|
||||||
* bit of other grottiness.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct filereq_tag {
|
|
||||||
TCHAR cwd[MAX_PATH];
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* `of' is expected to be initialised with most interesting fields, but
|
|
||||||
* this function does some administrivia. (assume `of' was memset to 0)
|
|
||||||
* save==1 -> GetSaveFileName; save==0 -> GetOpenFileName
|
|
||||||
* `state' is optional.
|
|
||||||
*/
|
|
||||||
bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save)
|
|
||||||
{
|
|
||||||
TCHAR cwd[MAX_PATH]; /* process CWD */
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
/* Get process CWD */
|
|
||||||
if (preserve) {
|
|
||||||
DWORD r = GetCurrentDirectory(lenof(cwd), cwd);
|
|
||||||
if (r == 0 || r >= lenof(cwd))
|
|
||||||
/* Didn't work, oh well. Stop trying to be clever. */
|
|
||||||
preserve = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open the file requester, maybe setting lpstrInitialDir */
|
|
||||||
{
|
|
||||||
#ifdef OPENFILENAME_SIZE_VERSION_400
|
|
||||||
of->lStructSize = OPENFILENAME_SIZE_VERSION_400;
|
|
||||||
#else
|
|
||||||
of->lStructSize = sizeof(*of);
|
|
||||||
#endif
|
|
||||||
of->lpstrInitialDir = (state && state->cwd[0]) ? state->cwd : NULL;
|
|
||||||
/* Actually put up the requester. */
|
|
||||||
ret = save ? GetSaveFileName(of) : GetOpenFileName(of);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CWD left by requester */
|
|
||||||
if (state) {
|
|
||||||
DWORD r = GetCurrentDirectory(lenof(state->cwd), state->cwd);
|
|
||||||
if (r == 0 || r >= lenof(state->cwd))
|
|
||||||
/* Didn't work, oh well. */
|
|
||||||
state->cwd[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Restore process CWD */
|
|
||||||
if (preserve)
|
|
||||||
/* If it fails, there's not much we can do. */
|
|
||||||
(void) SetCurrentDirectory(cwd);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
filereq *filereq_new(void)
|
|
||||||
{
|
|
||||||
filereq *ret = snew(filereq);
|
|
||||||
ret->cwd[0] = '\0';
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void filereq_free(filereq *state)
|
|
||||||
{
|
|
||||||
sfree(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Message box with optional context help.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static HWND message_box_owner;
|
|
||||||
|
|
||||||
/* Callback function to launch context help. */
|
|
||||||
static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo)
|
|
||||||
{
|
|
||||||
const char *context = NULL;
|
|
||||||
#define CHECK_CTX(name) \
|
|
||||||
do { \
|
|
||||||
if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \
|
|
||||||
context = WINHELP_CTX_ ## name; \
|
|
||||||
} while (0)
|
|
||||||
CHECK_CTX(errors_hostkey_absent);
|
|
||||||
CHECK_CTX(errors_hostkey_changed);
|
|
||||||
CHECK_CTX(errors_cantloadkey);
|
|
||||||
CHECK_CTX(option_cleanup);
|
|
||||||
CHECK_CTX(pgp_fingerprints);
|
|
||||||
#undef CHECK_CTX
|
|
||||||
if (context)
|
|
||||||
launch_help(message_box_owner, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
int message_box(HWND owner, LPCTSTR text, LPCTSTR caption,
|
|
||||||
DWORD style, DWORD helpctxid)
|
|
||||||
{
|
|
||||||
MSGBOXPARAMS mbox;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We use MessageBoxIndirect() because it allows us to specify a
|
|
||||||
* callback function for the Help button.
|
|
||||||
*/
|
|
||||||
mbox.cbSize = sizeof(mbox);
|
|
||||||
/* Assumes the globals `hinst' and `hwnd' have sensible values. */
|
|
||||||
mbox.hInstance = hinst;
|
|
||||||
mbox.hwndOwner = message_box_owner = owner;
|
|
||||||
mbox.lpfnMsgBoxCallback = &message_box_help_callback;
|
|
||||||
mbox.dwLanguageId = LANG_NEUTRAL;
|
|
||||||
mbox.lpszText = text;
|
|
||||||
mbox.lpszCaption = caption;
|
|
||||||
mbox.dwContextHelpId = helpctxid;
|
|
||||||
mbox.dwStyle = style;
|
|
||||||
if (helpctxid != 0 && has_help()) mbox.dwStyle |= MB_HELP;
|
|
||||||
return MessageBoxIndirect(&mbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Display the fingerprints of the PGP Master Keys to the user.
|
|
||||||
*/
|
|
||||||
void pgp_fingerprints_msgbox(HWND owner)
|
|
||||||
{
|
|
||||||
message_box(
|
|
||||||
owner,
|
|
||||||
"These are the fingerprints of the PuTTY PGP Master Keys. They can\n"
|
|
||||||
"be used to establish a trust path from this executable to another\n"
|
|
||||||
"one. See the manual for more information.\n"
|
|
||||||
"(Note: these fingerprints have nothing to do with SSH!)\n"
|
|
||||||
"\n"
|
|
||||||
"PuTTY Master Key as of " PGP_MASTER_KEY_YEAR
|
|
||||||
" (" PGP_MASTER_KEY_DETAILS "):\n"
|
|
||||||
" " PGP_MASTER_KEY_FP "\n\n"
|
|
||||||
"Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR
|
|
||||||
", " PGP_PREV_MASTER_KEY_DETAILS "):\n"
|
|
||||||
" " PGP_PREV_MASTER_KEY_FP,
|
|
||||||
"PGP fingerprints", MB_ICONINFORMATION | MB_OK,
|
|
||||||
HELPCTXID(pgp_fingerprints));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helper function to remove the border around a dialog item such as
|
|
||||||
* a read-only edit control.
|
|
||||||
*/
|
|
||||||
void MakeDlgItemBorderless(HWND parent, int id)
|
|
||||||
{
|
|
||||||
HWND child = GetDlgItem(parent, id);
|
|
||||||
LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE);
|
|
||||||
LONG_PTR exstyle = GetWindowLongPtr(child, GWL_EXSTYLE);
|
|
||||||
style &= ~WS_BORDER;
|
|
||||||
exstyle &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE);
|
|
||||||
SetWindowLongPtr(child, GWL_STYLE, style);
|
|
||||||
SetWindowLongPtr(child, GWL_EXSTYLE, exstyle);
|
|
||||||
SetWindowPos(child, NULL, 0, 0, 0, 0,
|
|
||||||
SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Handy wrapper around GetDlgItemText which doesn't make you invent
|
|
||||||
* an arbitrary length limit on the output string. Returned string is
|
|
||||||
* dynamically allocated; caller must free.
|
|
||||||
*/
|
|
||||||
char *GetDlgItemText_alloc(HWND hwnd, int id)
|
|
||||||
{
|
|
||||||
char *ret = NULL;
|
|
||||||
size_t size = 0;
|
|
||||||
|
|
||||||
do {
|
|
||||||
sgrowarray_nm(ret, size, size);
|
|
||||||
GetDlgItemText(hwnd, id, ret, size);
|
|
||||||
} while (!memchr(ret, '\0', size-1));
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Split a complete command line into argc/argv, attempting to do it
|
* Split a complete command line into argc/argv, attempting to do it
|
||||||
* exactly the same way the Visual Studio C library would do it (so
|
* exactly the same way the Visual Studio C library would do it (so
|
||||||
@ -210,6 +16,9 @@ char *GetDlgItemText_alloc(HWND hwnd, int id)
|
|||||||
* treat the rest as a raw string, you can. If you don't want to,
|
* treat the rest as a raw string, you can. If you don't want to,
|
||||||
* `argstart' can be safely left NULL.
|
* `argstart' can be safely left NULL.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "putty.h"
|
||||||
|
|
||||||
void split_into_argv(char *cmdline, int *argc, char ***argv,
|
void split_into_argv(char *cmdline, int *argc, char ***argv,
|
||||||
char ***argstart)
|
char ***argstart)
|
||||||
{
|
{
|
12
windows/utils/strtoumax.c
Normal file
12
windows/utils/strtoumax.c
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* Work around lack of strtoumax in older MSVC libraries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
uintmax_t strtoumax(const char *nptr, char **endptr, int base)
|
||||||
|
{
|
||||||
|
return _strtoui64(nptr, endptr, base);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user