mirror of
https://git.postgresql.org/git/postgresql.git
synced 2026-02-10 12:17:32 +08:00
This is a follow-up refactoring after 09ec55b and b674211, which has proved that the encoding and decoding routines used by SCRAM have a poor interface when it comes to check after buffer overflows. This adds an extra argument in the shape of the length of the result buffer for each routine, which is used for overflow checks when encoding or decoding an input string. The original idea comes from Tom Lane. As a result of that, the encoding routine can now fail, so all its callers are adjusted to generate proper error messages in case of problems. On failure, the result buffer gets zeroed. Author: Michael Paquier Reviewed-by: Daniel Gustafsson Discussion: https://postgr.es/m/20190623132535.GB1628@paquier.xyz
243 lines
4.7 KiB
C
243 lines
4.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* base64.c
|
|
* Encoding and decoding routines for base64 without whitespace.
|
|
*
|
|
* Copyright (c) 2001-2019, PostgreSQL Global Development Group
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/common/base64.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifndef FRONTEND
|
|
#include "postgres.h"
|
|
#else
|
|
#include "postgres_fe.h"
|
|
#endif
|
|
|
|
#include "common/base64.h"
|
|
|
|
/*
|
|
* BASE64
|
|
*/
|
|
|
|
static const char _base64[] =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
static const int8 b64lookup[128] = {
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
|
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
|
|
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
|
|
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
|
|
};
|
|
|
|
/*
|
|
* pg_b64_encode
|
|
*
|
|
* Encode into base64 the given string. Returns the length of the encoded
|
|
* string, and -1 in the event of an error with the result buffer zeroed
|
|
* for safety.
|
|
*/
|
|
int
|
|
pg_b64_encode(const char *src, int len, char *dst, int dstlen)
|
|
{
|
|
char *p;
|
|
const char *s,
|
|
*end = src + len;
|
|
int pos = 2;
|
|
uint32 buf = 0;
|
|
|
|
s = src;
|
|
p = dst;
|
|
|
|
while (s < end)
|
|
{
|
|
buf |= (unsigned char) *s << (pos << 3);
|
|
pos--;
|
|
s++;
|
|
|
|
/* write it out */
|
|
if (pos < 0)
|
|
{
|
|
/*
|
|
* Leave if there is an overflow in the area allocated for the
|
|
* encoded string.
|
|
*/
|
|
if ((p - dst + 4) > dstlen)
|
|
goto error;
|
|
|
|
*p++ = _base64[(buf >> 18) & 0x3f];
|
|
*p++ = _base64[(buf >> 12) & 0x3f];
|
|
*p++ = _base64[(buf >> 6) & 0x3f];
|
|
*p++ = _base64[buf & 0x3f];
|
|
|
|
pos = 2;
|
|
buf = 0;
|
|
}
|
|
}
|
|
if (pos != 2)
|
|
{
|
|
/*
|
|
* Leave if there is an overflow in the area allocated for the encoded
|
|
* string.
|
|
*/
|
|
if ((p - dst + 4) > dstlen)
|
|
goto error;
|
|
|
|
*p++ = _base64[(buf >> 18) & 0x3f];
|
|
*p++ = _base64[(buf >> 12) & 0x3f];
|
|
*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
|
|
*p++ = '=';
|
|
}
|
|
|
|
Assert((p - dst) <= dstlen);
|
|
return p - dst;
|
|
|
|
error:
|
|
memset(dst, 0, dstlen);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* pg_b64_decode
|
|
*
|
|
* Decode the given base64 string. Returns the length of the decoded
|
|
* string on success, and -1 in the event of an error with the result
|
|
* buffer zeroed for safety.
|
|
*/
|
|
int
|
|
pg_b64_decode(const char *src, int len, char *dst, int dstlen)
|
|
{
|
|
const char *srcend = src + len,
|
|
*s = src;
|
|
char *p = dst;
|
|
char c;
|
|
int b = 0;
|
|
uint32 buf = 0;
|
|
int pos = 0,
|
|
end = 0;
|
|
|
|
while (s < srcend)
|
|
{
|
|
c = *s++;
|
|
|
|
/* Leave if a whitespace is found */
|
|
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
|
|
goto error;
|
|
|
|
if (c == '=')
|
|
{
|
|
/* end sequence */
|
|
if (!end)
|
|
{
|
|
if (pos == 2)
|
|
end = 1;
|
|
else if (pos == 3)
|
|
end = 2;
|
|
else
|
|
{
|
|
/*
|
|
* Unexpected "=" character found while decoding base64
|
|
* sequence.
|
|
*/
|
|
goto error;
|
|
}
|
|
}
|
|
b = 0;
|
|
}
|
|
else
|
|
{
|
|
b = -1;
|
|
if (c > 0 && c < 127)
|
|
b = b64lookup[(unsigned char) c];
|
|
if (b < 0)
|
|
{
|
|
/* invalid symbol found */
|
|
goto error;
|
|
}
|
|
}
|
|
/* add it to buffer */
|
|
buf = (buf << 6) + b;
|
|
pos++;
|
|
if (pos == 4)
|
|
{
|
|
/*
|
|
* Leave if there is an overflow in the area allocated for the
|
|
* decoded string.
|
|
*/
|
|
if ((p - dst + 1) > dstlen)
|
|
goto error;
|
|
*p++ = (buf >> 16) & 255;
|
|
|
|
if (end == 0 || end > 1)
|
|
{
|
|
/* overflow check */
|
|
if ((p - dst + 1) > dstlen)
|
|
goto error;
|
|
*p++ = (buf >> 8) & 255;
|
|
}
|
|
if (end == 0 || end > 2)
|
|
{
|
|
/* overflow check */
|
|
if ((p - dst + 1) > dstlen)
|
|
goto error;
|
|
*p++ = buf & 255;
|
|
}
|
|
buf = 0;
|
|
pos = 0;
|
|
}
|
|
}
|
|
|
|
if (pos != 0)
|
|
{
|
|
/*
|
|
* base64 end sequence is invalid. Input data is missing padding, is
|
|
* truncated or is otherwise corrupted.
|
|
*/
|
|
goto error;
|
|
}
|
|
|
|
Assert((p - dst) <= dstlen);
|
|
return p - dst;
|
|
|
|
error:
|
|
memset(dst, 0, dstlen);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* pg_b64_enc_len
|
|
*
|
|
* Returns to caller the length of the string if it were encoded with
|
|
* base64 based on the length provided by caller. This is useful to
|
|
* estimate how large a buffer allocation needs to be done before doing
|
|
* the actual encoding.
|
|
*/
|
|
int
|
|
pg_b64_enc_len(int srclen)
|
|
{
|
|
/* 3 bytes will be converted to 4 */
|
|
return (srclen + 2) * 4 / 3;
|
|
}
|
|
|
|
/*
|
|
* pg_b64_dec_len
|
|
*
|
|
* Returns to caller the length of the string if it were to be decoded
|
|
* with base64, based on the length given by caller. This is useful to
|
|
* estimate how large a buffer allocation needs to be done before doing
|
|
* the actual decoding.
|
|
*/
|
|
int
|
|
pg_b64_dec_len(int srclen)
|
|
{
|
|
return (srclen * 3) >> 2;
|
|
}
|