PostgreSQL Source Code  git master
crypt.c File Reference
#include "postgres.h"
#include <unistd.h>
#include "catalog/pg_authid.h"
#include "common/md5.h"
#include "common/scram-common.h"
#include "libpq/crypt.h"
#include "libpq/scram.h"
#include "utils/builtins.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
Include dependency graph for crypt.c:

Go to the source code of this file.

Functions

char * get_role_password (const char *role, const char **logdetail)
 
PasswordType get_password_type (const char *shadow_pass)
 
char * encrypt_password (PasswordType target_type, const char *role, const char *password)
 
int md5_crypt_verify (const char *role, const char *shadow_pass, const char *client_pass, const char *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)
 

Function Documentation

◆ encrypt_password()

char* encrypt_password ( PasswordType  target_type,
const char *  role,
const char *  password 
)

Definition at line 115 of file crypt.c.

117 {
118  PasswordType guessed_type = get_password_type(password);
119  char *encrypted_password = NULL;
120  const char *errstr = NULL;
121 
122  if (guessed_type != PASSWORD_TYPE_PLAINTEXT)
123  {
124  /*
125  * Cannot convert an already-encrypted password from one format to
126  * another, so return it as it is.
127  */
128  encrypted_password = pstrdup(password);
129  }
130  else
131  {
132  switch (target_type)
133  {
134  case PASSWORD_TYPE_MD5:
135  encrypted_password = palloc(MD5_PASSWD_LEN + 1);
136 
137  if (!pg_md5_encrypt(password, role, strlen(role),
138  encrypted_password, &errstr))
139  elog(ERROR, "password encryption failed: %s", errstr);
140  break;
141 
143  encrypted_password = pg_be_scram_build_secret(password);
144  break;
145 
147  elog(ERROR, "cannot encrypt password with 'plaintext'");
148  break;
149  }
150  }
151 
152  Assert(encrypted_password);
153 
154  /*
155  * Valid password hashes may be very long, but we don't want to store
156  * anything that might need out-of-line storage, since de-TOASTing won't
157  * work during authentication because we haven't selected a database yet
158  * and cannot read pg_class. 512 bytes should be more than enough for all
159  * practical use, so fail for anything longer.
160  */
161  if (encrypted_password && /* keep compiler quiet */
162  strlen(encrypted_password) > MAX_ENCRYPTED_PASSWORD_LEN)
163  {
164  /*
165  * We don't expect any of our own hashing routines to produce hashes
166  * that are too long.
167  */
168  Assert(guessed_type != PASSWORD_TYPE_PLAINTEXT);
169 
170  ereport(ERROR,
171  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
172  errmsg("encrypted password is too long"),
173  errdetail("Encrypted passwords must be no longer than %d bytes.",
175  }
176 
177  return encrypted_password;
178 }
char * pg_be_scram_build_secret(const char *password)
Definition: auth-scram.c:474
#define Assert(condition)
Definition: c.h:849
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:88
PasswordType
Definition: crypt.h:38
@ PASSWORD_TYPE_PLAINTEXT
Definition: crypt.h:39
@ PASSWORD_TYPE_SCRAM_SHA_256
Definition: crypt.h:41
@ PASSWORD_TYPE_MD5
Definition: crypt.h:40
#define MAX_ENCRYPTED_PASSWORD_LEN
Definition: crypt.h:26
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
char * pstrdup(const char *in)
Definition: mcxt.c:1696
void * palloc(Size size)
Definition: mcxt.c:1317
#define MD5_PASSWD_LEN
Definition: md5.h:26
bool pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, char *buf, const char **errstr)
Definition: md5_common.c:145
static char * password
Definition: streamutil.c:54

References Assert, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, get_password_type(), MAX_ENCRYPTED_PASSWORD_LEN, MD5_PASSWD_LEN, palloc(), password, PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, PASSWORD_TYPE_SCRAM_SHA_256, pg_be_scram_build_secret(), pg_md5_encrypt(), and pstrdup().

Referenced by AlterRole(), and CreateRole().

◆ get_password_type()

PasswordType get_password_type ( const char *  shadow_pass)

Definition at line 88 of file crypt.c.

89 {
90  char *encoded_salt;
91  int iterations;
92  int key_length = 0;
93  pg_cryptohash_type hash_type;
94  uint8 stored_key[SCRAM_MAX_KEY_LEN];
95  uint8 server_key[SCRAM_MAX_KEY_LEN];
96 
97  if (strncmp(shadow_pass, "md5", 3) == 0 &&
98  strlen(shadow_pass) == MD5_PASSWD_LEN &&
99  strspn(shadow_pass + 3, MD5_PASSWD_CHARSET) == MD5_PASSWD_LEN - 3)
100  return PASSWORD_TYPE_MD5;
101  if (parse_scram_secret(shadow_pass, &iterations, &hash_type, &key_length,
102  &encoded_salt, stored_key, server_key))
105 }
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:591
unsigned char uint8
Definition: c.h:504
pg_cryptohash_type
Definition: cryptohash.h:20
#define MD5_PASSWD_CHARSET
Definition: md5.h:25
#define SCRAM_MAX_KEY_LEN
Definition: scram-common.h:30
int iterations
Definition: thread-thread.c:39

References iterations, MD5_PASSWD_CHARSET, MD5_PASSWD_LEN, parse_scram_secret(), PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, PASSWORD_TYPE_SCRAM_SHA_256, and SCRAM_MAX_KEY_LEN.

Referenced by AlterRole(), CheckPWChallengeAuth(), CreateRole(), encrypt_password(), md5_crypt_verify(), plain_crypt_verify(), RenameRole(), and scram_init().

◆ get_role_password()

char* get_role_password ( const char *  role,
const char **  logdetail 
)

Definition at line 36 of file crypt.c.

37 {
38  TimestampTz vuntil = 0;
39  HeapTuple roleTup;
40  Datum datum;
41  bool isnull;
42  char *shadow_pass;
43 
44  /* Get role info from pg_authid */
45  roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
46  if (!HeapTupleIsValid(roleTup))
47  {
48  *logdetail = psprintf(_("Role \"%s\" does not exist."),
49  role);
50  return NULL; /* no such user */
51  }
52 
53  datum = SysCacheGetAttr(AUTHNAME, roleTup,
54  Anum_pg_authid_rolpassword, &isnull);
55  if (isnull)
56  {
57  ReleaseSysCache(roleTup);
58  *logdetail = psprintf(_("User \"%s\" has no password assigned."),
59  role);
60  return NULL; /* user has no password */
61  }
62  shadow_pass = TextDatumGetCString(datum);
63 
64  datum = SysCacheGetAttr(AUTHNAME, roleTup,
65  Anum_pg_authid_rolvaliduntil, &isnull);
66  if (!isnull)
67  vuntil = DatumGetTimestampTz(datum);
68 
69  ReleaseSysCache(roleTup);
70 
71  /*
72  * Password OK, but check to be sure we are not past rolvaliduntil
73  */
74  if (!isnull && vuntil < GetCurrentTimestamp())
75  {
76  *logdetail = psprintf(_("User \"%s\" has an expired password."),
77  role);
78  return NULL;
79  }
80 
81  return shadow_pass;
82 }
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1644
#define TextDatumGetCString(d)
Definition: builtins.h:98
int64 TimestampTz
Definition: timestamp.h:39
#define _(x)
Definition: elog.c:90
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:596
static TimestampTz DatumGetTimestampTz(Datum X)
Definition: timestamp.h:34

References _, DatumGetTimestampTz(), GetCurrentTimestamp(), HeapTupleIsValid, PointerGetDatum(), psprintf(), ReleaseSysCache(), SearchSysCache1(), SysCacheGetAttr(), and TextDatumGetCString.

Referenced by CheckPasswordAuth(), and CheckPWChallengeAuth().

◆ md5_crypt_verify()

int md5_crypt_verify ( const char *  role,
const char *  shadow_pass,
const char *  client_pass,
const char *  md5_salt,
int  md5_salt_len,
const char **  logdetail 
)

Definition at line 192 of file crypt.c.

196 {
197  int retval;
198  char crypt_pwd[MD5_PASSWD_LEN + 1];
199  const char *errstr = NULL;
200 
201  Assert(md5_salt_len > 0);
202 
203  if (get_password_type(shadow_pass) != PASSWORD_TYPE_MD5)
204  {
205  /* incompatible password hash format. */
206  *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
207  role);
208  return STATUS_ERROR;
209  }
210 
211  /*
212  * Compute the correct answer for the MD5 challenge.
213  */
214  /* stored password already encrypted, only do salt */
215  if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
216  md5_salt, md5_salt_len,
217  crypt_pwd, &errstr))
218  {
219  *logdetail = errstr;
220  return STATUS_ERROR;
221  }
222 
223  if (strcmp(client_pass, crypt_pwd) == 0)
224  retval = STATUS_OK;
225  else
226  {
227  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
228  role);
229  retval = STATUS_ERROR;
230  }
231 
232  return retval;
233 }
#define STATUS_OK
Definition: c.h:1160
#define STATUS_ERROR
Definition: c.h:1161

