/* * 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; }
/* * 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; }