From 19cb1eafd187f29751460f04ce65e293c54a5ba4 Mon Sep 17 00:00:00 2001
From: marha <marha@users.sourceforge.net>
Date: Mon, 14 Apr 2014 23:58:38 +0200
Subject: Added pmink errsock.c winshare.c

---
 tools/plink/errsock.c  |  67 +++++++++++++++
 tools/plink/winshare.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 291 insertions(+)
 create mode 100644 tools/plink/errsock.c
 create mode 100644 tools/plink/winshare.c

(limited to 'tools/plink')

diff --git a/tools/plink/errsock.c b/tools/plink/errsock.c
new file mode 100644
index 000000000..1b3a88a25
--- /dev/null
+++ b/tools/plink/errsock.c
@@ -0,0 +1,67 @@
+/*
+ * A dummy Socket implementation which just holds an error message.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#define DEFINE_PLUG_METHOD_MACROS
+#include "tree234.h"
+#include "putty.h"
+#include "network.h"
+
+typedef struct Socket_error_tag *Error_Socket;
+
+struct Socket_error_tag {
+    const struct socket_function_table *fn;
+    /* the above variable absolutely *must* be the first in this structure */
+
+    char *error;
+    Plug plug;
+};
+
+static Plug sk_error_plug(Socket s, Plug p)
+{
+    Error_Socket ps = (Error_Socket) s;
+    Plug ret = ps->plug;
+    if (p)
+	ps->plug = p;
+    return ret;
+}
+
+static void sk_error_close(Socket s)
+{
+    Error_Socket ps = (Error_Socket) s;
+
+    sfree(ps->error);
+    sfree(ps);
+}
+
+static const char *sk_error_socket_error(Socket s)
+{
+    Error_Socket ps = (Error_Socket) s;
+    return ps->error;
+}
+
+Socket new_error_socket(const char *errmsg, Plug plug)
+{
+    static const struct socket_function_table socket_fn_table = {
+	sk_error_plug,
+	sk_error_close,
+	NULL /* write */,
+	NULL /* write_oob */,
+	NULL /* write_eof */,
+	NULL /* flush */,
+	NULL /* set_frozen */,
+	sk_error_socket_error
+    };
+
+    Error_Socket ret;
+
+    ret = snew(struct Socket_error_tag);
+    ret->fn = &socket_fn_table;
+    ret->plug = plug;
+    ret->error = dupstr(errmsg);
+
+    return (Socket) ret;
+}
diff --git a/tools/plink/winshare.c b/tools/plink/winshare.c
new file mode 100644
index 000000000..17bad46d9
--- /dev/null
+++ b/tools/plink/winshare.c
@@ -0,0 +1,224 @@
+/*
+ * Windows implementation of SSH connection-sharing IPC setup.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#define DEFINE_PLUG_METHOD_MACROS
+#include "tree234.h"
+#include "putty.h"
+#include "network.h"
+#include "proxy.h"
+#include "ssh.h"
+
+#if !defined NO_SECURITY
+
+#include "winsecur.h"
+
+#define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare"
+#define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex"
+
+static char *obfuscate_name(const char *realname)
+{
+    /*
+     * Windows's named pipes all live in the same namespace, so one
+     * user can see what pipes another user has open. This is an
+     * undesirable privacy leak and in particular permits one user to
+     * know what username@host another user is SSHing to, so we
+     * protect that information by using CryptProtectMemory (which
+     * uses a key built in to each user's account).
+     */
+    char *cryptdata;
+    int cryptlen;
+    SHA256_State sha;
+    unsigned char lenbuf[4];
+    unsigned char digest[32];
+    char retbuf[65];
+    int i;
+
+    cryptlen = strlen(realname) + 1;
+    cryptlen += CRYPTPROTECTMEMORY_BLOCK_SIZE - 1;
+    cryptlen /= CRYPTPROTECTMEMORY_BLOCK_SIZE;
+    cryptlen *= CRYPTPROTECTMEMORY_BLOCK_SIZE;
+
+    cryptdata = snewn(cryptlen, char);
+    memset(cryptdata, 0, cryptlen);
+    strcpy(cryptdata, realname);
+
+    /*
+     * CRYPTPROTECTMEMORY_CROSS_PROCESS causes CryptProtectMemory to
+     * use the same key in all processes with this user id, meaning
+     * that the next PuTTY process calling this function with the same
+     * input will get the same data.
+     *
+     * (Contrast with CryptProtectData, which invents a new session
+     * key every time since its API permits returning more data than
+     * was input, so calling _that_ and hashing the output would not
+     * be stable.)
+     *
+     * We don't worry too much if this doesn't work for some reason.
+     * Omitting this step still has _some_ privacy value (in that
+     * another user can test-hash things to confirm guesses as to
+     * where you might be connecting to, but cannot invert SHA-256 in
+     * the absence of any plausible guess). So we don't abort if we
+     * can't call CryptProtectMemory at all, or if it fails.
+     */
+    if (got_crypt())
+        p_CryptProtectMemory(cryptdata, cryptlen,
+                             CRYPTPROTECTMEMORY_CROSS_PROCESS);
+
+    /*
+     * We don't want to give away the length of the hostname either,
+     * so having got it back out of CryptProtectMemory we now hash it.
+     */
+    SHA256_Init(&sha);
+    PUT_32BIT_MSB_FIRST(lenbuf, cryptlen);
+    SHA256_Bytes(&sha, lenbuf, 4);
+    SHA256_Bytes(&sha, cryptdata, cryptlen);
+    SHA256_Final(&sha, digest);
+
+    sfree(cryptdata);
+
+    /*
+     * Finally, make printable.
+     */
+    for (i = 0; i < 32; i++) {
+        sprintf(retbuf + 2*i, "%02x", digest[i]);
+        /* the last of those will also write the trailing NUL */
+    }
+
+    return dupstr(retbuf);
+}
+
+static char *make_name(const char *prefix, const char *name)
+{
+    char *username, *retname;
+
+    username = get_username();
+    retname = dupprintf("%s.%s.%s", prefix, username, name);
+    sfree(username);
+
+    return retname;
+}
+
+Socket new_named_pipe_client(const char *pipename, Plug plug);
+Socket new_named_pipe_listener(const char *pipename, Plug plug);
+
+int platform_ssh_share(const char *pi_name, Conf *conf,
+                       Plug downplug, Plug upplug, Socket *sock,
+                       char **logtext, char **ds_err, char **us_err,
+                       int can_upstream, int can_downstream)
+{
+    char *name, *mutexname, *pipename;
+    HANDLE mutex;
+    Socket retsock;
+    PSECURITY_DESCRIPTOR psd;
+    PACL acl;
+
+    /*
+     * Transform the platform-independent version of the connection
+     * identifier into the obfuscated version we'll use for our
+     * Windows named pipe and mutex. A side effect of doing this is
+     * that it also eliminates any characters illegal in Windows pipe
+     * names.
+     */
+    name = obfuscate_name(pi_name);
+    if (!name) {
+        *logtext = dupprintf("Unable to call CryptProtectMemory: %s",
+                             win_strerror(GetLastError()));
+        return SHARE_NONE;
+    }
+
+    /*
+     * Make a mutex name out of the connection identifier, and lock it
+     * while we decide whether to be upstream or downstream.
+     */
+    {
+        SECURITY_ATTRIBUTES sa;
+
+        mutexname = make_name(CONNSHARE_MUTEX_PREFIX, name);
+        if (!make_private_security_descriptor(MUTEX_ALL_ACCESS,
+                                              &psd, &acl, logtext)) {
+            sfree(mutexname);
+            return SHARE_NONE;
+        }
+
+        memset(&sa, 0, sizeof(sa));
+        sa.nLength = sizeof(sa);
+        sa.lpSecurityDescriptor = psd;
+        sa.bInheritHandle = FALSE;
+
+        mutex = CreateMutex(&sa, FALSE, mutexname);
+
+        if (!mutex) {
+            *logtext = dupprintf("CreateMutex(\"%s\") failed: %s",
+                                 mutexname, win_strerror(GetLastError()));
+            sfree(mutexname);
+            LocalFree(psd);
+            LocalFree(acl);
+            return SHARE_NONE;
+        }
+
+        sfree(mutexname);
+        LocalFree(psd);
+        LocalFree(acl);
+
+        WaitForSingleObject(mutex, INFINITE);
+    }
+
+    pipename = make_name(CONNSHARE_PIPE_PREFIX, name);
+
+    *logtext = NULL;
+
+    if (can_downstream) {
+        retsock = new_named_pipe_client(pipename, downplug);
+        if (sk_socket_error(retsock) == NULL) {
+            sfree(*logtext);
+            *logtext = pipename;
+            *sock = retsock;
+            sfree(name);
+            ReleaseMutex(mutex);
+            CloseHandle(mutex);
+            return SHARE_DOWNSTREAM;
+        }
+        sfree(*ds_err);
+        *ds_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
+        sk_close(retsock);
+    }
+
+    if (can_upstream) {
+        retsock = new_named_pipe_listener(pipename, upplug);
+        if (sk_socket_error(retsock) == NULL) {
+            sfree(*logtext);
+            *logtext = pipename;
+            *sock = retsock;
+            sfree(name);
+            ReleaseMutex(mutex);
+            CloseHandle(mutex);
+            return SHARE_UPSTREAM;
+        }
+        sfree(*us_err);
+        *us_err = dupprintf("%s: %s", pipename, sk_socket_error(retsock));
+        sk_close(retsock);
+    }
+
+    /* One of the above clauses ought to have happened. */
+    assert(*logtext || *ds_err || *us_err);
+
+    sfree(pipename);
+    sfree(name);
+    ReleaseMutex(mutex);
+    CloseHandle(mutex);
+    return SHARE_NONE;
+}
+
+void platform_ssh_share_cleanup(const char *name)
+{
+}
+
+#else /* !defined NO_SECURITY */
+
+#include "noshare.c"
+
+#endif /* !defined NO_SECURITY */
-- 
cgit v1.2.3