PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
crypt.c File Reference
#include "postgres.h"
#include <unistd.h>
#include "catalog/pg_authid.h"
#include "common/md5.h"
#include "libpq/crypt.h"
#include "libpq/scram.h"
#include "miscadmin.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, 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, char **logdetail)
 
int plain_crypt_verify (const char *role, const char *shadow_pass, const char *client_pass, char **logdetail)
 

Function Documentation

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

Definition at line 117 of file crypt.c.

References elog, ERROR, get_password_type(), MD5_PASSWD_LEN, NULL, palloc(), PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, PASSWORD_TYPE_SCRAM, pg_md5_encrypt(), pstrdup(), and scram_build_verifier().

Referenced by AlterRole(), and CreateRole().

119 {
120  PasswordType guessed_type = get_password_type(password);
121  char *encrypted_password;
122 
123  switch (target_type)
124  {
126 
127  /*
128  * We cannot convert a hashed password back to plaintext, so just
129  * store the password as it was, whether it was hashed or not.
130  */
131  return pstrdup(password);
132 
133  case PASSWORD_TYPE_MD5:
134  switch (guessed_type)
135  {
137  encrypted_password = palloc(MD5_PASSWD_LEN + 1);
138 
139  if (!pg_md5_encrypt(password, role, strlen(role),
140  encrypted_password))
141  elog(ERROR, "password encryption failed");
142  return encrypted_password;
143 
144  case PASSWORD_TYPE_SCRAM:
145 
146  /*
147  * cannot convert a SCRAM verifier to an MD5 hash, so fall
148  * through to save the SCRAM verifier instead.
149  */
150  case PASSWORD_TYPE_MD5:
151  return pstrdup(password);
152  }
153  break;
154 
155  case PASSWORD_TYPE_SCRAM:
156  switch (guessed_type)
157  {
159  return scram_build_verifier(role, password, 0);
160 
161  case PASSWORD_TYPE_MD5:
162 
163  /*
164  * cannot convert an MD5 hash to a SCRAM verifier, so fall
165  * through to save the MD5 hash instead.
166  */
167  case PASSWORD_TYPE_SCRAM:
168  return pstrdup(password);
169  }
170  break;
171  }
172 
173  /*
174  * This shouldn't happen, because the above switch statements should
175  * handle every combination of source and target password types.
176  */
177  elog(ERROR, "cannot encrypt password to requested type");
178  return NULL; /* keep compiler quiet */
179 }
static char password[100]
Definition: streamutil.c:41
char * scram_build_verifier(const char *username, const char *password, int iterations)
Definition: auth-scram.c:338
char * pstrdup(const char *in)
Definition: mcxt.c:1077
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:99
bool pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, char *buf)
Definition: md5.c:323
#define ERROR
Definition: elog.h:43
PasswordType
Definition: crypt.h:24
#define MD5_PASSWD_LEN
Definition: md5.h:19
#define NULL
Definition: c.h:229
void * palloc(Size size)
Definition: mcxt.c:849
#define elog
Definition: elog.h:219
PasswordType get_password_type ( const char *  shadow_pass)

Definition at line 99 of file crypt.c.

References MD5_PASSWD_LEN, PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, and PASSWORD_TYPE_SCRAM.

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

100 {
101  if (strncmp(shadow_pass, "md5", 3) == 0 && strlen(shadow_pass) == MD5_PASSWD_LEN)
102  return PASSWORD_TYPE_MD5;
103  if (strncmp(shadow_pass, "scram-sha-256:", strlen("scram-sha-256:")) == 0)
104  return PASSWORD_TYPE_SCRAM;
106 }
#define MD5_PASSWD_LEN
Definition: md5.h:19
char* get_role_password ( const char *  role,
char **  logdetail 
)

Definition at line 39 of file crypt.c.

References _, Anum_pg_authid_rolpassword, Anum_pg_authid_rolvaliduntil, AUTHNAME, DatumGetTimestampTz, GetCurrentTimestamp(), HeapTupleIsValid, NULL, pfree(), PointerGetDatum, psprintf(), ReleaseSysCache(), SearchSysCache1, SysCacheGetAttr(), and TextDatumGetCString.

Referenced by CheckPasswordAuth(), and CheckPWChallengeAuth().

