zeek/src/Base64.cc
Arne Welzel 5dc54fb40e Base64: report byte as positive integer
A baseline difference between arm64 and x86 showed up. We would
print a wrong character as negative value on x86 due to chars
being signed by default. Force an unsigned interpretation which
is also more reasonable because we'd have never indexed the
base64 table with -112

    -XXXXXXXXXX.XXXXXX      XXXXXXXXXXX     131.243.99.154  3288 193.159.183.138 80      base64_illegal_encoding character -112 ignored by Base64 decoding       F       zeek    -
    +XXXXXXXXXX.XXXXXX      XXXXXXXXXXX     131.243.99.154  3288 193.159.183.138 80      base64_illegal_encoding character 144 ignored by Base64 decoding        F       zeek    -

Fixes more of #2742
2023-02-02 15:49:22 +01:00

284 lines
6.2 KiB
C++

#include "zeek/Base64.h"
#include "zeek/zeek-config.h"
#include <cmath>
#include "zeek/Conn.h"
#include "zeek/Reporter.h"
#include "zeek/ZeekString.h"
namespace zeek::detail
{
int Base64Converter::default_base64_table[256];
const std::string Base64Converter::default_alphabet =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
void Base64Converter::Encode(int len, const unsigned char* data, int* pblen, char** pbuf)
{
int blen;
char* buf;
if ( ! pbuf )
reporter->InternalError("nil pointer to encoding result buffer");
if ( *pbuf && (*pblen % 4 != 0) )
reporter->InternalError("Base64 encode buffer not a multiple of 4");
if ( *pbuf )
{
buf = *pbuf;
blen = *pblen;
}
else
{
blen = (int)(4 * ceil((double)len / 3));
*pbuf = buf = new char[blen];
*pblen = blen;
}
for ( int i = 0, j = 0; (i < len) && (j < blen); )
{
uint32_t bit32 = data[i++] << 16;
bit32 += (i++ < len ? data[i - 1] : 0) << 8;
bit32 += i++ < len ? data[i - 1] : 0;
buf[j++] = alphabet[(bit32 >> 18) & 0x3f];
buf[j++] = alphabet[(bit32 >> 12) & 0x3f];
buf[j++] = (i == (len + 2)) ? '=' : alphabet[(bit32 >> 6) & 0x3f];
buf[j++] = (i >= (len + 1)) ? '=' : alphabet[bit32 & 0x3f];
}
}
int* Base64Converter::InitBase64Table(const std::string& alphabet)
{
assert(alphabet.size() == 64);
static bool default_table_initialized = false;
if ( alphabet == default_alphabet && default_table_initialized )
return default_base64_table;
int* base64_table = nullptr;
if ( alphabet == default_alphabet )
{
base64_table = default_base64_table;
default_table_initialized = true;
}
else
base64_table = new int[256];
int i;
for ( i = 0; i < 256; ++i )
base64_table[i] = -1;
for ( i = 0; i < 26; ++i )
{
base64_table[int(alphabet[0 + i])] = i;
base64_table[int(alphabet[26 + i])] = i + 26;
}
for ( i = 0; i < 10; ++i )
base64_table[int(alphabet[52 + i])] = i + 52;
// Casts to avoid compiler warnings.
base64_table[int(alphabet[62])] = 62;
base64_table[int(alphabet[63])] = 63;
base64_table[int('=')] = 0;
return base64_table;
}
Base64Converter::Base64Converter(Connection* arg_conn, const std::string& arg_alphabet)
{
if ( arg_alphabet.size() > 0 )
{
assert(arg_alphabet.size() == 64);
alphabet = arg_alphabet;
}
else
{
alphabet = default_alphabet;
}
base64_table = nullptr;
base64_group_next = 0;
base64_padding = base64_after_padding = 0;
errored = 0;
conn = arg_conn;
}
Base64Converter::~Base64Converter()
{
if ( base64_table != default_base64_table )
delete[] base64_table;
}
int Base64Converter::Decode(int len, const char* data, int* pblen, char** pbuf)
{
int blen;
char* buf;
// Initialization of table on first_time call of Decode.
if ( ! base64_table )
base64_table = InitBase64Table(alphabet);
if ( ! pbuf )
reporter->InternalError("nil pointer to decoding result buffer");
if ( *pbuf )
{
buf = *pbuf;
blen = *pblen;
}
else
{
// Estimate the maximal number of 3-byte groups needed,
// plus 1 byte for the optional ending NUL.
blen = int((len + base64_group_next + 3) / 4) * 3 + 1;
*pbuf = buf = new char[blen];
}
int dlen = 0;
while ( true )
{
if ( base64_group_next == 4 )
{
// For every group of 4 6-bit numbers,
// write the decoded 3 bytes to the buffer.
if ( base64_after_padding )
{
if ( ++errored == 1 )
IllegalEncoding("extra base64 groups after '=' padding are ignored");
base64_group_next = 0;
continue;
}
int num_octets = 3 - base64_padding;
if ( buf + num_octets > *pbuf + blen )
break;
uint32_t bit32 = ((base64_group[0] & 0x3f) << 18) | ((base64_group[1] & 0x3f) << 12) |
((base64_group[2] & 0x3f) << 6) | ((base64_group[3] & 0x3f));
if ( --num_octets >= 0 )
*buf++ = char((bit32 >> 16) & 0xff);
if ( --num_octets >= 0 )
*buf++ = char((bit32 >> 8) & 0xff);
if ( --num_octets >= 0 )
*buf++ = char((bit32)&0xff);
if ( base64_padding > 0 )
base64_after_padding = 1;
base64_group_next = 0;
base64_padding = 0;
}
if ( dlen >= len )
break;
unsigned char c = (unsigned char)data[dlen];
if ( c == '=' )
++base64_padding;
int k = base64_table[c];
if ( k >= 0 )
base64_group[base64_group_next++] = k;
else
{
if ( ++errored == 1 )
IllegalEncoding(util::fmt("character %d ignored by Base64 decoding", (int)c));
}
++dlen;
}
*pblen = buf - *pbuf;
return dlen;
}
int Base64Converter::Done(int* pblen, char** pbuf)
{
const char* padding = "===";
if ( base64_group_next != 0 )
{
if ( base64_group_next < 4 )
IllegalEncoding(util::fmt("incomplete base64 group, padding with %d bits of 0",
(4 - base64_group_next) * 6));
Decode(4 - base64_group_next, padding, pblen, pbuf);
return -1;
}
if ( pblen )
*pblen = 0;
return 0;
}
void Base64Converter::IllegalEncoding(const char* msg)
{
// strncpy(error_msg, msg, sizeof(error_msg));
if ( conn )
conn->Weird("base64_illegal_encoding", msg);
else
reporter->Error("%s", msg);
}
String* decode_base64(const String* s, const String* a, Connection* conn)
{
if ( a && a->Len() != 0 && a->Len() != 64 )
{
reporter->Error("base64 decoding alphabet is not 64 characters: %s", a->CheckString());
return nullptr;
}
int buf_len = int((s->Len() + 3) / 4) * 3 + 1;
int rlen2, rlen = buf_len;
char *rbuf2, *rbuf = new char[rlen];
Base64Converter dec(conn, a ? a->CheckString() : "");
dec.Decode(s->Len(), (const char*)s->Bytes(), &rlen, &rbuf);
if ( dec.Errored() )
goto err;
rlen2 = buf_len - rlen;
rbuf2 = rbuf + rlen;
// Done() returns -1 if there isn't enough padding, but we just ignore
// it.
dec.Done(&rlen2, &rbuf2);
rlen += rlen2;
rbuf[rlen] = '\0';
return new String(true, (u_char*)rbuf, rlen);
err:
delete[] rbuf;
return nullptr;
}
String* encode_base64(const String* s, const String* a, Connection* conn)
{
if ( a && a->Len() != 0 && a->Len() != 64 )
{
reporter->Error("base64 alphabet is not 64 characters: %s", a->CheckString());
return nullptr;
}
char* outbuf = nullptr;
int outlen = 0;
Base64Converter enc(conn, a ? a->CheckString() : "");
enc.Encode(s->Len(), (const unsigned char*)s->Bytes(), &outlen, &outbuf);
return new String(true, (u_char*)outbuf, outlen);
}
} // namespace zeek::detail