Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1ss6g5-0068fL-VM for pgsql-hackers@arkaria.postgresql.org; Sat, 21 Sep 2024 20:26:02 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.94.2) (envelope-from ) id 1ss6g4-009rji-0w for pgsql-hackers@arkaria.postgresql.org; Sat, 21 Sep 2024 20:26:01 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1ss6g3-009rja-JN for pgsql-hackers@lists.postgresql.org; Sat, 21 Sep 2024 20:26:01 +0000 Received: from mail-il1-x134.google.com ([2607:f8b0:4864:20::134]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.94.2) (envelope-from ) id 1ss6g2-000NOj-5o; Sat, 21 Sep 2024 20:25:59 +0000 Received: by mail-il1-x134.google.com with SMTP id e9e14a558f8ab-3a0be4d803eso10259215ab.0; Sat, 21 Sep 2024 13:25:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1726950357; x=1727555157; darn=postgresql.org; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:from:to:cc:subject:date:message-id:reply-to; bh=tLqZcUczqP27SVIdj8rpn1XW9b6ACYRigRcK/yJZNT4=; b=Ra/5foCfGWt6ZAGpMf59DZ0hitzcvazIPBdRC9y6+Tdh5h7pyR4ks8Ns/xqINXjEOI eIB1wh01e08YTrt3WjcYq4ZWxzBQNDozvYOQ5nxafINbeV4WWzETtrzS1Ai9GrbVVxHo BQRvZV0XCgu5xyRD8TzFJZkVwlRuhFNYC1cno/Q8DGnMhgFU9qBsgVe58C6/W8xWABUM URasLDjm8MtQnReuo4nggtuXbNIpQn6i+ElF1BFQ7jUId6V+4Iy8RYG9M9nJDrQQYRBQ OfNXPR/HLTogiERnzSQci3yHPrxGC++FY0Z/B7f2PkoNqK3BANJVgsqoJ1HbnWCzTRLZ YLDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1726950357; x=1727555157; h=in-reply-to:content-disposition:mime-version:references:message-id :subject:cc:to:from:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=tLqZcUczqP27SVIdj8rpn1XW9b6ACYRigRcK/yJZNT4=; b=QenWYczhupNXRLM8MmkKeH7+HdAXGPfc34x8ekRJ9TbzRBCYGviJZn1QsoN0YcnHRZ L+ZCBZ90DinXEU2cQv4rR9b9ut0okd2NO4vsjgs+SIvw1HsKwMTPXsg+MPTbdmHdZa3e p159GvyvmastYo63+BLdUjkdFmuAsOwbDKZIYlxOoyuFSTQ+1nex6phOmDBzLMR7Jl+W fA9z07opt9q75EftZAKB4kVInYpA4WiwwNOQs9ltXdtLJvTZaZ9wamY2/uHo2KaDCRqN z51sXnJ1s+rZnc1UpYAHGzY5sjp+qMoy7qboOeehQNYZbdiczdnXdFSkEclopLKaK2wn XUmw== X-Forwarded-Encrypted: i=1; AJvYcCV7K3GSeCLHrYdUe50mzvGKvnuLrgy7jdGgbvJ9cAQLqdOg3f2iRqbxmA96RkLdKKr1PbMWGAwifpZpTL+3@postgresql.org X-Gm-Message-State: AOJu0Ywja4btjGESqEjfSxsB/o/ECi5h/8LZrEfZZrXIkDRN4kOqslsJ 1vuBiOscoS1+Y2PGO0IkMkg9GTBNDh1UqRkrVmMF7sgX7L5ARjx8tY6B6w== X-Google-Smtp-Source: AGHT+IEMo4dJCNKxKsdtIK+P4pLiPipwrtoRMshUeA/glFRLo64ZJC60a6zva6+efeDqfZphgVTn6Q== X-Received: by 2002:a05:6e02:1e01:b0:39d:1ca5:3904 with SMTP id e9e14a558f8ab-3a0c9d317e1mr63695815ab.14.1726950357242; Sat, 21 Sep 2024 13:25:57 -0700 (PDT) Received: from nathan (162-195-168-172.lightspeed.stlsmo.sbcglobal.net. [162.195.168.172]) by smtp.gmail.com with ESMTPSA id e9e14a558f8ab-3a0c9f22728sm14139535ab.83.2024.09.21.13.25.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 21 Sep 2024 13:25:56 -0700 (PDT) Date: Sat, 21 Sep 2024 15:25:54 -0500 From: Nathan Bossart To: Tom Lane Cc: "Jonathan S. Katz" , Michael Paquier , Alexander Lakhin , pgsql-hackers Subject: Re: Should rolpassword be toastable? Message-ID: References: <1986859.1726756275@sss.pgh.pa.us> <2047353.1726784074@sss.pgh.pa.us> <0a9b7f96-aa2f-41eb-8e69-62f7990ebf74@postgresql.org> <0d8f3541-13f4-4194-8dca-bae881cf1a9a@postgresql.org> <2445149.1726849661@sss.pgh.pa.us> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="2R5fTxH63iv17WWy" Content-Disposition: inline In-Reply-To: <2445149.1726849661@sss.pgh.pa.us> List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk --2R5fTxH63iv17WWy Content-Type: text/plain; charset=us-ascii Content-Disposition: inline On Fri, Sep 20, 2024 at 12:27:41PM -0400, Tom Lane wrote: > Nitpick: the message should say "%d bytes" not "%d characters", > because we're counting bytes. Passes an eyeball check otherwise. Thanks for reviewing. I went ahead and committed 0002 since it seems like there's consensus on that one. I've attached a rebased version of 0001 with s/characters/bytes. -- nathan --2R5fTxH63iv17WWy Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v4-0001-place-limit-on-password-hash-length.patch" From f039f02cc35d57862af64648d4152693e52fbee2 Mon Sep 17 00:00:00 2001 From: Nathan Bossart Date: Thu, 19 Sep 2024 20:59:10 -0500 Subject: [PATCH v4 1/1] place limit on password hash length --- src/backend/libpq/crypt.c | 60 ++++++++++++++++++-------- src/include/libpq/crypt.h | 10 +++++ src/test/regress/expected/password.out | 7 +++ src/test/regress/sql/password.sql | 4 ++ 4 files changed, 63 insertions(+), 18 deletions(-) diff --git a/src/backend/libpq/crypt.c b/src/backend/libpq/crypt.c index 629e51e00b..05d289977f 100644 --- a/src/backend/libpq/crypt.c +++ b/src/backend/libpq/crypt.c @@ -116,7 +116,7 @@ encrypt_password(PasswordType target_type, const char *role, const char *password) { PasswordType guessed_type = get_password_type(password); - char *encrypted_password; + char *encrypted_password = NULL; const char *errstr = NULL; if (guessed_type != PASSWORD_TYPE_PLAINTEXT) @@ -125,32 +125,56 @@ encrypt_password(PasswordType target_type, const char *role, * Cannot convert an already-encrypted password from one format to * another, so return it as it is. */ - return pstrdup(password); + encrypted_password = pstrdup(password); } - - switch (target_type) + else { - case PASSWORD_TYPE_MD5: - encrypted_password = palloc(MD5_PASSWD_LEN + 1); + switch (target_type) + { + case PASSWORD_TYPE_MD5: + encrypted_password = palloc(MD5_PASSWD_LEN + 1); - if (!pg_md5_encrypt(password, role, strlen(role), - encrypted_password, &errstr)) - elog(ERROR, "password encryption failed: %s", errstr); - return encrypted_password; + if (!pg_md5_encrypt(password, role, strlen(role), + encrypted_password, &errstr)) + elog(ERROR, "password encryption failed: %s", errstr); + break; - case PASSWORD_TYPE_SCRAM_SHA_256: - return pg_be_scram_build_secret(password); + case PASSWORD_TYPE_SCRAM_SHA_256: + encrypted_password = pg_be_scram_build_secret(password); + break; - case PASSWORD_TYPE_PLAINTEXT: - elog(ERROR, "cannot encrypt password with 'plaintext'"); + case PASSWORD_TYPE_PLAINTEXT: + elog(ERROR, "cannot encrypt password with 'plaintext'"); + break; + } } + Assert(encrypted_password); + /* - * This shouldn't happen, because the above switch statements should - * handle every combination of source and target password types. + * Valid password hashes may be very long, but we don't want to store + * anything that might need out-of-line storage, since de-TOASTing won't + * work during authentication because we haven't selected a database yet + * and cannot read pg_class. 256 bytes should be more than enough for all + * practical use, so fail for anything longer. */ - elog(ERROR, "cannot encrypt password to requested type"); - return NULL; /* keep compiler quiet */ + if (encrypted_password && /* keep compiler quiet */ + strlen(encrypted_password) > MAX_ENCRYPTED_PASSWORD_LEN) + { + /* + * We don't expect any of our own hashing routines to produce hashes + * that are too long. + */ + Assert(guessed_type != PASSWORD_TYPE_PLAINTEXT); + + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("encrypted password is too long"), + errdetail("Encrypted passwords must be no longer than %d bytes.", + MAX_ENCRYPTED_PASSWORD_LEN))); + } + + return encrypted_password; } /* diff --git a/src/include/libpq/crypt.h b/src/include/libpq/crypt.h index f744de4d20..a2c99cd16a 100644 --- a/src/include/libpq/crypt.h +++ b/src/include/libpq/crypt.h @@ -15,6 +15,16 @@ #include "datatype/timestamp.h" +/* + * Valid password hashes may be very long, but we don't want to store anything + * that might need out-of-line storage, since de-TOASTing won't work during + * authentication because we haven't selected a database yet and cannot read + * pg_class. 256 bytes should be more than enough for all practical use, and + * our own password encryption routines should never produce hashes longer than + * this. + */ +#define MAX_ENCRYPTED_PASSWORD_LEN (256) + /* * Types of password hashes or secrets. * diff --git a/src/test/regress/expected/password.out b/src/test/regress/expected/password.out index 924d6e001d..dbfe92a27d 100644 --- a/src/test/regress/expected/password.out +++ b/src/test/regress/expected/password.out @@ -127,6 +127,13 @@ SELECT rolname, rolpassword not like '%A6xHKoH/494E941doaPOYg==%' as is_rolpassw regress_passwd_sha_len2 | t (3 rows) +-- Test that valid hashes that are too long are rejected +CREATE ROLE regress_passwd10 PASSWORD 'SCRAM-SHA-256$00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004096:NuDacwYSUxeOeFUEf3ivTQ==$Wgvq3OCYrJI6eUfvKlAzn4p/j3mzgWzXbVnWeFK1qhY=:r1qSP0j2QojCjLpFUjI0i6ckInvxJDKoyWnN3zF8WCM='; +ERROR: encrypted password is too long +DETAIL: Encrypted passwords must be no longer than 256 bytes. +ALTER ROLE regress_passwd9 PASSWORD 'SCRAM-SHA-256$00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004096:NuDacwYSUxeOeFUEf3ivTQ==$Wgvq3OCYrJI6eUfvKlAzn4p/j3mzgWzXbVnWeFK1qhY=:r1qSP0j2QojCjLpFUjI0i6ckInvxJDKoyWnN3zF8WCM='; +ERROR: encrypted password is too long +DETAIL: Encrypted passwords must be no longer than 256 bytes. DROP ROLE regress_passwd1; DROP ROLE regress_passwd2; DROP ROLE regress_passwd3; diff --git a/src/test/regress/sql/password.sql b/src/test/regress/sql/password.sql index bb82aa4aa2..442c903c00 100644 --- a/src/test/regress/sql/password.sql +++ b/src/test/regress/sql/password.sql @@ -95,6 +95,10 @@ SELECT rolname, rolpassword not like '%A6xHKoH/494E941doaPOYg==%' as is_rolpassw WHERE rolname LIKE 'regress_passwd_sha_len%' ORDER BY rolname; +-- Test that valid hashes that are too long are rejected +CREATE ROLE regress_passwd10 PASSWORD 'SCRAM-SHA-256$00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004096:NuDacwYSUxeOeFUEf3ivTQ==$Wgvq3OCYrJI6eUfvKlAzn4p/j3mzgWzXbVnWeFK1qhY=:r1qSP0j2QojCjLpFUjI0i6ckInvxJDKoyWnN3zF8WCM='; +ALTER ROLE regress_passwd9 PASSWORD 'SCRAM-SHA-256$00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004096:NuDacwYSUxeOeFUEf3ivTQ==$Wgvq3OCYrJI6eUfvKlAzn4p/j3mzgWzXbVnWeFK1qhY=:r1qSP0j2QojCjLpFUjI0i6ckInvxJDKoyWnN3zF8WCM='; + DROP ROLE regress_passwd1; DROP ROLE regress_passwd2; DROP ROLE regress_passwd3; -- 2.39.5 (Apple Git-154) --2R5fTxH63iv17WWy--