1997-12-04 01:34:01 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
1999-02-14 00:22:53 +01:00
|
|
|
* crypt.c
|
2001-11-01 19:10:48 +01:00
|
|
|
* Look into the password file and check the encrypted password with
|
|
|
|
* the one passed in from the frontend.
|
1997-12-04 01:34:01 +01:00
|
|
|
*
|
2001-11-01 19:10:48 +01:00
|
|
|
* Original coding by Todd A. Brandys
|
1997-12-30 03:26:56 +01:00
|
|
|
*
|
2016-01-02 19:33:40 +01:00
|
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
2001-11-01 19:10:48 +01:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
1997-12-30 03:26:56 +01:00
|
|
|
*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/libpq/crypt.c
|
1997-12-04 01:34:01 +01:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
2001-11-01 19:10:48 +01:00
|
|
|
#include "postgres.h"
|
1997-12-04 01:34:01 +01:00
|
|
|
|
|
|
|
#include <unistd.h>
|
2001-11-02 19:39:57 +01:00
|
|
|
#ifdef HAVE_CRYPT_H
|
|
|
|
#include <crypt.h>
|
|
|
|
#endif
|
1997-12-04 01:34:01 +01:00
|
|
|
|
2009-08-29 21:26:52 +02:00
|
|
|
#include "catalog/pg_authid.h"
|
2016-09-02 12:49:59 +02:00
|
|
|
#include "common/md5.h"
|
1999-07-16 07:00:38 +02:00
|
|
|
#include "libpq/crypt.h"
|
2009-08-29 21:26:52 +02:00
|
|
|
#include "miscadmin.h"
|
|
|
|
#include "utils/builtins.h"
|
|
|
|
#include "utils/syscache.h"
|
2011-09-09 19:23:41 +02:00
|
|
|
#include "utils/timestamp.h"
|
1997-12-30 03:26:56 +01:00
|
|
|
|
1998-01-26 02:42:53 +01:00
|
|
|
|
2014-01-28 03:04:09 +01:00
|
|
|
/*
|
2016-12-12 11:48:13 +01:00
|
|
|
* Fetch stored password for a user, for authentication.
|
2016-12-08 12:44:47 +01:00
|
|
|
*
|
2016-12-12 11:48:13 +01:00
|
|
|
* Returns STATUS_OK on success. On error, returns STATUS_ERROR, and stores
|
|
|
|
* a palloc'd string describing the reason, for the postmaster log, in
|
|
|
|
* *logdetail. The error reason should *not* be sent to the client, to avoid
|
|
|
|
* giving away user information!
|
2016-12-08 12:44:47 +01:00
|
|
|
*
|
2016-12-12 11:48:13 +01:00
|
|
|
* If the password is expired, it is still returned in *shadow_pass, but the
|
|
|
|
* return code is STATUS_ERROR. On other errors, *shadow_pass is set to
|
|
|
|
* NULL.
|
2014-01-28 03:04:09 +01:00
|
|
|
*/
|
1998-02-26 05:46:47 +01:00
|
|
|
int
|
2016-12-12 11:48:13 +01:00
|
|
|
get_role_password(const char *role, char **shadow_pass, char **logdetail)
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
1999-05-25 18:15:34 +02:00
|
|
|
int retval = STATUS_ERROR;
|
2009-08-29 21:26:52 +02:00
|
|
|
TimestampTz vuntil = 0;
|
|
|
|
HeapTuple roleTup;
|
|
|
|
Datum datum;
|
|
|
|
bool isnull;
|
|
|
|
|
2016-12-12 11:48:13 +01:00
|
|
|
*shadow_pass = NULL;
|
|
|
|
|
2009-08-29 21:26:52 +02:00
|
|
|
/* Get role info from pg_authid */
|
2010-02-14 19:42:19 +01:00
|
|
|
roleTup = SearchSysCache1(AUTHNAME, PointerGetDatum(role));
|
2009-08-29 21:26:52 +02:00
|
|
|
if (!HeapTupleIsValid(roleTup))
|
2016-01-07 17:19:33 +01:00
|
|
|
{
|
|
|
|
*logdetail = psprintf(_("Role \"%s\" does not exist."),
|
|
|
|
role);
|
2010-02-26 03:01:40 +01:00
|
|
|
return STATUS_ERROR; /* no such user */
|
2016-01-07 17:19:33 +01:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2009-08-29 21:26:52 +02:00
|
|
|
datum = SysCacheGetAttr(AUTHNAME, roleTup,
|
|
|
|
Anum_pg_authid_rolpassword, &isnull);
|
|
|
|
if (isnull)
|
2002-04-25 02:56:36 +02:00
|
|
|
{
|
2009-08-29 21:26:52 +02:00
|
|
|
ReleaseSysCache(roleTup);
|
2014-01-28 03:04:09 +01:00
|
|
|
*logdetail = psprintf(_("User \"%s\" has no password assigned."),
|
|
|
|
role);
|
2010-02-26 03:01:40 +01:00
|
|
|
return STATUS_ERROR; /* user has no password */
|
2002-04-25 02:56:36 +02:00
|
|
|
}
|
2016-12-12 11:48:13 +01:00
|
|
|
*shadow_pass = TextDatumGetCString(datum);
|
2009-08-29 21:26:52 +02:00
|
|
|
|
|
|
|
datum = SysCacheGetAttr(AUTHNAME, roleTup,
|
|
|
|
Anum_pg_authid_rolvaliduntil, &isnull);
|
|
|
|
if (!isnull)
|
|
|
|
vuntil = DatumGetTimestampTz(datum);
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2009-08-29 21:26:52 +02:00
|
|
|
ReleaseSysCache(roleTup);
|
|
|
|
|
2016-12-12 11:48:13 +01:00
|
|
|
if (**shadow_pass == '\0')
|
2016-01-07 17:19:33 +01:00
|
|
|
{
|
|
|
|
*logdetail = psprintf(_("User \"%s\" has an empty password."),
|
|
|
|
role);
|
2016-12-12 11:48:13 +01:00
|
|
|
pfree(*shadow_pass);
|
|
|
|
*shadow_pass = NULL;
|
2010-02-26 03:01:40 +01:00
|
|
|
return STATUS_ERROR; /* empty password */
|
2016-01-07 17:19:33 +01:00
|
|
|
}
|
2009-08-29 21:26:52 +02:00
|
|
|
|
1998-02-26 05:46:47 +01:00
|
|
|
/*
|
2016-12-12 11:48:13 +01:00
|
|
|
* Password OK, now check to be sure we are not past rolvaliduntil
|
1998-02-26 05:46:47 +01:00
|
|
|
*/
|
2016-12-12 11:48:13 +01:00
|
|
|
if (isnull)
|
|
|
|
retval = STATUS_OK;
|
|
|
|
else if (vuntil < GetCurrentTimestamp())
|
2001-08-17 04:59:20 +02:00
|
|
|
{
|
2016-12-12 11:48:13 +01:00
|
|
|
*logdetail = psprintf(_("User \"%s\" has an expired password."),
|
|
|
|
role);
|
|
|
|
retval = STATUS_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
retval = STATUS_OK;
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check MD5 authentication response, and return STATUS_OK or STATUS_ERROR.
|
|
|
|
*
|
|
|
|
* 'shadow_pass' is the user's correct password or password hash, as stored
|
|
|
|
* in pg_authid.rolpassword.
|
|
|
|
* 'client_pass' is the response given by the remote user to the MD5 challenge.
|
|
|
|
* 'md5_salt' is the salt used in the MD5 authentication challenge.
|
|
|
|
*
|
|
|
|
* In the error case, optionally store a palloc'd string at *logdetail
|
|
|
|
* that will be sent to the postmaster log (but not the client).
|
|
|
|
*/
|
|
|
|
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 retval;
|
|
|
|
char crypt_pwd[MD5_PASSWD_LEN + 1];
|
|
|
|
char crypt_pwd2[MD5_PASSWD_LEN + 1];
|
|
|
|
|
|
|
|
Assert(md5_salt_len > 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute the correct answer for the MD5 challenge.
|
|
|
|
*
|
|
|
|
* We do not bother setting logdetail for any pg_md5_encrypt failure
|
|
|
|
* below: the only possible error is out-of-memory, which is unlikely, and
|
|
|
|
* if it did happen adding a psprintf call would only make things worse.
|
|
|
|
*/
|
|
|
|
if (isMD5(shadow_pass))
|
|
|
|
{
|
|
|
|
/* stored password already encrypted, only do salt */
|
|
|
|
if (!pg_md5_encrypt(shadow_pass + strlen("md5"),
|
|
|
|
md5_salt, md5_salt_len,
|
|
|
|
crypt_pwd))
|
2016-12-08 12:44:47 +01:00
|
|
|
{
|
2016-12-12 11:48:13 +01:00
|
|
|
return STATUS_ERROR;
|
2016-12-08 12:44:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-12-12 11:48:13 +01:00
|
|
|
/* stored password is plain, double-encrypt */
|
|
|
|
if (!pg_md5_encrypt(shadow_pass,
|
|
|
|
role,
|
|
|
|
strlen(role),
|
|
|
|
crypt_pwd2))
|
2016-12-08 12:44:47 +01:00
|
|
|
{
|
2016-12-12 11:48:13 +01:00
|
|
|
return STATUS_ERROR;
|
|
|
|
}
|
|
|
|
if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"),
|
|
|
|
md5_salt, md5_salt_len,
|
|
|
|
crypt_pwd))
|
|
|
|
{
|
|
|
|
return STATUS_ERROR;
|
2016-12-08 12:44:47 +01:00
|
|
|
}
|
2001-08-15 20:42:16 +02:00
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
2016-12-12 11:48:13 +01:00
|
|
|
if (strcmp(client_pass, crypt_pwd) == 0)
|
|
|
|
retval = STATUS_OK;
|
|
|
|
else
|
1998-02-26 05:46:47 +01:00
|
|
|
{
|
2016-12-12 11:48:13 +01:00
|
|
|
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
|
|
|
|
role);
|
|
|
|
retval = STATUS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check given password for given user, and return STATUS_OK or STATUS_ERROR.
|
|
|
|
*
|
|
|
|
* 'shadow_pass' is the user's correct password or password hash, as stored
|
|
|
|
* in pg_authid.rolpassword.
|
|
|
|
* 'client_pass' is the password given by the remote user.
|
|
|
|
*
|
|
|
|
* In the error case, optionally store a palloc'd string at *logdetail
|
|
|
|
* that will be sent to the postmaster log (but not the client).
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
plain_crypt_verify(const char *role, const char *shadow_pass,
|
|
|
|
const char *client_pass,
|
|
|
|
char **logdetail)
|
|
|
|
{
|
|
|
|
int retval;
|
|
|
|
char crypt_client_pass[MD5_PASSWD_LEN + 1];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Client sent password in plaintext. If we have an MD5 hash stored, hash
|
|
|
|
* the password the client sent, and compare the hashes. Otherwise
|
|
|
|
* compare the plaintext passwords directly.
|
|
|
|
*/
|
|
|
|
if (isMD5(shadow_pass))
|
|
|
|
{
|
|
|
|
if (!pg_md5_encrypt(client_pass,
|
|
|
|
role,
|
|
|
|
strlen(role),
|
|
|
|
crypt_client_pass))
|
2014-01-28 03:04:09 +01:00
|
|
|
{
|
2016-12-12 11:48:13 +01:00
|
|
|
/*
|
|
|
|
* We do not bother setting logdetail for pg_md5_encrypt failure:
|
|
|
|
* the only possible error is out-of-memory, which is unlikely,
|
|
|
|
* and if it did happen adding a psprintf call would only make
|
|
|
|
* things worse.
|
|
|
|
*/
|
|
|
|
return STATUS_ERROR;
|
2014-01-28 03:04:09 +01:00
|
|
|
}
|
2016-12-12 11:48:13 +01:00
|
|
|
client_pass = crypt_client_pass;
|
1998-02-26 05:46:47 +01:00
|
|
|
}
|
2016-12-12 11:48:13 +01:00
|
|
|
|
|
|
|
if (strcmp(client_pass, shadow_pass) == 0)
|
|
|
|
retval = STATUS_OK;
|
2016-01-07 17:19:33 +01:00
|
|
|
else
|
2016-12-12 11:48:13 +01:00
|
|
|
{
|
2016-01-07 17:19:33 +01:00
|
|
|
*logdetail = psprintf(_("Password does not match for user \"%s\"."),
|
|
|
|
role);
|
2016-12-12 11:48:13 +01:00
|
|
|
retval = STATUS_ERROR;
|
|
|
|
}
|
1998-02-26 05:46:47 +01:00
|
|
|
|
|
|
|
return retval;
|
1997-12-04 01:34:01 +01:00
|
|
|
}
|