Commit message: Fix base64d() buffer size (CVE-2018-6789)

Credits for discovering this bug: Meh Chang <meh@devco.re>

(cherry picked from commit 062990cc1b2f9e5d82a413b53c8f0569075de700)
CVE: CVE-2018-6789
CWE: CWE-119
Vulnerability description: An issue was discovered in the base64d function in the SMTP listener in Exim before 4.90.1. By sending a handcrafted message, a buffer overflow may happen. This can be used to execute code remotely.

Project: exim
File: base64.c

b64encode(uschar *clear, int len)
{
uschar *code = store_get(4*((len+2)/3) + 1);
uschar *p = code;

while (len-- >0)
  {
  int x, y;

  x = *clear++;
  *p++ = enc64table[(x >> 2) & 63];

  if (len-- <= 0)
    {
    *p++ = enc64table[(x << 4) & 63];
    *p++ = '=';
    *p++ = '=';
    break;
    }

  y = *clear++;
  *p++ = enc64table[((x << 4) | ((y >> 4) & 15)) & 63];

  if (len-- <= 0)
    {
    *p++ = enc64table[(y << 2) & 63];
    *p++ = '=';
    break;
    }

  x = *clear++;
  *p++ = enc64table[((y << 2) | ((x >> 6) & 3)) & 63];

  *p++ = enc64table[x & 63];
  }

*p = 0;

return code;
}

Project: exim
File: base64.c

mime_decode_base64(FILE * in, FILE * out, uschar * boundary)
{
uschar ibuf[MIME_MAX_LINE_LENGTH], obuf[MIME_MAX_LINE_LENGTH];
uschar *ipos, *opos;
ssize_t len, size = 0;
int bytestate = 0;

opos = obuf;

while (Ufgets(ibuf, MIME_MAX_LINE_LENGTH, in) != NULL)
  {
  if (boundary != NULL
     && Ustrncmp(ibuf, "--", 2) == 0
     && Ustrncmp((ibuf+2), boundary, Ustrlen(boundary)) == 0
     )
    break;

  for (ipos = ibuf ; *ipos != '\r' && *ipos != '\n' && *ipos != 0; ++ipos)
    if (*ipos == '=')			/* skip padding */
      ++bytestate;

    else if (mime_b64[*ipos] == 128)	/* skip bad characters */
      mime_set_anomaly(MIME_ANOMALY_BROKEN_BASE64);

    /* simple state-machine */
    else switch((bytestate++) & 3)
      {
      case 0:
	*opos = mime_b64[*ipos] << 2; break;
      case 1:
	*opos++ |= mime_b64[*ipos] >> 4;
	*opos = mime_b64[*ipos] << 4; break;
      case 2:
	*opos++ |= mime_b64[*ipos] >> 2;
	*opos = mime_b64[*ipos] << 6; break;
      case 3:
	*opos++ |= mime_b64[*ipos]; break;
      }

  /* something to write? */
  len = opos - obuf;
  if (len > 0)
    {
    if (fwrite(obuf, 1, len, out) != len) return -1; /* error */
    size += len;
    /* copy incomplete last byte to start of obuf, where we continue */
    if ((bytestate & 3) != 0)
      *obuf = *opos;
    opos = obuf;
    }
  } /* while */

/* write out last byte if it was incomplete */
if (bytestate & 3)
  {
  if (fwrite(obuf, 1, 1, out) != 1) return -1;
  ++size;
  }

return size;
}

Project: exim
File: base64.c

b64decode(const uschar *code, uschar **ptr)
{

int x, y;
uschar *result;

{
  int l = Ustrlen(code);
  *ptr = result = store_get(1 + l/4 * 3 + l%4);
}

/* Each cycle of the loop handles a quantum of 4 input bytes. For the last
quantum this may decode to 1, 2, or 3 output bytes. */

while ((x = *code++) != 0)
  {
  if (isspace(x)) continue;
  /* debug_printf("b64d: '%c'\n", x); */

  if (x > 127 || (x = dec64table[x]) == 255) return -1;

  while (isspace(y = *code++)) ;
  /* debug_printf("b64d: '%c'\n", y); */
  if (y == 0 || (y = dec64table[y]) == 255)
    return -1;

  *result++ = (x << 2) | (y >> 4);
  /* debug_printf("b64d:      -> %02x\n", result[-1]); */

  while (isspace(x = *code++)) ;
  /* debug_printf("b64d: '%c'\n", x); */
  if (x == '=')		/* endmarker, but there should be another */
    {
    while (isspace(x = *code++)) ;
    /* debug_printf("b64d: '%c'\n", x); */
    if (x != '=') return -1;
    while (isspace(y = *code++)) ;
    if (y != 0) return -1;
    /* debug_printf("b64d: DONE\n"); */
    break;
    }
  else
    {
    if (x > 127 || (x = dec64table[x]) == 255) return -1;
    *result++ = (y << 4) | (x >> 2);
    /* debug_printf("b64d:      -> %02x\n", result[-1]); */

    while (isspace(y = *code++)) ;
    /* debug_printf("b64d: '%c'\n", y); */
    if (y == '=')
      {
      while (isspace(y = *code++)) ;
      if (y != 0) return -1;
      /* debug_printf("b64d: DONE\n"); */
      break;
      }
    else
      {
      if (y > 127 || (y = dec64table[y]) == 255) return -1;
      *result++ = (x << 6) | y;
      /* debug_printf("b64d:      -> %02x\n", result[-1]); */
      }
    }
  }

*result = 0;
return result - *ptr;
}