40 {
41  TimestampTz vuntil = 0;
42  HeapTuple roleTup;
43  Datum datum;
44  bool isnull;
45  char *shadow_pass;
46 
47  /* Get role info from pg_authid */
48  roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
49  if (!HeapTupleIsValid(roleTup))
50  {
51  *logdetail = psprintf(_("Role \"%s\" does not exist."),
52  role);
53  return NULL; /* no such user */
54  }
55 
56  datum = SysCacheGetAttr(AUTHNAME, roleTup,
58  if (isnull)
59  {
60  ReleaseSysCache(roleTup);
61  *logdetail = psprintf(_("User \"%s\" has no password assigned."),
62  role);
63  return NULL; /* user has no password */
64  }
65  shadow_pass = TextDatumGetCString(datum);
66 
67  datum = SysCacheGetAttr(AUTHNAME, roleTup,
69  if (!isnull)
70  vuntil = DatumGetTimestampTz(datum);
71 
72  ReleaseSysCache(roleTup);
73 
74  if (*shadow_pass == '\0')
75  {
76  *logdetail = psprintf(_("User \"%s\" has an empty password."),
77  role);
78  pfree(shadow_pass);
79  return NULL; /* empty password */
80  }
81 
82  /*
83  * Password OK, but check to be sure we are not past rolvaliduntil
84  */
85  if (!isnull && vuntil < GetCurrentTimestamp())
86  {
87  *logdetail = psprintf(_("User \"%s\" has an expired password."),
88  role);
89  return NULL;
90  }
91 
92  return shadow_pass;
93 }
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1569
int64 TimestampTz
Definition: timestamp.h:39
#define PointerGetDatum(X)
Definition: postgres.h:562
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define Anum_pg_authid_rolpassword
Definition: pg_authid.h:88
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:152
void pfree(void *pointer)
Definition: mcxt.c:950
#define Anum_pg_authid_rolvaliduntil
Definition: pg_authid.h:89
#define DatumGetTimestampTz(X)
Definition: timestamp.h:28
#define TextDatumGetCString(d)
Definition: builtins.h:92
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1116
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1278
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define _(x)
Definition: elog.c:84
int md5_crypt_verify ( const char *  role,
const char *  shadow_pass,
const char *  client_pass,
const char *  md5_salt,
int  md5_salt_len,
char **  logdetail 
)

Definition at line 193 of file crypt.c.

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

Referenced by CheckMD5Auth().

197 {
198  int retval;
199  char crypt_pwd[MD5_PASSWD_LEN + 1];
200  char crypt_pwd2[MD5_PASSWD_LEN + 1];
201 
202  Assert(md5_salt_len > 0);
203 
204  /*
205  * Compute the correct answer for the MD5 challenge.
206  *
207  * We do not bother setting logdetail for any pg_md5_encrypt failure
208  * below: the only possible error is out-of-memory, which is unlikely, and
209  * if it did happen adding a psprintf call would only make things worse.
210  */
211  switch (get_password_type(shadow_pass))
212  {
213  case PASSWORD_TYPE_MD5:
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))
218  {
219  return STATUS_ERROR;
220  }
221  break;
222 
224  /* stored password is plain, double-encrypt */
225  if (!pg_md5_encrypt(shadow_pass,
226  role,
227  strlen(role),
228  crypt_pwd2))
229  {
230  return STATUS_ERROR;
231  }
232  if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
233  md5_salt, md5_salt_len,
234  crypt_pwd))
235  {
236  return STATUS_ERROR;
237  }
238  break;
239 
240  default:
241  /* unknown password hash format. */
242  *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
243  role);
244  return STATUS_ERROR;
245  }
246 
247  if (strcmp(client_pass, crypt_pwd) == 0)
248  retval = STATUS_OK;
249  else
250  {
251  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
252  role);
253  retval = STATUS_ERROR;
254  }
255 
256  return retval;
257 }
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:99
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define STATUS_ERROR
Definition: c.h:976
bool pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, char *buf)
Definition: md5.c:323
#define STATUS_OK
Definition: c.h:975
#define MD5_PASSWD_LEN
Definition: md5.h:19
#define Assert(condition)
Definition: c.h:675
#define _(x)
Definition: elog.c:84
int plain_crypt_verify ( const char *  role,
const char *  shadow_pass,
const char *  client_pass,
char **  logdetail 
)

Definition at line 270 of file crypt.c.

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

Referenced by check_password(), and CheckPasswordAuth().

273 {
274  char crypt_client_pass[MD5_PASSWD_LEN + 1];
275 
276  /*
277  * Client sent password in plaintext. If we have an MD5 hash stored, hash
278  * the password the client sent, and compare the hashes. Otherwise
279  * compare the plaintext passwords directly.
280  */
281  switch (get_password_type(shadow_pass))
282  {
283  case PASSWORD_TYPE_SCRAM:
285  client_pass,
286  shadow_pass))
287  {
288  return STATUS_OK;
289  }
290  else
291  {
292  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
293  role);
294  return STATUS_ERROR;
295  }
296  break;
297 
298  case PASSWORD_TYPE_MD5:
299  if (!pg_md5_encrypt(client_pass,
300  role,
301  strlen(role),
302  crypt_client_pass))
303  {
304  /*
305  * We do not bother setting logdetail for pg_md5_encrypt
306  * failure: the only possible error is out-of-memory, which is
307  * unlikely, and if it did happen adding a psprintf call would
308  * only make things worse.
309  */
310  return STATUS_ERROR;
311  }
312  if (strcmp(crypt_client_pass, shadow_pass) == 0)
313  return STATUS_OK;
314  else
315  {
316  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
317  role);
318  return STATUS_ERROR;
319  }
320  break;
321 
323  if (strcmp(client_pass, shadow_pass) == 0)
324  return STATUS_OK;
325  else
326  {
327  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
328  role);
329  return STATUS_ERROR;
330  }
331  break;
332  }
333 
334  /*
335  * This shouldn't happen. Plain "password" authentication is possible
336  * with any kind of stored password hash.
337  */
338  *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
339  role);
340  return STATUS_ERROR;
341 }
bool scram_verify_plain_password(const char *username, const char *password, const char *verifier)
Definition: auth-scram.c:385
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:99
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define STATUS_ERROR
Definition: c.h:976
bool pg_md5_encrypt(const char *passwd, const char *salt, size_t salt_len, char *buf)
Definition: md5.c:323
#define STATUS_OK
Definition: c.h:975
#define MD5_PASSWD_LEN
Definition: md5.h:19
#define _(x)
Definition: elog.c:84