123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- /*
- * 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((unsigned char)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;
- }
|