X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/f4ff9455ce1b9e6ccc9c580966c3a2a002bc20d1..8d90b8b27bf086da6245030459ab3e5977313eb0:/misc.c diff --git a/misc.c b/misc.c index 1bd556e6..b4fff666 100644 --- a/misc.c +++ b/misc.c @@ -5,10 +5,152 @@ #include #include #include +#include #include #include #include "putty.h" +/* + * 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. + */ +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; +} + +/* + * 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 + * specified by number (decimal, 0octal, 0xHEX) + * ~ ^ escape + */ +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; +} + +prompts_t *new_prompts(void *frontend) +{ + prompts_t *p = snew(prompts_t); + p->prompts = NULL; + p->n_prompts = 0; + p->frontend = frontend; + 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, int echo) +{ + prompt_t *pr = snew(prompt_t); + pr->prompt = promptstr; + pr->echo = echo; + pr->result = NULL; + pr->resultsize = 0; + p->n_prompts++; + p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *); + p->prompts[p->n_prompts-1] = pr; +} +void prompt_ensure_result_size(prompt_t *pr, int newlen) +{ + if ((int)pr->resultsize < newlen) { + char *newbuf; + newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */ + + /* + * We don't use sresize / realloc here, because we will be + * storing sensitive stuff like passwords in here, and we want + * to make sure that the data doesn't get copied around in + * memory without the old copy being destroyed. + */ + newbuf = snewn(newlen, char); + memcpy(newbuf, pr->result, pr->resultsize); + smemclr(pr->result, pr->resultsize); + sfree(pr->result); + pr->result = newbuf; + pr->resultsize = newlen; + } +} +void prompt_set_result(prompt_t *pr, const char *newstr) +{ + prompt_ensure_result_size(pr, strlen(newstr) + 1); + strcpy(pr->result, newstr); +} +void free_prompts(prompts_t *p) +{ + size_t i; + for (i=0; i < p->n_prompts; i++) { + prompt_t *pr = p->prompts[i]; + smemclr(pr->result, pr->resultsize); /* burn the evidence */ + sfree(pr->result); + sfree(pr->prompt); + sfree(pr); + } + sfree(p->prompts); + sfree(p->name); + sfree(p->instruction); + sfree(p); +} + /* ---------------------------------------------------------------------- * String handling routines. */ @@ -58,16 +200,71 @@ char *dupcat(const char *s1, ...) return p; } +void burnstr(char *string) /* sfree(str), only clear it first */ +{ + if (string) { + smemclr(string, strlen(string)); + sfree(string); + } +} + +int toint(unsigned u) +{ + /* + * Convert an unsigned to an int, without running into the + * undefined behaviour which happens by the strict C standard if + * the value overflows. You'd hope that sensible compilers would + * do the sensible thing in response to a cast, but actually I + * don't trust modern compilers not to do silly things like + * assuming that _obviously_ you wouldn't have caused an overflow + * and so they can elide an 'if (i < 0)' test immediately after + * the cast. + * + * Sensible compilers ought of course to optimise this entire + * function into 'just return the input value'! + */ + if (u <= (unsigned)INT_MAX) + return (int)u; + else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */ + return INT_MIN + (int)(u - (unsigned)INT_MIN); + else + return INT_MIN; /* fallback; should never occur on binary machines */ +} + /* * Do an sprintf(), but into a custom-allocated buffer. * - * Irritatingly, we don't seem to be able to do this portably using - * vsnprintf(), because there appear to be issues with re-using the - * same va_list for two calls, and the excellent C99 va_copy is not - * yet widespread. Bah. Instead I'm going to do a horrid, horrid - * hack, in which I trawl the format string myself, work out the - * maximum length of each format component, and resize the buffer - * before printing it. + * 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 , 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. */ char *dupprintf(const char *fmt, ...) { @@ -90,7 +287,24 @@ char *dupvprintf(const char *fmt, va_list ap) #ifdef _WINDOWS #define vsnprintf _vsnprintf #endif +#ifdef va_copy + /* Use the `va_copy' macro mandated by C99, if present. + * XXX some environments may have this as __va_copy() */ + va_list aq; + va_copy(aq, ap); + len = vsnprintf(buf, size, fmt, aq); + va_end(aq); +#else + /* Ugh. No va_copy macro, so do something nasty. + * Technically, you can't reuse a va_list like this: it is left + * unspecified whether advancing a va_list pointer modifies its + * value or something it points to, so on some platforms calling + * vsnprintf twice on the same va_list might fail hideously + * (indeed, it has been observed to). + * XXX the autoconf manual suggests that using memcpy() will give + * "maximum portability". */ len = vsnprintf(buf, size, fmt, ap); +#endif if (len >= 0 && len < size) { /* This is the C99-specified criterion for snprintf to have * been completely successful. */ @@ -108,6 +322,29 @@ char *dupvprintf(const char *fmt, va_list ap) } } +/* + * Read an entire line of text from a file. Return a buffer + * malloced to be as big as necessary (caller must free). + */ +char *fgetline(FILE *fp) +{ + char *ret = snewn(512, char); + int size = 512, len = 0; + while (fgets(ret + len, size - len, fp)) { + len += strlen(ret + len); + if (ret[len-1] == '\n') + break; /* got a newline, we're done */ + size = len + 512; + ret = sresize(ret, size, char); + } + if (len == 0) { /* first fgets returned NULL */ + sfree(ret); + return NULL; + } + ret[len] = '\0'; + return ret; +} + /* ---------------------------------------------------------------------- * Base64 encoding routine. This is required in public-key writing * but also in HTTP proxy handling, so it's centralised here. @@ -150,12 +387,11 @@ void base64_encode_atom(unsigned char *data, int n, char *out) * - return the current size of the buffer chain in bytes */ -#define BUFFER_GRANULE 512 +#define BUFFER_MIN_GRANULE 512 struct bufchain_granule { struct bufchain_granule *next; - int buflen, bufpos; - char buf[BUFFER_GRANULE]; + char *bufpos, *bufend, *bufmax; }; void bufchain_init(bufchain *ch) @@ -189,28 +425,29 @@ void bufchain_add(bufchain *ch, const void *data, int len) ch->buffersize += len; - if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) { - int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen); - memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen); - buf += copylen; - len -= copylen; - ch->tail->buflen += copylen; - } while (len > 0) { - int grainlen = min(len, BUFFER_GRANULE); - struct bufchain_granule *newbuf; - newbuf = snew(struct bufchain_granule); - newbuf->bufpos = 0; - newbuf->buflen = grainlen; - memcpy(newbuf->buf, buf, grainlen); - buf += grainlen; - len -= grainlen; - if (ch->tail) - ch->tail->next = newbuf; - else - ch->head = ch->tail = newbuf; - newbuf->next = NULL; - ch->tail = newbuf; + if (ch->tail && ch->tail->bufend < ch->tail->bufmax) { + int 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) { + int 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; + } } } @@ -222,13 +459,13 @@ void bufchain_consume(bufchain *ch, int len) while (len > 0) { int remlen = len; assert(ch->head != NULL); - if (remlen >= ch->head->buflen - ch->head->bufpos) { - remlen = ch->head->buflen - ch->head->bufpos; + if (remlen >= ch->head->bufend - ch->head->bufpos) { + remlen = ch->head->bufend - ch->head->bufpos; tmp = ch->head; ch->head = tmp->next; - sfree(tmp); if (!ch->head) ch->tail = NULL; + sfree(tmp); } else ch->head->bufpos += remlen; ch->buffersize -= remlen; @@ -238,8 +475,8 @@ void bufchain_consume(bufchain *ch, int len) void bufchain_prefix(bufchain *ch, void **data, int *len) { - *len = ch->head->buflen - ch->head->bufpos; - *data = ch->head->buf + ch->head->bufpos; + *len = ch->head->bufend - ch->head->bufpos; + *data = ch->head->bufpos; } void bufchain_fetch(bufchain *ch, void *data, int len) @@ -254,9 +491,9 @@ void bufchain_fetch(bufchain *ch, void *data, int len) int remlen = len; assert(tmp != NULL); - if (remlen >= tmp->buflen - tmp->bufpos) - remlen = tmp->buflen - tmp->bufpos; - memcpy(data_c, tmp->buf + tmp->bufpos, remlen); + if (remlen >= tmp->bufend - tmp->bufpos) + remlen = tmp->bufend - tmp->bufpos; + memcpy(data_c, tmp->bufpos, remlen); tmp = tmp->next; len -= remlen; @@ -299,14 +536,22 @@ void mlog(char *file, int line) } #endif -void *safemalloc(size_t size) +void *safemalloc(size_t n, size_t size) { void *p; + + if (n > INT_MAX / size) { + p = NULL; + } else { + size *= n; + if (size == 0) size = 1; #ifdef MINEFIELD - p = minefield_c_malloc(size); + p = minefield_c_malloc(size); #else - p = malloc(size); + p = malloc(size); #endif + } + if (!p) { char str[200]; #ifdef MALLOC_LOG @@ -326,22 +571,29 @@ void *safemalloc(size_t size) return p; } -void *saferealloc(void *ptr, size_t size) +void *saferealloc(void *ptr, size_t n, size_t size) { void *p; - if (!ptr) { + + if (n > INT_MAX / size) { + p = NULL; + } else { + size *= n; + if (!ptr) { #ifdef MINEFIELD - p = minefield_c_malloc(size); + p = minefield_c_malloc(size); #else - p = malloc(size); + p = malloc(size); #endif - } else { + } else { #ifdef MINEFIELD - p = minefield_c_realloc(ptr, size); + p = minefield_c_realloc(ptr, size); #else - p = realloc(ptr, size); + p = realloc(ptr, size); #endif + } } + if (!p) { char str[200]; #ifdef MALLOC_LOG @@ -408,7 +660,7 @@ void debug_memdump(void *buf, int len, int L) if (L) { int delta; debug_printf("\t%d (0x%x) bytes:\n", len, len); - delta = 15 & (int) p; + delta = 15 & (unsigned long int) p; p -= delta; len += delta; } @@ -436,3 +688,63 @@ void debug_memdump(void *buf, int len, int L) } #endif /* def DEBUG */ + +/* + * Determine whether or not a Conf represents a session which can + * sensibly be launched right now. + */ +int 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); +} + +#ifndef PLATFORM_HAS_SMEMCLR +/* + * 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. + */ +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++; + } +} +#endif