References _, Assert, get_password_type(), MD5_PASSWD_LEN, PASSWORD_TYPE_MD5, pg_md5_encrypt(), psprintf(), STATUS_ERROR, and STATUS_OK.

Referenced by CheckMD5Auth().

◆ plain_crypt_verify()

int plain_crypt_verify ( const char *  role,
const char *  shadow_pass,
const char *  client_pass,
const char **  logdetail 
)

Definition at line 246 of file crypt.c.

249 {
250  char crypt_client_pass[MD5_PASSWD_LEN + 1];
251  const char *errstr = NULL;
252 
253  /*
254  * Client sent password in plaintext. If we have an MD5 hash stored, hash
255  * the password the client sent, and compare the hashes. Otherwise
256  * compare the plaintext passwords directly.
257  */
258  switch (get_password_type(shadow_pass))
259  {
262  client_pass,
263  shadow_pass))
264  {
265  return STATUS_OK;
266  }
267  else
268  {
269  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
270  role);
271  return STATUS_ERROR;
272  }
273  break;
274 
275  case PASSWORD_TYPE_MD5:
276  if (!pg_md5_encrypt(client_pass,
277  role,
278  strlen(role),
279  crypt_client_pass,
280  &errstr))
281  {
282  *logdetail = errstr;
283  return STATUS_ERROR;
284  }
285  if (strcmp(crypt_client_pass, shadow_pass) == 0)
286  return STATUS_OK;
287  else
288  {
289  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
290  role);
291  return STATUS_ERROR;
292  }
293  break;
294 
296 
297  /*
298  * We never store passwords in plaintext, so this shouldn't
299  * happen.
300  */
301  break;
302  }
303 
304  /*
305  * This shouldn't happen. Plain "password" authentication is possible
306  * with any kind of stored password hash.
307  */
308  *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
309  role);
310  return STATUS_ERROR;
311 }
bool scram_verify_plain_password(const char *username, const char *password, const char *secret)
Definition: auth-scram.c:514

References _, get_password_type(), MD5_PASSWD_LEN, PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, PASSWORD_TYPE_SCRAM_SHA_256, pg_md5_encrypt(), psprintf(), scram_verify_plain_password(), STATUS_ERROR, and STATUS_OK.

Referenced by AlterRole(), check_password(), CheckPasswordAuth(), and CreateRole().