mirror of
https://github.com/zeek/zeek.git
synced 2025-10-10 10:38:20 +00:00

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
284 lines
6.2 KiB
C++
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
|