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 "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

◆ encrypt_password()

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, palloc(), PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, PASSWORD_TYPE_SCRAM_SHA_256, pg_be_scram_build_verifier(), pg_md5_encrypt(), and pstrdup().

Referenced by AlterRole(), and CreateRole().

119 {
120  PasswordType guessed_type = get_password_type(password);
121  char *encrypted_password;
122 
123  if (guessed_type != PASSWORD_TYPE_PLAINTEXT)
124  {
125  /*
126  * Cannot convert an already-encrypted password from one format to
127  * another, so return it as it is.
128  */
129  return pstrdup(password);
130  }
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))
139  elog(ERROR, "password encryption failed");
140  return encrypted_password;
141 
144 
146  elog(ERROR, "cannot encrypt password with 'plaintext'");
147  }
148 
149  /*
150  * This shouldn't happen, because the above switch statements should
151  * handle every combination of source and target password types.
152  */
153  elog(ERROR, "cannot encrypt password to requested type");
154  return NULL; /* keep compiler quiet */
155 }
static char password[100]
Definition: streamutil.c:55
char * pstrdup(const char *in)
Definition: mcxt.c:1161
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:92
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:27
#define MD5_PASSWD_LEN
Definition: md5.h:20
void * palloc(Size size)
Definition: mcxt.c:924
#define elog(elevel,...)
Definition: elog.h:226
char * pg_be_scram_build_verifier(const char *password)
Definition: auth-scram.c:451

◆ get_password_type()

PasswordType get_password_type ( const char *  shadow_pass)

Definition at line 92 of file crypt.c.

References MD5_PASSWD_CHARSET, MD5_PASSWD_LEN, parse_scram_verifier(), PASSWORD_TYPE_MD5, PASSWORD_TYPE_PLAINTEXT, PASSWORD_TYPE_SCRAM_SHA_256, and SCRAM_KEY_LEN.

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

93 {
94  char *encoded_salt;
95  int iterations;
96  uint8 stored_key[SCRAM_KEY_LEN];
97  uint8 server_key[SCRAM_KEY_LEN];
98 
99  if (strncmp(shadow_pass, "md5", 3) == 0 &&
100  strlen(shadow_pass) == MD5_PASSWD_LEN &&
101  strspn(shadow_pass + 3, MD5_PASSWD_CHARSET) == MD5_PASSWD_LEN - 3)
102  return PASSWORD_TYPE_MD5;
103  if (parse_scram_verifier(shadow_pass, &iterations, &encoded_salt,
104  stored_key, server_key))
107 }
unsigned char uint8
Definition: c.h:356
#define MD5_PASSWD_CHARSET
Definition: md5.h:19
#define MD5_PASSWD_LEN
Definition: md5.h:20
bool parse_scram_verifier(const char *verifier, int *iterations, char **salt, uint8 *stored_key, uint8 *server_key)
Definition: auth-scram.c:556
#define SCRAM_KEY_LEN
Definition: scram-common.h:23

◆ get_role_password()

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

Definition at line 40 of file crypt.c.

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

Referenced by CheckPasswordAuth(), and CheckPWChallengeAuth().

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

◆ 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,
char **  logdetail 
)

Definition at line 169 of file crypt.c.

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

Referenced by CheckMD5Auth().

173 {
174  int retval;
175  char crypt_pwd[MD5_PASSWD_LEN + 1];
176 
177  Assert(md5_salt_len > 0);
178 
179  if (get_password_type(shadow_pass) != PASSWORD_TYPE_MD5)
180  {
181  /* incompatible password hash format. */
182  *logdetail = psprintf(_("User \"%s\" has a password that cannot be used with MD5 authentication."),
183  role);
184  return STATUS_ERROR;
185  }
186 
187  /*
188  * Compute the correct answer for the MD5 challenge.
189  *
190  * We do not bother setting logdetail for any pg_md5_encrypt failure
191  * below: the only possible error is out-of-memory, which is unlikely, and
192  * if it did happen adding a psprintf call would only make things worse.
193  */
194  /* stored password already encrypted, only do salt */
195  if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
196  md5_salt, md5_salt_len,
197  crypt_pwd))
198  {
199  return STATUS_ERROR;
200  }
201 
202  if (strcmp(client_pass, crypt_pwd) == 0)
203  retval = STATUS_OK;
204  else
205  {
206  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
207  role);
208  retval = STATUS_ERROR;
209  }
210 
211  return retval;
212 }
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:92
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define STATUS_ERROR
Definition: c.h:1090
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:1089
#define MD5_PASSWD_LEN
Definition: md5.h:20
#define Assert(condition)
Definition: c.h:732
#define _(x)
Definition: elog.c:84

◆ plain_crypt_verify()

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

Definition at line 225 of file crypt.c.

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().

228 {
229  char crypt_client_pass[MD5_PASSWD_LEN + 1];
230 
231  /*
232  * Client sent password in plaintext. If we have an MD5 hash stored, hash
233  * the password the client sent, and compare the hashes. Otherwise
234  * compare the plaintext passwords directly.
235  */
236  switch (get_password_type(shadow_pass))
237  {
240  client_pass,
241  shadow_pass))
242  {
243  return STATUS_OK;
244  }
245  else
246  {
247  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
248  role);
249  return STATUS_ERROR;
250  }
251  break;
252 
253  case PASSWORD_TYPE_MD5:
254  if (!pg_md5_encrypt(client_pass,
255  role,
256  strlen(role),
257  crypt_client_pass))
258  {
259  /*
260  * We do not bother setting logdetail for pg_md5_encrypt
261  * failure: the only possible error is out-of-memory, which is
262  * unlikely, and if it did happen adding a psprintf call would
263  * only make things worse.
264  */
265  return STATUS_ERROR;
266  }
267  if (strcmp(crypt_client_pass, shadow_pass) == 0)
268  return STATUS_OK;
269  else
270  {
271  *logdetail = psprintf(_("Password does not match for user \"%s\"."),
272  role);
273  return STATUS_ERROR;
274  }
275  break;
276 
278 
279  /*
280  * We never store passwords in plaintext, so this shouldn't
281  * happen.
282  */
283  break;
284  }
285 
286  /*
287  * This shouldn't happen. Plain "password" authentication is possible
288  * with any kind of stored password hash.
289  */
290  *logdetail = psprintf(_("Password of user \"%s\" is in unrecognized format."),
291  role);
292  return STATUS_ERROR;
293 }
bool scram_verify_plain_password(const char *username, const char *password, const char *verifier)
Definition: auth-scram.c:488
PasswordType get_password_type(const char *shadow_pass)
Definition: crypt.c:92
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define STATUS_ERROR
Definition: c.h:1090
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:1089
#define MD5_PASSWD_LEN
Definition: md5.h:20
#define _(x)
Definition: elog.c:84