X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=misc.c;h=9aff234b9fdde6c04527c2b79db32ef02e1122e4;hb=refs%2Fheads%2F0.68%2Bkerberos-fix;hp=d7c32c49db29fc5d188f489df76dfc7e71c65ea8;hpb=0348f570771a0412609de947778cad9d5b3dca93;p=PuTTY.git diff --git a/misc.c b/misc.c index d7c32c49..9aff234b 100644 --- a/misc.c +++ b/misc.c @@ -9,6 +9,7 @@ #include #include #include "putty.h" +#include "misc.h" /* * Parse a string block size specification. This is approximately a @@ -174,7 +175,7 @@ int main(void) return fails != 0 ? 1 : 0; } /* Stubs to stop the rest of this module causing compile failures. */ -void modalfatalbox(char *fmt, ...) {} +void modalfatalbox(const char *fmt, ...) {} int conf_get_int(Conf *conf, int primary) { return 0; } char *conf_get_str(Conf *conf, int primary) { return NULL; } #endif /* TEST_HOST_STRFOO */ @@ -393,25 +394,23 @@ int toint(unsigned u) * directive we don't know about, we should panic and die rather * than run any risk. */ -char *dupprintf(const char *fmt, ...) +static char *dupvprintf_inner(char *buf, int oldlen, int *oldsize, + const char *fmt, va_list ap) { - char *ret; - va_list ap; - va_start(ap, fmt); - ret = dupvprintf(fmt, ap); - va_end(ap); - return ret; -} -char *dupvprintf(const char *fmt, va_list ap) -{ - char *buf; - int len, size; - - buf = snewn(512, char); - size = 512; + int len, size, newsize; + + assert(*oldsize >= oldlen); + size = *oldsize - oldlen; + if (size == 0) { + size = 512; + newsize = oldlen + size; + buf = sresize(buf, newsize, char); + } else { + newsize = *oldsize; + } while (1) { -#ifdef _WINDOWS +#if defined _WINDOWS && !defined __WINE__ && _MSC_VER < 1900 /* 1900 == VS2015 has real snprintf */ #define vsnprintf _vsnprintf #endif #ifdef va_copy @@ -419,7 +418,7 @@ char *dupvprintf(const char *fmt, va_list ap) * XXX some environments may have this as __va_copy() */ va_list aq; va_copy(aq, ap); - len = vsnprintf(buf, size, fmt, aq); + len = vsnprintf(buf + oldlen, size, fmt, aq); va_end(aq); #else /* Ugh. No va_copy macro, so do something nasty. @@ -430,11 +429,12 @@ char *dupvprintf(const char *fmt, va_list ap) * (indeed, it has been observed to). * XXX the autoconf manual suggests that using memcpy() will give * "maximum portability". */ - len = vsnprintf(buf, size, fmt, ap); + len = vsnprintf(buf + oldlen, size, fmt, ap); #endif if (len >= 0 && len < size) { /* This is the C99-specified criterion for snprintf to have * been completely successful. */ + *oldsize = newsize; return buf; } else if (len > 0) { /* This is the C99 error condition: the returned length is @@ -445,10 +445,67 @@ char *dupvprintf(const char *fmt, va_list ap) * buffer wasn't big enough, so we enlarge it a bit and hope. */ size += 512; } - buf = sresize(buf, size, char); + newsize = oldlen + size; + buf = sresize(buf, newsize, char); } } +char *dupvprintf(const char *fmt, va_list ap) +{ + int 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; +} + +struct strbuf { + char *s; + int len, size; +}; +strbuf *strbuf_new(void) +{ + strbuf *buf = snew(strbuf); + buf->len = 0; + buf->size = 512; + buf->s = snewn(buf->size, char); + *buf->s = '\0'; + return buf; +} +void strbuf_free(strbuf *buf) +{ + sfree(buf->s); + sfree(buf); +} +char *strbuf_str(strbuf *buf) +{ + return buf->s; +} +char *strbuf_to_str(strbuf *buf) +{ + char *ret = buf->s; + sfree(buf); + return ret; +} +void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap) +{ + buf->s = dupvprintf_inner(buf->s, buf->len, &buf->size, fmt, ap); + buf->len += strlen(buf->s + buf->len); +} +void strbuf_catf(strbuf *buf, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + strbuf_catfv(buf, fmt, ap); + va_end(ap); +} + /* * Read an entire line of text from a file. Return a buffer * malloced to be as big as necessary (caller must free). @@ -459,7 +516,7 @@ char *fgetline(FILE *fp) int size = 512, len = 0; while (fgets(ret + len, size - len, fp)) { len += strlen(ret + len); - if (ret[len-1] == '\n') + if (len > 0 && ret[len-1] == '\n') break; /* got a newline, we're done */ size = len + 512; ret = sresize(ret, size, char); @@ -472,12 +529,29 @@ char *fgetline(FILE *fp) return ret; } +/* + * 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. + */ +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; +} + /* ---------------------------------------------------------------------- - * Base64 encoding routine. This is required in public-key writing - * but also in HTTP proxy handling, so it's centralised here. + * Core base64 encoding and decoding routines. */ -void base64_encode_atom(unsigned char *data, int n, char *out) +void base64_encode_atom(const unsigned char *data, int n, char *out) { static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; @@ -501,6 +575,54 @@ void base64_encode_atom(unsigned char *data, int n, char *out) out[3] = '='; } +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; +} + /* ---------------------------------------------------------------------- * Generic routines to deal with send buffers: a linked list of * smallish blocks, with the operations @@ -689,7 +811,7 @@ void *safemalloc(size_t n, size_t size) #else strcpy(str, "Out of memory!"); #endif - modalfatalbox(str); + modalfatalbox("%s", str); } #ifdef MALLOC_LOG if (fp) @@ -731,7 +853,7 @@ void *saferealloc(void *ptr, size_t n, size_t size) #else strcpy(str, "Out of memory!"); #endif - modalfatalbox(str); + modalfatalbox("%s", str); } #ifdef MALLOC_LOG if (fp) @@ -764,9 +886,9 @@ void safefree(void *ptr) */ #ifdef DEBUG -extern void dputs(char *); /* defined in per-platform *misc.c */ +extern void dputs(const char *); /* defined in per-platform *misc.c */ -void debug_printf(char *fmt, ...) +void debug_printf(const char *fmt, ...) { char *buf; va_list ap; @@ -779,15 +901,15 @@ void debug_printf(char *fmt, ...) } -void debug_memdump(void *buf, int len, int L) +void debug_memdump(const void *buf, int len, int L) { int i; - unsigned char *p = buf; + const unsigned char *p = buf; char foo[17]; if (L) { int delta; debug_printf("\t%d (0x%x) bytes:\n", len, len); - delta = 15 & (unsigned long int) p; + delta = 15 & (uintptr_t)p; p -= delta; len += delta; } @@ -875,3 +997,236 @@ void smemclr(void *b, size_t n) { } } #endif + +/* + * 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. + */ +int 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 (strlen(q) == 16*3 - 1 && + q[strspn(q, "0123456789abcdefABCDEF:")] == 0) { + /* + * Might be a 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 (q[3*i] == ':' || q[3*i+1] == ':') + goto not_fingerprint; /* sorry */ + for (i = 0; i < 15; i++) + if (q[3*i+2] != ':') + goto not_fingerprint; /* sorry */ + for (i = 0; i < 16*3 - 1; i++) + key[i] = tolower(q[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, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz+/=")] == 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; +} + +int 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; +} + +int match_ssh_id(int stringlen, const void *string, const char *id) +{ + int idlen = strlen(id); + return (idlen == stringlen && !memcmp(string, id, idlen)); +} + +void *get_ssh_string(int *datalen, const void **data, int *stringlen) +{ + void *ret; + unsigned int len; + + if (*datalen < 4) + return NULL; + len = GET_32BIT_MSB_FIRST((const unsigned char *)*data); + if (*datalen - 4 < len) + return NULL; + ret = (void *)((const char *)*data + 4); + *datalen -= len + 4; + *data = (const char *)*data + len + 4; + *stringlen = len; + return ret; +} + +int get_ssh_uint32(int *datalen, const void **data, unsigned *ret) +{ + if (*datalen < 4) + return FALSE; + *ret = GET_32BIT_MSB_FIRST((const unsigned char *)*data); + *datalen -= 4; + *data = (const char *)*data + 4; + return TRUE; +} + +int strstartswith(const char *s, const char *t) +{ + return !memcmp(s, t, strlen(t)); +} + +int strendswith(const char *s, const char *t) +{ + size_t slen = strlen(s), tlen = strlen(t); + return slen >= tlen && !strcmp(s + (slen - tlen), t); +} + +char *buildinfo(const char *newline) +{ + strbuf *buf = strbuf_new(); + extern const char commitid[]; /* in commitid.c */ + + strbuf_catf(buf, "Build platform: %d-bit %s", + (int)(CHAR_BIT * sizeof(void *)), + BUILDINFO_PLATFORM); + +#ifdef __clang_version__ + strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__); +#elif defined __GNUC__ && defined __VERSION__ + strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__); +#elif defined _MSC_VER + strbuf_catf(buf, "%sCompiler: Visual Studio", newline); +#if _MSC_VER == 1900 + strbuf_catf(buf, " 2015 / MSVC++ 14.0"); +#elif _MSC_VER == 1800 + strbuf_catf(buf, " 2013 / MSVC++ 12.0"); +#elif _MSC_VER == 1700 + strbuf_catf(buf, " 2012 / MSVC++ 11.0"); +#elif _MSC_VER == 1600 + strbuf_catf(buf, " 2010 / MSVC++ 10.0"); +#elif _MSC_VER == 1500 + strbuf_catf(buf, " 2008 / MSVC++ 9.0"); +#elif _MSC_VER == 1400 + strbuf_catf(buf, " 2005 / MSVC++ 8.0"); +#elif _MSC_VER == 1310 + strbuf_catf(buf, " 2003 / MSVC++ 7.1"); +#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 + +#ifdef NO_SECURITY + strbuf_catf(buf, "%sBuild option: NO_SECURITY", 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); +}