@ -30,28 +30,28 @@
/*
/*
* Check given password for given user , and return STATUS_OK or STATUS_ERROR .
* Fetch stored password for a user , for authentication .
*
*
* ' client_pass ' is the password response given by the remote user . If
* Returns STATUS_OK on success . On error , returns STATUS_ERROR , and stores
* ' md5_salt ' is not NULL , it is a response to an MD5 authentication
* a palloc ' d string describing the reason , for the postmaster log , in
* challenge , with the given salt . Otherwise , it is a plaintext password .
* * logdetail . The error reason should * not * be sent to the client , to avoid
* giving away user information !
*
*
* In the error case , optionally store a palloc ' d string at * logdetail
* If the password is expired , it is still returned in * shadow_pass , but the
* that will be sent to the postmaster log ( but not the client ) .
* return code is STATUS_ERROR . On other errors , * shadow_pass is set to
* NULL .
*/
*/
int
int
md5_crypt_verify ( const char * role , char * client_pass ,
get_role_password ( const char * role , char * * shadow_pass , char * * logdetail )
char * md5_salt , int md5_salt_len , char * * logdetail )
{
{
int retval = STATUS_ERROR ;
int retval = STATUS_ERROR ;
char * shadow_pass ,
* crypt_pwd ;
TimestampTz vuntil = 0 ;
TimestampTz vuntil = 0 ;
char * crypt_client_pass = client_pass ;
HeapTuple roleTup ;
HeapTuple roleTup ;
Datum datum ;
Datum datum ;
bool isnull ;
bool isnull ;
* shadow_pass = NULL ;
/* Get role info from pg_authid */
/* Get role info from pg_authid */
roleTup = SearchSysCache1 ( AUTHNAME , PointerGetDatum ( role ) ) ;
roleTup = SearchSysCache1 ( AUTHNAME , PointerGetDatum ( role ) ) ;
if ( ! HeapTupleIsValid ( roleTup ) )
if ( ! HeapTupleIsValid ( roleTup ) )
@ -70,7 +70,7 @@ md5_crypt_verify(const char *role, char *client_pass,
role ) ;
role ) ;
return STATUS_ERROR ; /* user has no password */
return STATUS_ERROR ; /* user has no password */
}
}
shadow_pass = TextDatumGetCString ( datum ) ;
* shadow_pass = TextDatumGetCString ( datum ) ;
datum = SysCacheGetAttr ( AUTHNAME , roleTup ,
datum = SysCacheGetAttr ( AUTHNAME , roleTup ,
Anum_pg_authid_rolvaliduntil , & isnull ) ;
Anum_pg_authid_rolvaliduntil , & isnull ) ;
@ -79,104 +79,151 @@ md5_crypt_verify(const char *role, char *client_pass,
ReleaseSysCache ( roleTup ) ;
ReleaseSysCache ( roleTup ) ;
if ( * shadow_pass = = ' \0 ' )
if ( * * shadow_pass = = ' \0 ' )
{
{
* logdetail = psprintf ( _ ( " User \" %s \" has an empty password. " ) ,
* logdetail = psprintf ( _ ( " User \" %s \" has an empty password. " ) ,
role ) ;
role ) ;
pfree ( * shadow_pass ) ;
* shadow_pass = NULL ;
return STATUS_ERROR ; /* empty password */
return STATUS_ERROR ; /* empty password */
}
}
/*
/*
* Compare with the encrypted or plain password depending on the
* Password OK , now check to be sure we are not past rolvaliduntil
* authentication method being used for this connection . ( 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 . )
*/
*/
if ( md5_salt )
if ( isnull )
retval = STATUS_OK ;
else if ( vuntil < GetCurrentTimestamp ( ) )
{
{
/* MD5 authentication */
* logdetail = psprintf ( _ ( " User \" %s \" has an expired password. " ) ,
Assert ( md5_salt_len > 0 ) ;
role ) ;
crypt_pwd = palloc ( MD5_PASSWD_LEN + 1 ) ;
retval = STATUS_ERROR ;
if ( isMD5 ( shadow_pass ) )
}
{
else
/* stored password already encrypted, only do salt */
retval = STATUS_OK ;
if ( ! pg_md5_encrypt ( shadow_pass + strlen ( " md5 " ) ,
md5_salt , md5_salt_len ,
return retval ;
crypt_pwd ) )
}
{
pfree ( crypt_pwd ) ;
/*
return STATUS_ERROR ;
* Check MD5 authentication response , and return STATUS_OK or STATUS_ERROR .
}
*
}
* ' shadow_pass ' is the user ' s correct password or password hash , as stored
else
* 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 ) )
{
{
/* stored password is plain, double-encrypt */
return STATUS_ERROR ;
char * crypt_pwd2 = palloc ( MD5_PASSWD_LEN + 1 ) ;
if ( ! pg_md5_encrypt ( shadow_pass ,
role ,
strlen ( role ) ,
crypt_pwd2 ) )
{
pfree ( crypt_pwd ) ;
pfree ( crypt_pwd2 ) ;
return STATUS_ERROR ;
}
if ( ! pg_md5_encrypt ( crypt_pwd2 + strlen ( " md5 " ) ,
md5_salt , md5_salt_len ,
crypt_pwd ) )
{
pfree ( crypt_pwd ) ;
pfree ( crypt_pwd2 ) ;
return STATUS_ERROR ;
}
pfree ( crypt_pwd2 ) ;
}
}
}
}
else
else
{
{
/* Client sent password in plaintext */
/* stored password is plain, double-encrypt */
if ( isMD5 ( shadow_pass ) )
if ( ! pg_md5_encrypt ( shadow_pass ,
role ,
strlen ( role ) ,
crypt_pwd2 ) )
{
{
/* Encrypt user-supplied password to match stored MD5 */
return STATUS_ERROR ;
crypt_client_pass = palloc ( MD5_PASSWD_LEN + 1 ) ;
}
if ( ! pg_md5_encrypt ( client_pass ,
if ( ! pg_md5_encrypt ( crypt_pwd2 + strlen ( " md5 " ) ,
role ,
md5_salt , md5_salt_len ,
strlen ( role ) ,
crypt_pwd ) )
crypt_client_pass ) )
{
{
return STATUS_ERROR ;
pfree ( crypt_client_pass ) ;
return STATUS_ERROR ;
}
}
}
crypt_pwd = shadow_pass ;
}
}
if ( strcmp ( crypt_client_pass , crypt_pwd ) = = 0 )
if ( strcmp ( client_pass , crypt_pwd ) = = 0 )
retval = STATUS_OK ;
else
{
{
/*
* logdetail = psprintf ( _ ( " Password does not match for user \" %s \" . " ) ,
* Password OK , now check to be sure we are not past rolvaliduntil
role ) ;
*/
retval = STATUS_ERROR ;
if ( isnull )
}
retval = STATUS_OK ;
else if ( vuntil < GetCurrentTimestamp ( ) )
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 ) )
{
{
* logdetail = psprintf ( _ ( " User \" %s \" has an expired password. " ) ,
/*
role ) ;
* We do not bother setting logdetail for pg_md5_encrypt failure :
retval = STATUS_ERROR ;
* 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 ;
}
}
else
client_pass = crypt_client_pass ;
retval = STATUS_OK ;
}
}
if ( strcmp ( client_pass , shadow_pass ) = = 0 )
retval = STATUS_OK ;
else
else
{
* logdetail = psprintf ( _ ( " Password does not match for user \" %s \" . " ) ,
* logdetail = psprintf ( _ ( " Password does not match for user \" %s \" . " ) ,
role ) ;
role ) ;
retval = STATUS_ERROR ;
if ( crypt_pwd ! = shadow_pass )
}
pfree ( crypt_pwd ) ;
if ( crypt_client_pass ! = client_pass )
pfree ( crypt_client_pass ) ;
return retval ;
return retval ;
}
}