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
296 if (strcmp(client_pass, crypt_pwd) == 0)
297 {
298 retval = STATUS_OK;
299
301 {
302 MemoryContext oldcontext;
303 char *warning;
304 char *detail;
305
307
308 warning = pstrdup(_("authenticated with an MD5-encrypted password"));
309 detail = pstrdup(_("MD5 password support is deprecated and will be removed in a future release of PostgreSQL."));
311
312 MemoryContextSwitchTo(oldcontext);
313 }
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
325/*
326 * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
327 *
328 * 'shadow_pass' is the user's correct password hash, as stored in
329 * pg_authid.rolpassword.
330 * 'client_pass' is the password given by the remote user.
331 *
332 * In the error case, store a string at *logdetail that will be sent to the
333 * postmaster log (but not the client).
334 */
335int
336plain_crypt_verify(const char *role, const char *shadow_pass,
337 const char *client_pass,
338 const char **logdetail)
339{
341 const char *errstr = NULL;
342
343 /*
344 * Client sent password in plaintext. If we have an MD5 hash stored, hash
345 * the password the client sent, and compare the hashes. Otherwise
346 * compare the plaintext passwords directly.
347 */
349 {
354 {
355 return STATUS_OK;
356 }
357 else
358 {
359 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
360 role);
361 return STATUS_ERROR;
362 }
363 break;
364
367 (const uint8 *) role,
368 strlen(role),
370 &errstr))
371 {
372 *logdetail = errstr;
373 return STATUS_ERROR;
374 }
376 return STATUS_OK;
377 else
378 {
379 *logdetail = psprintf(_("Password does not match for user \"%s\"."),
380 role);
381 return STATUS_ERROR;
382 }
383 break;
384
386
387 /*
388 * We never store passwords in plaintext, so this shouldn't
389 * happen.
390 */
391 break;
392 }
393
394 /*
395 * This shouldn't happen. Plain "password" authentication is possible
396 * with any kind of stored password hash.
397 */
398 *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
399 role);
400 return STATUS_ERROR;
401}
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:84
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1645
Datum now(PG_FUNCTION_ARGS)
Definition timestamp.c:1609
#define TextDatumGetCString(d)
Definition builtins.h:99
#define STATUS_OK
Definition c.h:1186
uint8_t uint8
Definition c.h:556
#define ngettext(s, p, n)
Definition c.h:1198
#define Assert(condition)
Definition c.h:885
uint64_t uint64
Definition c.h:559
#define STATUS_ERROR
Definition c.h:1187
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:336
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:874
int errmsg(const char *fmt,...)
Definition elog.c:1093
#define _(x)
Definition elog.c:95
int errhint(const char *fmt,...) pg_attribute_printf(1
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define WARNING
Definition elog.h:36
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
#define HeapTupleIsValid(tuple)
Definition htup.h:78
char * pstrdup(const char *in)
Definition mcxt.c:1781
MemoryContext TopMemoryContext
Definition mcxt.c:166
void * palloc(Size size)
Definition mcxt.c:1387
#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 MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
uint64_t Datum
Definition postgres.h:70
void StoreConnectionWarning(char *msg, char *detail)
Definition postinit.c:1472
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:264
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:595
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:529