Example #1
0
/*
 * pg_be_scram_init
 *
 * Initialize a new SCRAM authentication exchange status tracker.  This
 * needs to be called before doing any exchange.  It will be filled later
 * after the beginning of the exchange with verifier data.
 *
 * 'username' is the username provided by the client in the startup message.
 * 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
 * If 'shadow_pass' is NULL, we still perform an authentication exchange, but
 * it will fail, as if an incorrect password was given.
 */
void *
pg_be_scram_init(const char *username, const char *shadow_pass)
{
	scram_state *state;
	bool		got_verifier;

	state = (scram_state *) palloc0(sizeof(scram_state));
	state->state = SCRAM_AUTH_INIT;
	state->username = username;

	/*
	 * Parse the stored password verifier.
	 */
	if (shadow_pass)
	{
		int			password_type = get_password_type(shadow_pass);

		if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
		{
			if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
									 state->StoredKey, state->ServerKey))
				got_verifier = true;
			else
			{
				/*
				 * The password looked like a SCRAM verifier, but could not be
				 * parsed.
				 */
				ereport(LOG,
						(errmsg("invalid SCRAM verifier for user \"%s\"",
								username)));
				got_verifier = false;
			}
		}
		else
		{
			/*
			 * The user doesn't have SCRAM verifier. (You cannot do SCRAM
			 * authentication with an MD5 hash.)
			 */
			state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
										state->username);
			got_verifier = false;
		}
	}
	else
	{
		/*
		 * The caller requested us to perform a dummy authentication.  This is
		 * considered normal, since the caller requested it, so don't set log
		 * detail.
		 */
		got_verifier = false;
	}

	/*
	 * If the user did not have a valid SCRAM verifier, we still go through
	 * the motions with a mock one, and fail as if the client supplied an
	 * incorrect password.  This is to avoid revealing information to an
	 * attacker.
	 */
	if (!got_verifier)
	{
		mock_scram_verifier(username, &state->iterations, &state->salt,
							state->StoredKey, state->ServerKey);
		state->doomed = true;
	}

	return state;
}
Example #2
0
/*
 * pg_be_scram_init
 *
 * Initialize a new SCRAM authentication exchange status tracker.  This
 * needs to be called before doing any exchange.  It will be filled later
 * after the beginning of the exchange with verifier data.
 *
 * 'selected_mech' identifies the SASL mechanism that the client selected.
 * It should be one of the mechanisms that we support, as returned by
 * pg_be_scram_get_mechanisms().
 *
 * 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
 * The username was provided by the client in the startup message, and is
 * available in port->user_name.  If 'shadow_pass' is NULL, we still perform
 * an authentication exchange, but it will fail, as if an incorrect password
 * was given.
 */
void *
pg_be_scram_init(Port *port,
				 const char *selected_mech,
				 const char *shadow_pass)
{
	scram_state *state;
	bool		got_verifier;

	state = (scram_state *) palloc0(sizeof(scram_state));
	state->port = port;
	state->state = SCRAM_AUTH_INIT;

	/*
	 * Parse the selected mechanism.
	 *
	 * Note that if we don't support channel binding, either because the SSL
	 * implementation doesn't support it or we're not using SSL at all, we
	 * would not have advertised the PLUS variant in the first place.  If the
	 * client nevertheless tries to select it, it's a protocol violation like
	 * selecting any other SASL mechanism we don't support.
	 */
#ifdef HAVE_BE_TLS_GET_CERTIFICATE_HASH
	if (strcmp(selected_mech, SCRAM_SHA_256_PLUS_NAME) == 0 && port->ssl_in_use)
		state->channel_binding_in_use = true;
	else
#endif
	if (strcmp(selected_mech, SCRAM_SHA_256_NAME) == 0)
		state->channel_binding_in_use = false;
	else
		ereport(ERROR,
				(errcode(ERRCODE_PROTOCOL_VIOLATION),
				 errmsg("client selected an invalid SASL authentication mechanism")));

	/*
	 * Parse the stored password verifier.
	 */
	if (shadow_pass)
	{
		int			password_type = get_password_type(shadow_pass);

		if (password_type == PASSWORD_TYPE_SCRAM_SHA_256)
		{
			if (parse_scram_verifier(shadow_pass, &state->iterations, &state->salt,
									 state->StoredKey, state->ServerKey))
				got_verifier = true;
			else
			{
				/*
				 * The password looked like a SCRAM verifier, but could not be
				 * parsed.
				 */
				ereport(LOG,
						(errmsg("invalid SCRAM verifier for user \"%s\"",
								state->port->user_name)));
				got_verifier = false;
			}
		}
		else
		{
			/*
			 * The user doesn't have SCRAM verifier. (You cannot do SCRAM
			 * authentication with an MD5 hash.)
			 */
			state->logdetail = psprintf(_("User \"%s\" does not have a valid SCRAM verifier."),
										state->port->user_name);
			got_verifier = false;
		}
	}
	else
	{
		/*
		 * The caller requested us to perform a dummy authentication.  This is
		 * considered normal, since the caller requested it, so don't set log
		 * detail.
		 */
		got_verifier = false;
	}

	/*
	 * If the user did not have a valid SCRAM verifier, we still go through
	 * the motions with a mock one, and fail as if the client supplied an
	 * incorrect password.  This is to avoid revealing information to an
	 * attacker.
	 */
	if (!got_verifier)
	{
		mock_scram_verifier(state->port->user_name, &state->iterations,
							&state->salt, state->StoredKey, state->ServerKey);
		state->doomed = true;
	}

	return state;
}