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 "miscadmin.h"
25 #include "utils/builtins.h"
26 #include "utils/syscache.h"
27 #include "utils/timestamp.h"
28 
29 
30 /*
31  * Fetch stored password for a user, for authentication.
32  *
33  * Returns STATUS_OK on success. On error, returns STATUS_ERROR, and stores
34  * a palloc'd string describing the reason, for the postmaster log, in
35  * *logdetail. The error reason should *not* be sent to the client, to avoid
36  * giving away user information!
37  *
38  * If the password is expired, it is still returned in *shadow_pass, but the
39  * return code is STATUS_ERROR. On other errors, *shadow_pass is set to
40  * NULL.
41  */
42 int
43 get_role_password(const char *role, char **shadow_pass, char **logdetail)
44 {
45  int retval = STATUS_ERROR;
46  TimestampTz vuntil = 0;
47  HeapTuple roleTup;
48  Datum datum;
49  bool isnull;
50 
51  *shadow_pass = NULL;
52 
53  /* Get role info from pg_authid */
54  roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
55  if (!HeapTupleIsValid(roleTup))
56  {
57  *logdetail = psprintf(_("Role \"%s\" does not exist."),
58  role);
59  return STATUS_ERROR; /* no such user */
60  }
61 
62  datum = SysCacheGetAttr(AUTHNAME, roleTup,
64  if (isnull)
65  {
66  ReleaseSysCache(roleTup);
67  *logdetail = psprintf(_("User \"%s\" has no password assigned."),
68  role);
69  return STATUS_ERROR; /* user has no password */
70  }
71  *shadow_pass = TextDatumGetCString(datum);
72 
73  datum = SysCacheGetAttr(AUTHNAME, roleTup,
75  if (!isnull)
76  vuntil = DatumGetTimestampTz(datum);
77 
78  ReleaseSysCache(roleTup);
79 
80  if (**shadow_pass == '\0')
81  {
82  *logdetail = psprintf(_("User \"%s\" has an empty password."),
83  role);
84  pfree(*shadow_pass);
85  *shadow_pass = NULL;
86  return STATUS_ERROR; /* empty password */
87  }
88 
89  /*
90  * Password OK, now check to be sure we are not past rolvaliduntil
91  */
92  if (isnull)
93  retval = STATUS_OK;
94  else if (vuntil < GetCurrentTimestamp())
95  {
96  *logdetail = psprintf(_("User \"%s\" has an expired password."),
97  role);
98  retval = STATUS_ERROR;
99  }
100  else
101  retval = STATUS_OK;
102 
103  return retval;
104 }
105 
106 /*
107  * What kind of a password verifier is 'shadow_pass'?
108  */
110 get_password_type(const char *shadow_pass)
111 {
112  if (strncmp(shadow_pass, "md5", 3) == 0 && strlen(shadow_pass) == MD5_PASSWD_LEN)
113  return PASSWORD_TYPE_MD5;
115 }
116 
117 /*
118  * Given a user-supplied password, convert it into a verifier of
119  * 'target_type' kind.
120  *
121  * If the password looks like a valid MD5 hash, it is stored as it is.
122  * We cannot reverse the hash, so even if the caller requested a plaintext
123  * plaintext password, the MD5 hash is returned.
124  */
125 char *
126 encrypt_password(PasswordType target_type, const char *role,
127  const char *password)
128 {
129  PasswordType guessed_type = get_password_type(password);
130  char *encrypted_password;
131 
132  switch (target_type)
133  {
135 
136  /*
137  * We cannot convert a hashed password back to plaintext, so just
138  * store the password as it was, whether it was hashed or not.
139  */
140  return pstrdup(password);
141 
142  case PASSWORD_TYPE_MD5:
143  switch (guessed_type)
144  {
146  encrypted_password = palloc(MD5_PASSWD_LEN + 1);
147 
148  if (!pg_md5_encrypt(password, role, strlen(role),
149  encrypted_password))
150  elog(ERROR, "password encryption failed");
151  return encrypted_password;
152 
153  case PASSWORD_TYPE_MD5:
154  return pstrdup(password);
155  }
156  }
157 
158  /*
159  * This shouldn't happen, because the above switch statements should
160  * handle every combination of source and target password types.
161  */
162  elog(ERROR, "cannot encrypt password to requested type");
163  return NULL; /* keep compiler quiet */
164 }
165 
166 /*
167  * Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
168  *
169  * 'shadow_pass' is the user's correct password or password hash, as stored
170  * in pg_authid.rolpassword.
171  * 'client_pass' is the response given by the remote user to the MD5 challenge.
172  * 'md5_salt' is the salt used in the MD5 authentication challenge.
173  *
174  * In the error case, optionally store a palloc'd string at *logdetail
175  * that will be sent to the postmaster log (but not the client).
176  */
177 int
178 md5_crypt_verify(const char *role, const char *shadow_pass,
179  const char *client_pass,
180  const char *md5_salt, int md5_salt_len,
181  char **logdetail)
182 {
183  int retval;
184  char crypt_pwd[MD5_PASSWD_LEN + 1];
185  char crypt_pwd2[MD5_PASSWD_LEN + 1];
186 
187  Assert(md5_salt_len > 0);
188 
189  /*
190  * Compute the correct answer for the MD5 challenge.
191  *
192  * We do not bother setting logdetail for any pg_md5_encrypt failure
193  * below: the only possible error is out-of-memory, which is unlikely, and
194  * if it did happen adding a psprintf call would only make things worse.
195  */
196  switch (get_password_type(shadow_pass))
197  {
198  case PASSWORD_TYPE_MD5:
199  /* stored password already encrypted, only do salt */
200  if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
201  md5_salt, md5_salt_len,
202  crypt_pwd))
203  {
204  return STATUS_ERROR;
205  }
206  break;
207 
209  /* stored password is plain, double-encrypt */
210  if (!pg_md5_encrypt(shadow_pass,
211  role,
212  strlen(role),
213  crypt_pwd2))
214  {
215  return STATUS_ERROR;
216  }
217  if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
218  md5_salt, md5_salt_len,
219  crypt_pwd))
220  {
221  return STATUS_ERROR;
222  }
223  break;
224 
225  default:
226  /* unknown password hash format. */
227  *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
228  role);
229  return STATUS_ERROR;
230  }
231 
232  if (strcmp(client_pass, crypt_pwd) == 0)
233  retval = STATUS_OK;
234  else
235  {
236  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
237  role);
238  retval = STATUS_ERROR;
239  }
240 
241  return retval;
242 }
243 
244 /*
245  * Check given password for given user, and return STATUS_OK or STATUS_ERROR.
246  *
247  * 'shadow_pass' is the user's correct password or password hash, as stored
248  * in pg_authid.rolpassword.
249  * 'client_pass' is the password given by the remote user.
250  *
251  * In the error case, optionally store a palloc'd string at *logdetail
252  * that will be sent to the postmaster log (but not the client).
253  */
254 int
255 plain_crypt_verify(const char *role, const char *shadow_pass,
256  const char *client_pass,
257  char **logdetail)
258 {
259  int retval;
260  char crypt_client_pass[MD5_PASSWD_LEN + 1];
261 
262  /*
263  * Client sent password in plaintext. If we have an MD5 hash stored, hash
264  * the password the client sent, and compare the hashes. Otherwise
265  * compare the plaintext passwords directly.
266  */
267  switch (get_password_type(shadow_pass))
268  {
269  case PASSWORD_TYPE_MD5:
270  if (!pg_md5_encrypt(client_pass,
271  role,
272  strlen(role),
273  crypt_client_pass))
274  {
275  /*
276  * We do not bother setting logdetail for pg_md5_encrypt
277  * failure: the only possible error is out-of-memory, which is
278  * unlikely, and if it did happen adding a psprintf call would
279  * only make things worse.
280  */
281  return STATUS_ERROR;
282  }
283  client_pass = crypt_client_pass;
284  break;
286  break;
287 
288  default:
289 
290  /*
291  * This shouldn't happen. Plain "password" authentication should
292  * be possible with any kind of stored password hash.
293  */
294  *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
295  role);
296  return STATUS_ERROR;
297  }
298 
299  if (strcmp(client_pass, shadow_pass) == 0)
300  retval = STATUS_OK;
301  else
302  {
303  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
304  role);
305  retval = STATUS_ERROR;
306  }
307 
308  return retval;
309 }
static char password[100]
Definition: streamutil.c:41
TimestampTz GetCurrentTimestamp(void)
Definition: timestamp.c:1569
int64 TimestampTz
Definition: timestamp.h:39
#define PointerGetDatum(X)
Definition: postgres.h:564
char * pstrdup(const char *in)
Definition: mcxt.c:1165
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:110
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define STATUS_ERROR
Definition: c.h:972
#define Anum_pg_authid_rolpassword
Definition: pg_authid.h:88
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:149
void pfree(void *pointer)
Definition: mcxt.c:992
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:971
int plain_crypt_verify(const char *role, const char *shadow_pass, const char *client_pass, char **logdetail)
Definition: crypt.c:255
#define TextDatumGetCString(d)
Definition: builtins.h:91
uintptr_t Datum
Definition: postgres.h:374
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1083
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1245
char * encrypt_password(PasswordType target_type, const char *role, const char *password)
Definition: crypt.c:126
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:226
#define Assert(condition)
Definition: c.h:671
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:178
int get_role_password(const char *role, char **shadow_pass, char **logdetail)
Definition: crypt.c:43
void * palloc(Size size)
Definition: mcxt.c:891
#define elog
Definition: elog.h:219
#define _(x)
Definition: elog.c:84