]> granicus.if.org Git - postgresql/commitdiff
pgcrypto: Detect and report too-short crypt() salts.
authorNoah Misch <noah@leadboat.com>
Mon, 5 Oct 2015 14:06:29 +0000 (10:06 -0400)
committerNoah Misch <noah@leadboat.com>
Mon, 5 Oct 2015 14:06:29 +0000 (10:06 -0400)
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

contrib/pgcrypto/crypt-blowfish.c
contrib/pgcrypto/crypt-des.c
contrib/pgcrypto/expected/crypt-blowfish.out
contrib/pgcrypto/expected/crypt-des.out
contrib/pgcrypto/expected/crypt-xdes.out
contrib/pgcrypto/px-crypt.c
contrib/pgcrypto/sql/crypt-blowfish.sql
contrib/pgcrypto/sql/crypt-des.sql
contrib/pgcrypto/sql/crypt-xdes.sql

index fbaa3d776a08fb439158ddb41cde4c67f73079b8..4054e6a06fc758e2bd388080ee0b6aeb523de42e 100644 (file)
@@ -602,6 +602,17 @@ _crypt_blowfish_rn(const char *key, const char *setting,
        if (size < 7 + 22 + 31 + 1)
                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] != '$' ||
                setting[1] != '2' ||
                (setting[2] != 'a' && setting[2] != 'x') ||
@@ -611,14 +622,18 @@ _crypt_blowfish_rn(const char *key, const char *setting,
                (setting[4] == '3' && setting[5] > '1') ||
                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'));
        if (count < 16 || BF_decode(data.binary.salt, &setting[7], 16))
        {
                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);
 
index b43141fed5cb667c508f0d8131e1be4f33b3f94f..e94346159955c405076f1515b81d1abe770dc874 100644 (file)
@@ -681,9 +681,19 @@ px_crypt_des(const char *key, const char *setting)
        if (*setting == _PASSWORD_EFMT1)
        {
                /*
-                * "new"-style: setting - underscore, 4 bytes of count, 4 bytes of
-                * salt key - unlimited characters
+                * "new"-style: setting must be a 9-character (underscore, then 4
+                * 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++)
                        count |= ascii_to_bin(setting[i]) << (i - 1) * 6;
 
@@ -722,10 +732,16 @@ px_crypt_des(const char *key, const char *setting)
 #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;
 
+               if (strlen(setting) < 2)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("invalid salt")));
+
                salt = (ascii_to_bin(setting[1]) << 6)
                        | ascii_to_bin(setting[0]);
 
index 329d78f6254621d3e1dedfe86afb8c0b18a03f3d..d79b0c047b4c77a0312acbb517c869959a6e67a7 100644 (file)
@@ -13,6 +13,15 @@ SELECT crypt('foox', '$2a$06$RQiOJ.3ELirrXwxIZY8q0O');
  $2a$06$RQiOJ.3ELirrXwxIZY8q0OR3CVJrAfda1z26CCHPnB6mmVZD8p0/C
 (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);
 INSERT INTO ctest VALUES ('password', '', '');
 UPDATE ctest SET salt = gen_salt('bf', 8);
index b8b605037d48b1b35fcda93fe66a735aa06122bc..a462dcd580a896295e8f298b752333236154db17 100644 (file)
@@ -13,6 +13,10 @@ SELECT crypt('foox', 'NB');
  NB53EGGqrrb5E
 (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);
 INSERT INTO ctest VALUES ('password', '', '');
 UPDATE ctest SET salt = gen_salt('des');
index cdcdefb1996997f4a7f9b54f3efef46acd20b219..8cf907512f6f7fbbc219788b5fbb862774ac5707 100644 (file)
@@ -13,6 +13,30 @@ SELECT crypt('foox', '_J9..j2zz');
  _J9..j2zzAYKMvO2BYRY
 (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);
 INSERT INTO ctest VALUES ('password', '', '');
 UPDATE ctest SET salt = gen_salt('xdes', 1001);
index 7b003a76ca663bd418ac858d0a6e7680325e87c8..e3246fc5b9d5f4194735eeefd0f013db9272acff 100644 (file)
@@ -42,7 +42,7 @@ run_crypt_des(const char *psw, const char *salt,
        char       *res;
 
        res = px_crypt_des(psw, salt);
-       if (strlen(res) > len - 1)
+       if (res == NULL || strlen(res) > len - 1)
                return NULL;
        strcpy(buf, res);
        return buf;
index 60c114005552093ba07c83d79a63fc96a3f87aa6..3b5a681c3f5cd56d5376f4a56f466fcc110f7f24 100644 (file)
@@ -6,6 +6,15 @@ SELECT crypt('', '$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);
 INSERT INTO ctest VALUES ('password', '', '');
 
index fabdc652fc986808dc6a7c3a0d5dc21ed31ac7b0..a85ec1e655510d8d66e7f6f478ce63e7375e25c9 100644 (file)
@@ -6,6 +6,10 @@ SELECT crypt('', '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);
 INSERT INTO ctest VALUES ('password', '', '');
 
index d4a74f76bde5276b2474a443a04d5e5e34caa602..8171cd872be21e128ca7343979b2cb8400901400 100644 (file)
@@ -6,6 +6,22 @@ SELECT crypt('', '_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);
 INSERT INTO ctest VALUES ('password', '', '');