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
35/*
36 * Fetch stored password for a user, for authentication.
37 *
38 * On error, returns NULL, and stores a palloc'd string describing the reason,
39 * for the postmaster log, in *logdetail. The error reason should *not* be
40 * sent to the client, to avoid giving away user information!
41 */
42char *
43get_role_password(const char *role, const char **logdetail)
44{
47 Datum datum;
48 bool isnull;
49 char *shadow_pass;
50
51 /* Get role info from pg_authid */
54 {
55 *logdetail = psprintf(_("Role \"%s\" does not exist."),
56 role);
57 return NULL; /* no such user */
58 }
59
62 if (isnull)
63 {
65 *logdetail = psprintf(_("User \"%s\" has no password assigned."),
66 role);
67 return NULL; /* user has no password */
68 }
70
73 if (!isnull)
75
77
78 /*
79 * Password OK, but check to be sure we are not past rolvaliduntil or
80 * password_expiration_warning_threshold.
81 */
82 if (!isnull)
83 {
86
87 /*
88 * If we're past rolvaliduntil, the connection attempt should fail, so
89 * update logdetail and return NULL.
90 */
91 if (vuntil < now)
92 {
93 *logdetail = psprintf(_("User \"%s\" has an expired password."),
94 role);
95 return NULL;
96 }
97
98 /*
99 * If we're past the warning threshold, the connection attempt should
100 * succeed, but we still want to emit a warning. To do so, we queue
101 * the warning message using StoreConnectionWarning() so that it will
102 * be emitted at the end of InitPostgres(), and we return normally.
103 */
105 {
106 MemoryContext oldcontext;
107 int days;
108 int hours;
109 int minutes;
110 char *warning;
111 char *detail;
112
114
118
119 warning = pstrdup(_("role password will expire soon"));
120
121 if (days > 0)
122 detail = psprintf(ngettext("The password for role \"%s\" will expire in %d day.",
123 "The password for role \"%s\" will expire in %d days.",
124 days),
125 role, days);
126 else if (hours > 0)
127 detail = psprintf(ngettext("The password for role \"%s\" will expire in %d hour.",
128 "The password for role \"%s\" will expire in %d hours.",
129 hours),
130 role, hours);
131 else if (minutes > 0)
132 detail = psprintf(ngettext("The password for role \"%s\" will expire in %d minute.",
133 "The password for role \"%s\" will expire in %d minutes.",
134 minutes),
135 role, minutes);
136 else
137 detail = psprintf(_("The password for role \"%s\" will expire in less than 1 minute."),
138 role);
139
141
142 MemoryContextSwitchTo(oldcontext);
143 }
144 }
145
146 return shadow_pass;
147}
148
149/*
150 * What kind of a password type is 'shadow_pass'?
151 */
154{
155 char *encoded_salt;
156 int iterations;
157 int key_length = 0;
158 pg_cryptohash_type hash_type;
161
162 if (strncmp(shadow_pass, "md5", 3) == 0 &&
165 return PASSWORD_TYPE_MD5;
166 if (parse_scram_secret(shadow_pass, &iterations, &hash_type, &key_length,
170}
171
172/*
173 * Given a user-supplied password, convert it into a secret of
174 * 'target_type' kind.
175 *
176 * If the password is already in encrypted form, we cannot reverse the
177 * hash, so it is stored as it is regardless of the requested type.
178 */
179char *
180encrypt_password(PasswordType target_type, const char *role,
181 const char *password)
182{
184 char *encrypted_password = NULL;
185 const char *errstr = NULL;
186
188 {
189 /*
190 * Cannot convert an already-encrypted password from one format to
191 * another, so return it as it is.
192 */
194 }
195 else
196 {
197 switch (target_type)
198 {
201
202 if (!pg_md5_encrypt(password, (const uint8 *) role, strlen(role),
204 elog(ERROR, "password encryption failed: %s", errstr);
205 break;
206
209 break;
210
212 elog(ERROR, "cannot encrypt password with 'plaintext'");
213 break;
214 }
215 }
216
218
219 /*
220 * Valid password hashes may be very long, but we don't want to store
221 * anything that might need out-of-line storage, since de-TOASTing won't
222 * work during authentication because we haven't selected a database yet
223 * and cannot read pg_class. 512 bytes should be more than enough for all
224 * practical use, so fail for anything longer.
225 */
226 if (encrypted_password && /* keep compiler quiet */
228 {
229 /*
230 * We don't expect any of our own hashing routines to produce hashes
231 * that are too long.
232 */
234
237 errmsg("encrypted password is too long"),
238 errdetail("Encrypted passwords must be no longer than %d bytes.",
240 }
241
246 errmsg("setting an MD5-encrypted password"),
247 errdetail("MD5 password support is deprecated and will be removed in a future release of PostgreSQL."),
248 errhint("Refer to the PostgreSQL documentation for details about migrating to another password type.")));
249
250 return encrypted_password;
251}
252
253/*
254 * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
255 *
256 * 'shadow_pass' is the user's correct password or password hash, as stored
257 * in pg_authid.rolpassword.
258 * 'client_pass' is the response given by the remote user to the MD5 challenge.
259 * 'md5_salt' is the salt used in the MD5 authentication challenge.
260 *
261 * In the error case, save a string at *logdetail that will be sent to the
262 * postmaster log (but not the client).
263 */
264int
265md5_crypt_verify(const char *role, const char *shadow_pass,
266 const char *client_pass,
267 const uint8 *md5_salt, int md5_salt_len,
268 const char **logdetail)
269{
270 int retval;
271 char crypt_pwd[MD5_PASSWD_LEN + 1];
272 const char *errstr = NULL;
273
274 Assert(md5_salt_len > 0);
275
277 {
278 /* incompatible password hash format. */
279 *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
280 role);
281 return STATUS_ERROR;
282 }
283
284 /*
285 * Compute the correct answer for the MD5 challenge.
286 */
287 /* stored password already encrypted, only do salt */
288 if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
290 crypt_pwd, &errstr))
291 {
292 *logdetail = errstr;
293 return STATUS_ERROR;
294 }
295
298 {
299 retval = STATUS_OK;
300
302 {
303 MemoryContext oldcontext;
304 char *warning;
305 char *detail;
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 }
316 else
317 {
318 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
319 role);
320 retval = STATUS_ERROR;
321 }
322
323 return retval;
324}
325
326/*
327 * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
328 *
329 * 'shadow_pass' is the user's correct password hash, as stored in
330 * pg_authid.rolpassword.
331 * 'client_pass' is the password given by the remote user.
332 *
333 * In the error case, store a string at *logdetail that will be sent to the
334 * postmaster log (but not the client).
335 */
336int
337plain_crypt_verify(const char *role, const char *shadow_pass,
338 const char *client_pass,
339 const char **logdetail)
340{
342 const char *errstr = NULL;
343
344 /*
345 * Client sent password in plaintext. If we have an MD5 hash stored, hash
346 * the password the client sent, and compare the hashes. Otherwise
347 * compare the plaintext passwords directly.
348 */
350 {
355 {
356 return STATUS_OK;
357 }
358 else
359 {
360 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
361 role);
362 return STATUS_ERROR;
363 }
364 break;
365
368 (const uint8 *) role,
369 strlen(role),
371 &errstr))
372 {
373 *logdetail = errstr;
374 return STATUS_ERROR;
375 }
378 return STATUS_OK;
379 else
380 {
381 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
382 role);
383 return STATUS_ERROR;
384 }
385 break;
386
388
389 /*
390 * We never store passwords in plaintext, so this shouldn't
391 * happen.
392 */
393 break;
394 }
395
396 /*
397 * This shouldn't happen. Plain "password" authentication is possible
398 * with any kind of stored password hash.
399 */
400 *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
401 role);
402 return STATUS_ERROR;
403}
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:1258
uint8_t uint8
Definition c.h:622
#define ngettext(s, p, n)
Definition c.h:1270
#define Assert(condition)
Definition c.h:943
uint64_t uint64
Definition c.h:625
#define STATUS_ERROR
Definition c.h:1259
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:265
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:337
char * get_role_password(const char *role, const char **logdetail)
Definition crypt.c:43
bool md5_password_warnings
Definition crypt.c:33
PasswordType get_password_type(const char *shadow_pass)
Definition crypt.c:153
char * encrypt_password(PasswordType target_type, const char *role, const char *password)
Definition crypt.c:180
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)
Definition postinit.c:1508
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