Commit 1d812c8b authored by Noah Misch's avatar Noah Misch

pgcrypto: Detect and report too-short crypt() salts.

Certain short salts crashed the backend or disclosed a few bytes of
backend memory.  For existing salt-induced error conditions, emit a
message saying as much.  Back-patch to 9.0 (all supported versions).

Josh Kupershmidt

Security: CVE-2015-5288
parent 2ca9d544
...@@ -602,6 +602,17 @@ _crypt_blowfish_rn(const char *key, const char *setting, ...@@ -602,6 +602,17 @@ _crypt_blowfish_rn(const char *key, const char *setting,
if (size < 7 + 22 + 31 + 1) if (size < 7 + 22 + 31 + 1)
return NULL; return NULL;
/*
* Blowfish salt value must be formatted as follows: "$2a$" or "$2x$", a
* two digit cost parameter, "$", and 22 digits from the alphabet
* "./0-9A-Za-z". -- from the PHP crypt docs. Apparently we enforce a few
* more restrictions on the count in the salt as well.
*/
if (strlen(setting) < 29)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid salt")));
if (setting[0] != '$' || if (setting[0] != '$' ||
setting[1] != '2' || setting[1] != '2' ||
(setting[2] != 'a' && setting[2] != 'x') || (setting[2] != 'a' && setting[2] != 'x') ||
...@@ -611,14 +622,18 @@ _crypt_blowfish_rn(const char *key, const char *setting, ...@@ -611,14 +622,18 @@ _crypt_blowfish_rn(const char *key, const char *setting,
(setting[4] == '3' && setting[5] > '1') || (setting[4] == '3' && setting[5] > '1') ||
setting[6] != '$') setting[6] != '$')
{ {
return NULL; ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid salt")));
} }
count = (BF_word) 1 << ((setting[4] - '0') * 10 + (setting[5] - '0')); count = (BF_word) 1 << ((setting[4] - '0') * 10 + (setting[5] - '0'));
if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16)) if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16))
{ {
px_memset(data.binary.salt, 0, sizeof(data.binary.salt)); px_memset(data.binary.salt, 0, sizeof(data.binary.salt));
return NULL; ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid salt")));
} }
BF_swap(data.binary.salt, 4); BF_swap(data.binary.salt, 4);
......
...@@ -681,9 +681,19 @@ px_crypt_des(const char *key, const char *setting) ...@@ -681,9 +681,19 @@ px_crypt_des(const char *key, const char *setting)
if (*setting == _PASSWORD_EFMT1) if (*setting == _PASSWORD_EFMT1)
{ {
/* /*
* "new"-style: setting - underscore, 4 bytes of count, 4 bytes of * "new"-style: setting must be a 9-character (underscore, then 4
* salt key - unlimited characters * bytes of count, then 4 bytes of salt) string. See CRYPT(3) under
* the "Extended crypt" heading for further details.
*
* Unlimited characters of the input key are used. This is known as
* the "Extended crypt" DES method.
*
*/ */
if (strlen(setting) < 9)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid salt")));
for (i = 1, count = 0L; i < 5; i++) for (i = 1, count = 0L; i < 5; i++)
count |= ascii_to_bin(setting[i]) << (i - 1) * 6; count |= ascii_to_bin(setting[i]) << (i - 1) * 6;
...@@ -722,10 +732,16 @@ px_crypt_des(const char *key, const char *setting) ...@@ -722,10 +732,16 @@ px_crypt_des(const char *key, const char *setting)
#endif /* !DISABLE_XDES */ #endif /* !DISABLE_XDES */
{ {
/* /*
* "old"-style: setting - 2 bytes of salt key - up to 8 characters * "old"-style: setting - 2 bytes of salt key - only up to the first 8
* characters of the input key are used.
*/ */
count = 25; count = 25;
if (strlen(setting) < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid salt")));
salt = (ascii_to_bin(setting[1]) << 6) salt = (ascii_to_bin(setting[1]) << 6)
| ascii_to_bin(setting[0]); | ascii_to_bin(setting[0]);
......
...@@ -13,6 +13,15 @@ SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O'); ...@@ -13,6 +13,15 @@ SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
$2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C $2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C
(1 row) (1 row)
-- error, salt too short:
SELECT crypt('foox', '$2a$');
ERROR: invalid salt
-- error, first digit of count in salt invalid
SELECT crypt('foox', '$2a$40$RQiOJ.3ELirrXwxIZY8q0O');
ERROR: invalid salt
-- error, count in salt too small
SELECT crypt('foox', '$2a$00$RQiOJ.3ELirrXwxIZY8q0O');
ERROR: invalid salt
CREATE TABLE ctest (data text, res text, salt text); CREATE TABLE ctest (data text, res text, salt text);
INSERT INTO ctest VALUES ('password', '', ''); INSERT INTO ctest VALUES ('password', '', '');
UPDATE ctest SET salt = gen_salt('bf', 8); UPDATE ctest SET salt = gen_salt('bf', 8);
......
...@@ -13,6 +13,10 @@ SELECT crypt('foox', 'NB'); ...@@ -13,6 +13,10 @@ SELECT crypt('foox', 'NB');
NB53EGGqrrb5E NB53EGGqrrb5E
(1 row) (1 row)
-- We are supposed to pass in a 2-character salt.
-- error since salt is too short:
SELECT crypt('password', 'a');
ERROR: invalid salt
CREATE TABLE ctest (data text, res text, salt text); CREATE TABLE ctest (data text, res text, salt text);
INSERT INTO ctest VALUES ('password', '', ''); INSERT INTO ctest VALUES ('password', '', '');
UPDATE ctest SET salt = gen_salt('des'); UPDATE ctest SET salt = gen_salt('des');
......
...@@ -13,6 +13,30 @@ SELECT crypt('foox', '_J9..j2zz'); ...@@ -13,6 +13,30 @@ SELECT crypt('foox', '_J9..j2zz');
_J9..j2zzAYKMvO2BYRY _J9..j2zzAYKMvO2BYRY
(1 row) (1 row)
-- check XDES handling of keys longer than 8 chars
SELECT crypt('longlongpassword', '_J9..j2zz');
crypt
----------------------
_J9..j2zz4BeseiQNwUg
(1 row)
-- error, salt too short
SELECT crypt('foox', '_J9..BWH');
ERROR: invalid salt
-- error, count specified in the second argument is 0
SELECT crypt('password', '_........');
ERROR: crypt(3) returned NULL
-- error, count will wind up still being 0 due to invalid encoding
-- of the count: only chars ``./0-9A-Za-z' are valid
SELECT crypt('password', '_..!!!!!!');
ERROR: crypt(3) returned NULL
-- count should be non-zero here, will work
SELECT crypt('password', '_/!!!!!!!');
crypt
----------------------
_/!!!!!!!zqM49hRzxko
(1 row)
CREATE TABLE ctest (data text, res text, salt text); CREATE TABLE ctest (data text, res text, salt text);
INSERT INTO ctest VALUES ('password', '', ''); INSERT INTO ctest VALUES ('password', '', '');
UPDATE ctest SET salt = gen_salt('xdes', 1001); UPDATE ctest SET salt = gen_salt('xdes', 1001);
......
...@@ -42,7 +42,7 @@ run_crypt_des(const char *psw, const char *salt, ...@@ -42,7 +42,7 @@ run_crypt_des(const char *psw, const char *salt,
char *res; char *res;
res = px_crypt_des(psw, salt); res = px_crypt_des(psw, salt);
if (strlen(res) > len - 1) if (res == NULL || strlen(res) > len - 1)
return NULL; return NULL;
strcpy(buf, res); strcpy(buf, res);
return buf; return buf;
......
...@@ -6,6 +6,15 @@ SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O'); ...@@ -6,6 +6,15 @@ SELECT crypt('', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O'); SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
-- error, salt too short:
SELECT crypt('foox', '$2a$');
-- error, first digit of count in salt invalid
SELECT crypt('foox', '$2a$40$RQiOJ.3ELirrXwxIZY8q0O');
-- error, count in salt too small
SELECT crypt('foox', '$2a$00$RQiOJ.3ELirrXwxIZY8q0O');
CREATE TABLE ctest (data text, res text, salt text); CREATE TABLE ctest (data text, res text, salt text);
INSERT INTO ctest VALUES ('password', '', ''); INSERT INTO ctest VALUES ('password', '', '');
......
...@@ -6,6 +6,10 @@ SELECT crypt('', 'NB'); ...@@ -6,6 +6,10 @@ SELECT crypt('', 'NB');
SELECT crypt('foox', 'NB'); SELECT crypt('foox', 'NB');
-- We are supposed to pass in a 2-character salt.
-- error since salt is too short:
SELECT crypt('password', 'a');
CREATE TABLE ctest (data text, res text, salt text); CREATE TABLE ctest (data text, res text, salt text);
INSERT INTO ctest VALUES ('password', '', ''); INSERT INTO ctest VALUES ('password', '', '');
......
...@@ -6,6 +6,22 @@ SELECT crypt('', '_J9..j2zz'); ...@@ -6,6 +6,22 @@ SELECT crypt('', '_J9..j2zz');
SELECT crypt('foox', '_J9..j2zz'); SELECT crypt('foox', '_J9..j2zz');
-- check XDES handling of keys longer than 8 chars
SELECT crypt('longlongpassword', '_J9..j2zz');
-- error, salt too short
SELECT crypt('foox', '_J9..BWH');
-- error, count specified in the second argument is 0
SELECT crypt('password', '_........');
-- error, count will wind up still being 0 due to invalid encoding
-- of the count: only chars ``./0-9A-Za-z' are valid
SELECT crypt('password', '_..!!!!!!');
-- count should be non-zero here, will work
SELECT crypt('password', '_/!!!!!!!');
CREATE TABLE ctest (data text, res text, salt text); CREATE TABLE ctest (data text, res text, salt text);
INSERT INTO ctest VALUES ('password', '', ''); INSERT INTO ctest VALUES ('password', '', '');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment