Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'utils/validate_manual_hostkey.c')
-rw-r--r--utils/validate_manual_hostkey.c121
1 files changed, 121 insertions, 0 deletions
diff --git a/utils/validate_manual_hostkey.c b/utils/validate_manual_hostkey.c
new file mode 100644
index 00000000..7c5d1b88
--- /dev/null
+++ b/utils/validate_manual_hostkey.c
@@ -0,0 +1,121 @@
+/*
+ * 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 (strspn(r, BASE64_CHARS_NOEQ) == 43) {
+ memmove(key, q, 50); /* 7-char prefix + 43-char base64 */
+ key[50] = '\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 */
+
+ size_t base64_len = strspn(q, BASE64_CHARS_ALL);
+ memmove(key, q, base64_len);
+ key[base64_len] = '\0';
+ return true;
+ }
+ not_ssh2_blob:;
+ }
+
+ return false;
+}