@ -17,8 +17,6 @@
* by the SASLprep profile , we skip the SASLprep pre - processing and use
* the raw bytes in calculating the hash .
*
* - Channel binding is not supported yet .
*
*
* The password stored in pg_authid consists of the iteration count , salt ,
* StoredKey and ServerKey .
@ -112,6 +110,11 @@ typedef struct
const char * username ; /* username from startup packet */
bool ssl_in_use ;
const char * tls_finished_message ;
size_t tls_finished_len ;
char * channel_binding_type ;
int iterations ;
char * salt ; /* base64-encoded */
uint8 StoredKey [ SCRAM_KEY_LEN ] ;
@ -168,7 +171,11 @@ static char *scram_mock_salt(const char *username);
* it will fail , as if an incorrect password was given .
*/
void *
pg_be_scram_init ( const char * username , const char * shadow_pass )
pg_be_scram_init ( const char * username ,
const char * shadow_pass ,
bool ssl_in_use ,
const char * tls_finished_message ,
size_t tls_finished_len )
{
scram_state * state ;
bool got_verifier ;
@ -176,6 +183,10 @@ pg_be_scram_init(const char *username, const char *shadow_pass)
state = ( scram_state * ) palloc0 ( sizeof ( scram_state ) ) ;
state - > state = SCRAM_AUTH_INIT ;
state - > username = username ;
state - > ssl_in_use = ssl_in_use ;
state - > tls_finished_message = tls_finished_message ;
state - > tls_finished_len = tls_finished_len ;
state - > channel_binding_type = NULL ;
/*
* Parse the stored password verifier .
@ -773,31 +784,89 @@ read_client_first_message(scram_state *state, char *input)
* - - - - - -
*/
/* read gs2-cbind-flag */
/*
* Read gs2 - cbind - flag . ( For details see also RFC 5802 Section 6 " Channel
* Binding " .)
*/
switch ( * input )
{
case ' n ' :
/* Client does not support channel binding */
/*
* The client does not support channel binding or has simply
* decided to not use it . In that case just let it go .
*/
input + + ;
if ( * input ! = ' , ' )
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
errmsg ( " malformed SCRAM message " ) ,
errdetail ( " Comma expected, but found character \" %s \" . " ,
sanitize_char ( * input ) ) ) ) ;
input + + ;
break ;
case ' y ' :
/* Client supports channel binding, but we're not doing it today */
/*
* The client supports channel binding and thinks that the server
* does not . In this case , the server must fail authentication if
* it supports channel binding , which in this implementation is
* the case if a connection is using SSL .
*/
if ( state - > ssl_in_use )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION ) ,
errmsg ( " SCRAM channel binding negotiation error " ) ,
errdetail ( " The client supports SCRAM channel binding but thinks the server does not. "
" However, this server does support channel binding. " ) ) ) ;
input + + ;
if ( * input ! = ' , ' )
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
errmsg ( " malformed SCRAM message " ) ,
errdetail ( " Comma expected, but found character \" %s \" . " ,
sanitize_char ( * input ) ) ) ) ;
input + + ;
break ;
case ' p ' :
/*
* Client requires channel binding . We don ' t support it .
*
* RFC 5802 specifies a particular error code ,
* e = server - does - support - channel - binding , for this . But it can
* only be sent in the server - final message , and we don ' t want to
* go through the motions of the authentication , knowing it will
* fail , just to send that error message .
* The client requires channel binding . Channel binding type
* follows , e . g . , " p=tls-unique " .
*/
ereport ( ERROR ,
( errcode ( ERRCODE_FEATURE_NOT_SUPPORTED ) ,
errmsg ( " client requires SCRAM channel binding, but it is not supported " ) ) ) ;
{
char * channel_binding_type ;
if ( ! state - > ssl_in_use )
{
/*
* Without SSL , we don ' t support channel binding .
*
* RFC 5802 specifies a particular error code ,
* e = server - does - support - channel - binding , for this . But
* it can only be sent in the server - final message , and we
* don ' t want to go through the motions of the
* authentication , knowing it will fail , just to send that
* error message .
*/
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
errmsg ( " client requires SCRAM channel binding, but it is not supported " ) ) ) ;
}
/*
* Read value provided by client ; only tls - unique is supported
* for now . ( It is not safe to print the name of an
* unsupported binding type in the error message . Pranksters
* could print arbitrary strings into the log that way . )
*/
channel_binding_type = read_attr_value ( & input , ' p ' ) ;
if ( strcmp ( channel_binding_type , SCRAM_CHANNEL_BINDING_TLS_UNIQUE ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
( errmsg ( " unsupported SCRAM channel-binding type " ) ) ) ) ;
/* Save the name for handling of subsequent messages */
state - > channel_binding_type = pstrdup ( channel_binding_type ) ;
}
break ;
default :
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
@ -805,13 +874,6 @@ read_client_first_message(scram_state *state, char *input)
errdetail ( " Unexpected channel-binding flag \" %s \" . " ,
sanitize_char ( * input ) ) ) ) ;
}
if ( * input ! = ' , ' )
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
errmsg ( " malformed SCRAM message " ) ,
errdetail ( " Comma expected, but found character \" %s \" . " ,
sanitize_char ( * input ) ) ) ) ;
input + + ;
/*
* Forbid optional authzid ( authorization identity ) . We don ' t support it .
@ -1032,14 +1094,73 @@ read_client_final_message(scram_state *state, char *input)
*/
/*
* Read channel - binding . We don ' t support channel binding , so it ' s
* expected to always be " biws " , which is " n,, " , base64 - encoded .
* Read channel binding . This repeats the channel - binding flags and i s
* then followed by the actual binding data depending on the type .
*/
channel_binding = read_attr_value ( & p , ' c ' ) ;
if ( strcmp ( channel_binding , " biws " ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
( errmsg ( " unexpected SCRAM channel-binding attribute in client-final-message " ) ) ) ) ;
if ( state - > channel_binding_type )
{
const char * cbind_data = NULL ;
size_t cbind_data_len = 0 ;
size_t cbind_header_len ;
char * cbind_input ;
size_t cbind_input_len ;
char * b64_message ;
int b64_message_len ;
/*
* Fetch data appropriate for channel binding type
*/
if ( strcmp ( state - > channel_binding_type , SCRAM_CHANNEL_BINDING_TLS_UNIQUE ) = = 0 )
{
cbind_data = state - > tls_finished_message ;
cbind_data_len = state - > tls_finished_len ;
}
else
{
/* should not happen */
elog ( ERROR , " invalid channel binding type " ) ;
}
/* should not happen */
if ( cbind_data = = NULL | | cbind_data_len = = 0 )
elog ( ERROR , " empty channel binding data for channel binding type \" %s \" " ,
state - > channel_binding_type ) ;
cbind_header_len = 4 + strlen ( state - > channel_binding_type ) ; /* p=type,, */
cbind_input_len = cbind_header_len + cbind_data_len ;
cbind_input = palloc ( cbind_input_len ) ;
snprintf ( cbind_input , cbind_input_len , " p=%s,, " , state - > channel_binding_type ) ;
memcpy ( cbind_input + cbind_header_len , cbind_data , cbind_data_len ) ;
b64_message = palloc ( pg_b64_enc_len ( cbind_input_len ) + 1 ) ;
b64_message_len = pg_b64_encode ( cbind_input , cbind_input_len ,
b64_message ) ;
b64_message [ b64_message_len ] = ' \0 ' ;
/*
* Compare the value sent by the client with the value expected by
* the server .
*/
if ( strcmp ( channel_binding , b64_message ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION ) ,
( errmsg ( " SCRAM channel binding check failed " ) ) ) ) ;
}
else
{
/*
* If we are not using channel binding , the binding data is expected
* to always be " biws " , which is " n,, " base64 - encoded , or " eSws " ,
* which is " y,, " .
*/
if ( strcmp ( channel_binding , " biws " ) ! = 0 & &
strcmp ( channel_binding , " eSws " ) ! = 0 )
ereport ( ERROR ,
( errcode ( ERRCODE_PROTOCOL_VIOLATION ) ,
( errmsg ( " unexpected SCRAM channel-binding attribute in client-final-message " ) ) ) ) ;
}
state - > client_final_nonce = read_attr_value ( & p , ' r ' ) ;
/* ignore optional extensions */