PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2017, 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 #ifdef HAVE_CRYPT_H
18 #include <crypt.h>
19 #endif
20 
21 #include "catalog/pg_authid.h"
22 #include "common/md5.h"
23 #include "libpq/crypt.h"
24 #include "libpq/scram.h"
25 #include "miscadmin.h"
26 #include "utils/builtins.h"
27 #include "utils/syscache.h"
28 #include "utils/timestamp.h"
29 
30 
31 /*
32  * Fetch stored password for a user, for authentication.
33  *
34  * On error, returns NULL, and stores a palloc'd string describing the reason,
35  * for the postmaster log, in *logdetail. The error reason should *not* be
36  * sent to the client, to avoid giving away user information!
37  */
38 char *
39 get_role_password(const char *role, char **logdetail)
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 }
94 
95 /*
96  * What kind of a password verifier is 'shadow_pass'?
97  */
99 get_password_type(const char *shadow_pass)
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 }
107 
108 /*
109  * Given a user-supplied password, convert it into a verifier of
110  * 'target_type' kind.
111  *
112  * If the password looks like a valid MD5 hash, it is stored as it is.
113  * We cannot reverse the hash, so even if the caller requested a plaintext
114  * plaintext password, the MD5 hash is returned.
115  */
116 char *
117 encrypt_password(PasswordType target_type, const char *role,
118  const char *password)
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 }
180 
181 /*
182  * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
183  *
184  * 'shadow_pass' is the user's correct password or password hash, as stored
185  * in pg_authid.rolpassword.
186  * 'client_pass' is the response given by the remote user to the MD5 challenge.
187  * 'md5_salt' is the salt used in the MD5 authentication challenge.
188  *
189  * In the error case, optionally store a palloc'd string at *logdetail
190  * that will be sent to the postmaster log (but not the client).
191  */
192 int
193 md5_crypt_verify(const char *role, const char *shadow_pass,
194  const char *client_pass,
195  const char *md5_salt, int md5_salt_len,
196  char **logdetail)
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 }
258 
259 /*
260  * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
261  *
262  * 'shadow_pass' is the user's correct password or password hash, as stored
263  * in pg_authid.rolpassword.
264  * 'client_pass' is the password given by the remote user.
265  *
266  * In the error case, optionally store a palloc'd string at *logdetail
267  * that will be sent to the postmaster log (but not the client).
268  */
269 int
270 plain_crypt_verify(const char *role, const char *shadow_pass,
271  const char *client_pass,
272  char **logdetail)
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 }
static char password[100]
Definition: streamutil.c:41
bool scram_verify_plain_password(const char *username, const char *password, const char *verifier)
Definition: auth-scram.c:385
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1569
int64 TimestampTz
Definition: timestamp.h:39
char * scram_build_verifier(const char *username, const char *password, int iterations)
Definition: auth-scram.c:338
#define PointerGetDatum(X)
Definition: postgres.h:562
char * pstrdup(const char *in)
Definition: mcxt.c:1077
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
#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
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
#define Anum_pg_authid_rolvaliduntil
Definition: pg_authid.h:89
#define DatumGetTimestampTz(X)
Definition: timestamp.h:28
#define STATUS_OK
Definition: c.h:975
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, char **logdetail)
Definition: crypt.c:270
#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
char * encrypt_password(PasswordType target_type, const char *role, const char *password)
Definition: crypt.c:117
PasswordType
Definition: crypt.h:24
#define MD5_PASSWD_LEN
Definition: md5.h:19
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:229
#define Assert(condition)
Definition: c.h:675
char * get_role_password(const char *role, char **logdetail)
Definition: crypt.c:39
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: crypt.c:193
void * palloc(Size size)
Definition: mcxt.c:849
#define elog
Definition: elog.h:219
#define _(x)
Definition: elog.c:84