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  /*
75  * Password OK, but check to be sure we are not past rolvaliduntil
76  */
77  if (!isnull && vuntil < GetCurrentTimestamp())
78  {
79  *logdetail = psprintf(_("User \"%s\" has an expired password."),
80  role);
81  return NULL;
82  }
83 
84  return shadow_pass;
85 }
86 
87 /*
88  * What kind of a password verifier is 'shadow_pass'?
89  */
91 get_password_type(const char *shadow_pass)
92 {
93  if (strncmp(shadow_pass, "md5", 3) == 0 && strlen(shadow_pass) == MD5_PASSWD_LEN)
94  return PASSWORD_TYPE_MD5;
95  if (strncmp(shadow_pass, "SCRAM-SHA-256$", strlen("SCRAM-SHA-256$")) == 0)
98 }
99 
100 /*
101  * Given a user-supplied password, convert it into a verifier of
102  * 'target_type' kind.
103  *
104  * If the password is already in encrypted form, we cannot reverse the
105  * hash, so it is stored as it is regardless of the requested type.
106  */
107 char *
108 encrypt_password(PasswordType target_type, const char *role,
109  const char *password)
110 {
111  PasswordType guessed_type = get_password_type(password);
112  char *encrypted_password;
113 
114  if (guessed_type != PASSWORD_TYPE_PLAINTEXT)
115  {
116  /*
117  * Cannot convert an already-encrypted password from one format to
118  * another, so return it as it is.
119  */
120  return pstrdup(password);
121  }
122 
123  switch (target_type)
124  {
125  case PASSWORD_TYPE_MD5:
126  encrypted_password = palloc(MD5_PASSWD_LEN + 1);
127 
128  if (!pg_md5_encrypt(password, role, strlen(role),
129  encrypted_password))
130  elog(ERROR, "password encryption failed");
131  return encrypted_password;
132 
134  return pg_be_scram_build_verifier(password);
135 
137  elog(ERROR, "cannot encrypt password with 'plaintext'");
138  }
139 
140  /*
141  * This shouldn't happen, because the above switch statements should
142  * handle every combination of source and target password types.
143  */
144  elog(ERROR, "cannot encrypt password to requested type");
145  return NULL; /* keep compiler quiet */
146 }
147 
148 /*
149  * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
150  *
151  * 'shadow_pass' is the user's correct password or password hash, as stored
152  * in pg_authid.rolpassword.
153  * 'client_pass' is the response given by the remote user to the MD5 challenge.
154  * 'md5_salt' is the salt used in the MD5 authentication challenge.
155  *
156  * In the error case, optionally store a palloc'd string at *logdetail
157  * that will be sent to the postmaster log (but not the client).
158  */
159 int
160 md5_crypt_verify(const char *role, const char *shadow_pass,
161  const char *client_pass,
162  const char *md5_salt, int md5_salt_len,
163  char **logdetail)
164 {
165  int retval;
166  char crypt_pwd[MD5_PASSWD_LEN + 1];
167 
168  Assert(md5_salt_len > 0);
169 
170  if (get_password_type(shadow_pass) != PASSWORD_TYPE_MD5)
171  {
172  /* incompatible password hash format. */
173  *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
174  role);
175  return STATUS_ERROR;
176  }
177 
178  /*
179  * Compute the correct answer for the MD5 challenge.
180  *
181  * We do not bother setting logdetail for any pg_md5_encrypt failure
182  * below: the only possible error is out-of-memory, which is unlikely, and
183  * if it did happen adding a psprintf call would only make things worse.
184  */
185  /* stored password already encrypted, only do salt */
186  if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
187  md5_salt, md5_salt_len,
188  crypt_pwd))
189  {
190  return STATUS_ERROR;
191  }
192 
193  if (strcmp(client_pass, crypt_pwd) == 0)
194  retval = STATUS_OK;
195  else
196  {
197  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
198  role);
199  retval = STATUS_ERROR;
200  }
201 
202  return retval;
203 }
204 
205 /*
206  * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
207  *
208  * 'shadow_pass' is the user's correct password hash, as stored in
209  * pg_authid.rolpassword.
210  * 'client_pass' is the password given by the remote user.
211  *
212  * In the error case, optionally store a palloc'd string at *logdetail
213  * that will be sent to the postmaster log (but not the client).
214  */
215 int
216 plain_crypt_verify(const char *role, const char *shadow_pass,
217  const char *client_pass,
218  char **logdetail)
219 {
220  char crypt_client_pass[MD5_PASSWD_LEN + 1];
221 
222  /*
223  * Client sent password in plaintext. If we have an MD5 hash stored, hash
224  * the password the client sent, and compare the hashes. Otherwise
225  * compare the plaintext passwords directly.
226  */
227  switch (get_password_type(shadow_pass))
228  {
231  client_pass,
232  shadow_pass))
233  {
234  return STATUS_OK;
235  }
236  else
237  {
238  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
239  role);
240  return STATUS_ERROR;
241  }
242  break;
243 
244  case PASSWORD_TYPE_MD5:
245  if (!pg_md5_encrypt(client_pass,
246  role,
247  strlen(role),
248  crypt_client_pass))
249  {
250  /*
251  * We do not bother setting logdetail for pg_md5_encrypt
252  * failure: the only possible error is out-of-memory, which is
253  * unlikely, and if it did happen adding a psprintf call would
254  * only make things worse.
255  */
256  return STATUS_ERROR;
257  }
258  if (strcmp(crypt_client_pass, shadow_pass) == 0)
259  return STATUS_OK;
260  else
261  {
262  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
263  role);
264  return STATUS_ERROR;
265  }
266  break;
267 
269 
270  /*
271  * We never store passwords in plaintext, so this shouldn't
272  * happen.
273  */
274  break;
275  }
276 
277  /*
278  * This shouldn't happen. Plain "password" authentication is possible
279  * with any kind of stored password hash.
280  */
281  *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
282  role);
283  return STATUS_ERROR;
284 }
static char password[100]
Definition: streamutil.c:42
bool scram_verify_plain_password(const char *username, const char *password, const char *verifier)
Definition: auth-scram.c:418
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1570
int64 TimestampTz
Definition: timestamp.h:39
#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:91
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define STATUS_ERROR
Definition: c.h:977
#define Anum_pg_authid_rolpassword
Definition: pg_authid.h:88
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:156
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:976
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, char **logdetail)
Definition: crypt.c:216
#define TextDatumGetCString(d)
Definition: builtins.h:92
uintptr_t Datum
Definition: postgres.h:372
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1117
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1279
char * encrypt_password(PasswordType target_type, const char *role, const char *password)
Definition: crypt.c:108
PasswordType
Definition: crypt.h:27
#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:676
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:160
void * palloc(Size size)
Definition: mcxt.c:849
#define elog
Definition: elog.h:219
char * pg_be_scram_build_verifier(const char *password)
Definition: auth-scram.c:381
#define _(x)
Definition: elog.c:84