PostgreSQL Source Code git master
Loading...
Searching...
No Matches
crypt.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * crypt.c
4 * Functions for dealing with encrypted passwords stored in
5 * pg_authid.rolpassword.
6 *
7 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
9 *
10 * src/backend/libpq/crypt.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include <unistd.h>
17
18#include "catalog/pg_authid.h"
19#include "common/md5.h"
20#include "common/scram-common.h"
21#include "libpq/crypt.h"
22#include "libpq/scram.h"
23#include "miscadmin.h"
24#include "utils/builtins.h"
25#include "utils/memutils.h"
26#include "utils/syscache.h"
27#include "utils/timestamp.h"
28
29/* Threshold for password expiration warnings. */
31
32/* Enables deprecation warnings for MD5 passwords. */
34
35static bool md5_password_warning_enabled(void);
36
37/*
38 * Fetch stored password for a user, for authentication.
39 *
40 * On error, returns NULL, and stores a palloc'd string describing the reason,
41 * for the postmaster log, in *logdetail. The error reason should *not* be
42 * sent to the client, to avoid giving away user information!
43 */
44char *
45get_role_password(const char *role, const char **logdetail)
46{
49 Datum datum;
50 bool isnull;
51 char *shadow_pass;
52
53 /* Get role info from pg_authid */
56 {
57 *logdetail = psprintf(_("Role \"%s\" does not exist."),
58 role);
59 return NULL; /* no such user */
60 }
61
64 if (isnull)
65 {
67 *logdetail = psprintf(_("User \"%s\" has no password assigned."),
68 role);
69 return NULL; /* user has no password */
70 }
72
75 if (!isnull)
77
79
80 /*
81 * Password OK, but check to be sure we are not past rolvaliduntil or
82 * password_expiration_warning_threshold.
83 */
84 if (!isnull)
85 {
88
89 /*
90 * If we're past rolvaliduntil, the connection attempt should fail, so
91 * update logdetail and return NULL.
92 */
93 if (vuntil < now)
94 {
95 *logdetail = psprintf(_("User \"%s\" has an expired password."),
96 role);
97 return NULL;
98 }
99
100 /*
101 * If we're past the warning threshold, the connection attempt should
102 * succeed, but we still want to emit a warning. To do so, we queue
103 * the warning message using StoreConnectionWarning() so that it will
104 * be emitted at the end of InitPostgres(), and we return normally.
105 */
107 {
108 MemoryContext oldcontext;
109 int days;
110 int hours;
111 int minutes;
112 char *warning;
113 char *detail;
114
116
120
121 warning = pstrdup(_("role password will expire soon"));
122
123 if (days > 0)
124 detail = psprintf(ngettext("The password for role \"%s\" will expire in %d day.",
125 "The password for role \"%s\" will expire in %d days.",
126 days),
127 role, days);
128 else if (hours > 0)
129 detail = psprintf(ngettext("The password for role \"%s\" will expire in %d hour.",
130 "The password for role \"%s\" will expire in %d hours.",
131 hours),
132 role, hours);
133 else if (minutes > 0)
134 detail = psprintf(ngettext("The password for role \"%s\" will expire in %d minute.",
135 "The password for role \"%s\" will expire in %d minutes.",
136 minutes),
137 role, minutes);
138 else
139 detail = psprintf(_("The password for role \"%s\" will expire in less than 1 minute."),
140 role);
141
143
144 MemoryContextSwitchTo(oldcontext);
145 }
146 }
147
148 return shadow_pass;
149}
150
151/*
152 * What kind of a password type is 'shadow_pass'?
153 */
156{
157 char *encoded_salt;
158 int iterations;
159 int key_length = 0;
160 pg_cryptohash_type hash_type;
163
164 if (strncmp(shadow_pass, "md5", 3) == 0 &&
167 return PASSWORD_TYPE_MD5;
168 if (parse_scram_secret(shadow_pass, &iterations, &hash_type, &key_length,
172}
173
174/*
175 * Given a user-supplied password, convert it into a secret of
176 * 'target_type' kind.
177 *
178 * If the password is already in encrypted form, we cannot reverse the
179 * hash, so it is stored as it is regardless of the requested type.
180 */
181char *
182encrypt_password(PasswordType target_type, const char *role,
183 const char *password)
184{
186 char *encrypted_password = NULL;
187 const char *errstr = NULL;
188
190 {
191 /*
192 * Cannot convert an already-encrypted password from one format to
193 * another, so return it as it is.
194 */
196 }
197 else
198 {
199 switch (target_type)
200 {
203
204 if (!pg_md5_encrypt(password, (const uint8 *) role, strlen(role),
206 elog(ERROR, "password encryption failed: %s", errstr);
207 break;
208
211 break;
212
214 elog(ERROR, "cannot encrypt password with 'plaintext'");
215 break;
216 }
217 }
218
220
221 /*
222 * Valid password hashes may be very long, but we don't want to store
223 * anything that might need out-of-line storage, since de-TOASTing won't
224 * work during authentication because we haven't selected a database yet
225 * and cannot read pg_class. 512 bytes should be more than enough for all
226 * practical use, so fail for anything longer.
227 */
228 if (encrypted_password && /* keep compiler quiet */
230 {
231 /*
232 * We don't expect any of our own hashing routines to produce hashes
233 * that are too long.
234 */
236
239 errmsg("encrypted password is too long"),
240 errdetail("Encrypted passwords must be no longer than %d bytes.",
242 }
243
248 errmsg("setting an MD5-encrypted password"),
249 errdetail("MD5 password support is deprecated and will be removed in a future release of PostgreSQL."),
250 errhint("Refer to the PostgreSQL documentation for details about migrating to another password type.")));
251
252 return encrypted_password;
253}
254
255/*
256 * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
257 *
258 * 'shadow_pass' is the user's correct password or password hash, as stored
259 * in pg_authid.rolpassword.
260 * 'client_pass' is the response given by the remote user to the MD5 challenge.
261 * 'md5_salt' is the salt used in the MD5 authentication challenge.
262 *
263 * In the error case, save a string at *logdetail that will be sent to the
264 * postmaster log (but not the client).
265 */
266int
267md5_crypt_verify(const char *role, const char *shadow_pass,
268 const char *client_pass,
269 const uint8 *md5_salt, int md5_salt_len,
270 const char **logdetail)
271{
272 int retval;
273 char crypt_pwd[MD5_PASSWD_LEN + 1];
274 const char *errstr = NULL;
275
276 Assert(md5_salt_len > 0);
277
279 {
280 /* incompatible password hash format. */
281 *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
282 role);
283 return STATUS_ERROR;
284 }
285
286 /*
287 * Compute the correct answer for the MD5 challenge.
288 */
289 /* stored password already encrypted, only do salt */
290 if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
292 crypt_pwd, &errstr))
293 {
294 *logdetail = errstr;
295 return STATUS_ERROR;
296 }
297
300 {
301 MemoryContext oldcontext;
302 char *warning;
303 char *detail;
304
305 retval = STATUS_OK;
306
308
309 warning = pstrdup(_("authenticated with an MD5-encrypted password"));
310 detail = pstrdup(_("MD5 password support is deprecated and will be removed in a future release of PostgreSQL."));
312
313 MemoryContextSwitchTo(oldcontext);
314 }
315 else
316 {
317 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
318 role);
319 retval = STATUS_ERROR;
320 }
321
322 return retval;
323}
324
325static bool
330
331/*
332 * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
333 *
334 * 'shadow_pass' is the user's correct password hash, as stored in
335 * pg_authid.rolpassword.
336 * 'client_pass' is the password given by the remote user.
337 *
338 * In the error case, store a string at *logdetail that will be sent to the
339 * postmaster log (but not the client).
340 */
341int
342plain_crypt_verify(const char *role, const char *shadow_pass,
343 const char *client_pass,
344 const char **logdetail)
345{
347 const char *errstr = NULL;
348
349 /*
350 * Client sent password in plaintext. If we have an MD5 hash stored, hash
351 * the password the client sent, and compare the hashes. Otherwise
352 * compare the plaintext passwords directly.
353 */
355 {
360 {
361 return STATUS_OK;
362 }
363 else
364 {
365 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
366 role);
367 return STATUS_ERROR;
368 }
369 break;
370
373 (const uint8 *) role,
374 strlen(role),
376 &errstr))
377 {
378 *logdetail = errstr;
379 return STATUS_ERROR;
380 }
383 return STATUS_OK;
384 else
385 {
386 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
387 role);
388 return STATUS_ERROR;
389 }
390 break;
391
393
394 /*
395 * We never store passwords in plaintext, so this shouldn't
396 * happen.
397 */
398 break;
399 }
400
401 /*
402 * This shouldn't happen. Plain "password" authentication is possible
403 * with any kind of stored password hash.
404 */
405 *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
406 role);
407 return STATUS_ERROR;
408}
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)
Definition auth-scram.c:598
char * pg_be_scram_build_secret(const char *password)
Definition auth-scram.c:481
bool scram_verify_plain_password(const char *username, const char *password, const char *secret)
Definition auth-scram.c:521
const char *const days[]
Definition datetime.c:85
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1649
Datum now(PG_FUNCTION_ARGS)
Definition timestamp.c:1613
#define TextDatumGetCString(d)
Definition builtins.h:99
#define STATUS_OK
Definition c.h:1314
uint8_t uint8
Definition c.h:678
#define ngettext(s, p, n)
Definition c.h:1326
#define Assert(condition)
Definition c.h:999
uint64_t uint64
Definition c.h:681
#define STATUS_ERROR
Definition c.h:1315
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)
Definition crypt.c:267
int password_expiration_warning_threshold
Definition crypt.c:30
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, const char **logdetail)
Definition crypt.c:342
char * get_role_password(const char *role, const char **logdetail)
Definition crypt.c:45
bool md5_password_warnings
Definition crypt.c:33
PasswordType get_password_type(const char *shadow_pass)
Definition crypt.c:155
static bool md5_password_warning_enabled(void)
Definition crypt.c:326
char * encrypt_password(PasswordType target_type, const char *role, const char *password)
Definition crypt.c:182
PasswordType
Definition crypt.h:44
@ PASSWORD_TYPE_PLAINTEXT
Definition crypt.h:45
@ PASSWORD_TYPE_SCRAM_SHA_256
Definition crypt.h:47
@ PASSWORD_TYPE_MD5
Definition crypt.h:46
#define MAX_ENCRYPTED_PASSWORD_LEN
Definition crypt.h:26
pg_cryptohash_type
Definition cryptohash.h:20
int64 TimestampTz
Definition timestamp.h:39
#define USECS_PER_HOUR
Definition timestamp.h:132
#define USECS_PER_DAY
Definition timestamp.h:131
#define USECS_PER_SEC
Definition timestamp.h:134
#define USECS_PER_MINUTE
Definition timestamp.h:133
int errcode(int sqlerrcode)
Definition elog.c:875
#define _(x)
Definition elog.c:96
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:37
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
#define HeapTupleIsValid(tuple)
Definition htup.h:78
char * pstrdup(const char *in)
Definition mcxt.c:1910
MemoryContext TopMemoryContext
Definition mcxt.c:167
void * palloc(Size size)
Definition mcxt.c:1390
#define MD5_PASSWD_CHARSET
Definition md5.h:25
#define MD5_PASSWD_LEN
Definition md5.h:26
bool pg_md5_encrypt(const char *passwd, const uint8 *salt, size_t salt_len, char *buf, const char **errstr)
Definition md5_common.c:145
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
int timingsafe_bcmp(const void *b1, const void *b2, size_t n)
uint64_t Datum
Definition postgres.h:70
#define PointerGetDatum(X)
Definition postgres.h:354
void StoreConnectionWarning(char *msg, char *detail, ConnectionWarningFilter filter)
Definition postinit.c:1517
static int fb(int x)
char * psprintf(const char *fmt,...)
Definition psprintf.c:43
#define SCRAM_MAX_KEY_LEN
static char * password
Definition streamutil.c:51
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596
int iterations
static uint64 TimestampDifferenceMicroseconds(TimestampTz start_time, TimestampTz stop_time)
Definition timestamp.h:90
static TimestampTz DatumGetTimestampTz(Datum X)
Definition timestamp.h:34
static void static void static void warning(const char *const string,...) pg_attribute_printf(1
Definition zic.c:533