1/*-------------------------------------------------------------------------
4 * Functions for dealing with encrypted passwords stored in
5 * pg_authid.rolpassword.
7 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
10 * src/backend/libpq/crypt.c
12 *-------------------------------------------------------------------------
27/* Enables deprecation warnings for MD5 passwords. */
31 * Fetch stored password for a user, for authentication.
33 * On error, returns NULL, and stores a palloc'd string describing the reason,
34 * for the postmaster log, in *logdetail. The error reason should *not* be
35 * sent to the client, to avoid giving away user information!
46 /* Get role info from pg_authid */
50 *logdetail =
psprintf(
_(
"Role \"%s\" does not exist."),
52 return NULL;
/* no such user */
56 Anum_pg_authid_rolpassword, &isnull);
60 *logdetail =
psprintf(
_(
"User \"%s\" has no password assigned."),
62 return NULL;
/* user has no password */
67 Anum_pg_authid_rolvaliduntil, &isnull);
74 * Password OK, but check to be sure we are not past rolvaliduntil
78 *logdetail =
psprintf(
_(
"User \"%s\" has an expired password."),
87 * What kind of a password type is 'shadow_pass'?
99 if (strncmp(shadow_pass,
"md5", 3) == 0 &&
104 &encoded_salt, stored_key, server_key))
110 * Given a user-supplied password, convert it into a secret of
111 * 'target_type' kind.
113 * If the password is already in encrypted form, we cannot reverse the
114 * hash, so it is stored as it is regardless of the requested type.
121 char *encrypted_password = NULL;
122 const char *errstr = NULL;
127 * Cannot convert an already-encrypted password from one format to
128 * another, so return it as it is.
140 encrypted_password, &errstr))
141 elog(
ERROR,
"password encryption failed: %s", errstr);
149 elog(
ERROR,
"cannot encrypt password with 'plaintext'");
154 Assert(encrypted_password);
157 * Valid password hashes may be very long, but we don't want to store
158 * anything that might need out-of-line storage, since de-TOASTing won't
159 * work during authentication because we haven't selected a database yet
160 * and cannot read pg_class. 512 bytes should be more than enough for all
161 * practical use, so fail for anything longer.
163 if (encrypted_password &&
/* keep compiler quiet */
167 * We don't expect any of our own hashing routines to produce hashes
173 (
errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
174 errmsg(
"encrypted password is too long"),
175 errdetail(
"Encrypted passwords must be no longer than %d bytes.",
182 (
errcode(ERRCODE_WARNING_DEPRECATED_FEATURE),
183 errmsg(
"setting an MD5-encrypted password"),
184 errdetail(
"MD5 password support is deprecated and will be removed in a future release of PostgreSQL."),
185 errhint(
"Refer to the PostgreSQL documentation for details about migrating to another password type.")));
187 return encrypted_password;
191 * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
193 * 'shadow_pass' is the user's correct password or password hash, as stored
194 * in pg_authid.rolpassword.
195 * 'client_pass' is the response given by the remote user to the MD5 challenge.
196 * 'md5_salt' is the salt used in the MD5 authentication challenge.
198 * In the error case, save a string at *logdetail that will be sent to the
199 * postmaster log (but not the client).
203 const char *client_pass,
204 const uint8 *md5_salt,
int md5_salt_len,
205 const char **logdetail)
209 const char *errstr = NULL;
215 /* incompatible password hash format. */
216 *logdetail =
psprintf(
_(
"User \"%s\" has a password that cannot be used with MD5 authentication."),
222 * Compute the correct answer for the MD5 challenge.
224 /* stored password already encrypted, only do salt */
226 md5_salt, md5_salt_len,
233 if (strcmp(client_pass, crypt_pwd) == 0)
237 *logdetail =
psprintf(
_(
"Password does not match for user \"%s\"."),
246 * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
248 * 'shadow_pass' is the user's correct password hash, as stored in
249 * pg_authid.rolpassword.
250 * 'client_pass' is the password given by the remote user.
252 * In the error case, store a string at *logdetail that will be sent to the
253 * postmaster log (but not the client).
257 const char *client_pass,
258 const char **logdetail)
261 const char *errstr = NULL;
264 * Client sent password in plaintext. If we have an MD5 hash stored, hash
265 * the password the client sent, and compare the hashes. Otherwise
266 * compare the plaintext passwords directly.
279 *logdetail =
psprintf(
_(
"Password does not match for user \"%s\"."),
295 if (strcmp(crypt_client_pass, shadow_pass) == 0)
299 *logdetail =
psprintf(
_(
"Password does not match for user \"%s\"."),
308 * We never store passwords in plaintext, so this shouldn't
315 * This shouldn't happen. Plain "password" authentication is possible
316 * with any kind of stored password hash.
318 *logdetail =
psprintf(
_(
"Password of user \"%s\" is in unrecognized format."),
bool parse_scram_secret(const char *secret, int *iterations, pg_cryptohash_type *hash_type, int *key_length, char **salt, uint8 *stored_key, uint8 *server_key)
char * pg_be_scram_build_secret(const char *password)
bool scram_verify_plain_password(const char *username, const char *password, const char *secret)
TimestampTz GetCurrentTimestamp(void)
#define TextDatumGetCString(d)
int md5_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, const uint8 *md5_salt, int md5_salt_len, const char **logdetail)
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, const char **logdetail)
char * get_role_password(const char *role, const char **logdetail)
bool md5_password_warnings
PasswordType get_password_type(const char *shadow_pass)
char * encrypt_password(PasswordType target_type, const char *role, const char *password)
@ PASSWORD_TYPE_PLAINTEXT
@ PASSWORD_TYPE_SCRAM_SHA_256
#define MAX_ENCRYPTED_PASSWORD_LEN
int errdetail(const char *fmt,...)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereport(elevel,...)
Assert(PointerIsAligned(start, uint64))
#define HeapTupleIsValid(tuple)
char * pstrdup(const char *in)
#define MD5_PASSWD_CHARSET
bool pg_md5_encrypt(const char *passwd, const uint8 *salt, size_t salt_len, char *buf, const char **errstr)
static Datum PointerGetDatum(const void *X)
char * psprintf(const char *fmt,...)
#define SCRAM_MAX_KEY_LEN
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
static TimestampTz DatumGetTimestampTz(Datum X)