+
+void Server::handleCommand_FirstSrp(NetworkPacket* pkt)
+{
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
+ ClientState cstate = client->getState();
+
+ std::string playername = client->getName();
+
+ std::string salt;
+ std::string verification_key;
+
+ std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString();
+ u8 is_empty;
+
+ *pkt >> salt >> verification_key >> is_empty;
+
+ verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s
+ << ", with is_empty=" << (is_empty == 1) << std::endl;
+
+ // Either this packet is sent because the user is new or to change the password
+ if (cstate == CS_HelloSent) {
+ if (!client->isMechAllowed(AUTH_MECHANISM_FIRST_SRP)) {
+ actionstream << "Server: Client from " << addr_s
+ << " tried to set password without being "
+ << "authenticated, or the username being new." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+
+ if (!isSingleplayer() &&
+ g_settings->getBool("disallow_empty_password") &&
+ is_empty == 1) {
+ actionstream << "Server: " << playername
+ << " supplied empty password from " << addr_s << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
+ return;
+ }
+
+ std::string initial_ver_key;
+
+ initial_ver_key = encode_srp_verifier(verification_key, salt);
+ m_script->createAuth(playername, initial_ver_key);
+
+ acceptAuth(pkt->getPeerId(), false);
+ } else {
+ if (cstate < CS_SudoMode) {
+ infostream << "Server::ProcessData(): Ignoring TOSERVER_FIRST_SRP from "
+ << addr_s << ": " << "Client has wrong state " << cstate << "."
+ << std::endl;
+ return;
+ }
+ m_clients.event(pkt->getPeerId(), CSE_SudoLeave);
+ std::string pw_db_field = encode_srp_verifier(verification_key, salt);
+ bool success = m_script->setPassword(playername, pw_db_field);
+ if (success) {
+ actionstream << playername << " changes password" << std::endl;
+ SendChatMessage(pkt->getPeerId(), L"Password change successful.");
+ } else {
+ actionstream << playername << " tries to change password but "
+ << "it fails" << std::endl;
+ SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable.");
+ }
+ }
+}
+
+void Server::handleCommand_SrpBytesA(NetworkPacket* pkt)
+{
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
+ ClientState cstate = client->getState();
+
+ bool wantSudo = (cstate == CS_Active);
+
+ if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
+ actionstream << "Server: got SRP _A packet in wrong state "
+ << cstate << " from "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << ". Ignoring." << std::endl;
+ return;
+ }
+
+ if (client->chosen_mech != AUTH_MECHANISM_NONE) {
+ actionstream << "Server: got SRP _A packet, while auth"
+ << "is already going on with mech " << client->chosen_mech
+ << " from " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl;
+ if (wantSudo) {
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ std::string bytes_A;
+ u8 based_on;
+ *pkt >> bytes_A >> based_on;
+
+ infostream << "Server: TOSERVER_SRP_BYTES_A received with "
+ << "based_on=" << int(based_on) << " and len_A="
+ << bytes_A.length() << "." << std::endl;
+
+ AuthMechanism chosen = (based_on == 0) ?
+ AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP;
+
+ if (wantSudo) {
+ if (!client->isSudoMechAllowed(chosen)) {
+ actionstream << "Server: Player \"" << client->getName()
+ << "\" at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " tried to change password using unallowed mech "
+ << chosen << "." << std::endl;
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ }
+ } else {
+ if (!client->isMechAllowed(chosen)) {
+ actionstream << "Server: Client tried to authenticate from "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " using unallowed mech " << chosen << "." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ client->chosen_mech = chosen;
+
+ std::string salt;
+ std::string verifier;
+
+ if (based_on == 0) {
+
+ generate_srp_verifier_and_salt(client->getName(), client->enc_pwd,
+ &verifier, &salt);
+ } else if (!decode_srp_verifier_and_salt(client->enc_pwd, &verifier, &salt)) {
+ // Non-base64 errors should have been catched in the init handler
+ actionstream << "Server: User " << client->getName()
+ << " tried to log in, but srp verifier field"
+ << " was invalid (most likely invalid base64)." << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+ return;
+ }
+
+ char *bytes_B = 0;
+ size_t len_B = 0;
+
+ client->auth_data = srp_verifier_new(SRP_SHA256, SRP_NG_2048,
+ client->getName().c_str(),
+ (const unsigned char *) salt.c_str(), salt.size(),
+ (const unsigned char *) verifier.c_str(), verifier.size(),
+ (const unsigned char *) bytes_A.c_str(), bytes_A.size(),
+ NULL, 0,
+ (unsigned char **) &bytes_B, &len_B, NULL, NULL);
+
+ if (!bytes_B) {
+ actionstream << "Server: User " << client->getName()
+ << " tried to log in, SRP-6a safety check violated in _A handler."
+ << std::endl;
+ if (wantSudo) {
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId());
+ resp_pkt << salt << std::string(bytes_B, len_B);
+ Send(&resp_pkt);
+}
+
+void Server::handleCommand_SrpBytesM(NetworkPacket* pkt)
+{
+ RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid);
+ ClientState cstate = client->getState();
+
+ bool wantSudo = (cstate == CS_Active);
+
+ verbosestream << "Server: Recieved TOCLIENT_SRP_BYTES_M." << std::endl;
+
+ if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) {
+ actionstream << "Server: got SRP _M packet in wrong state "
+ << cstate << " from "
+ << getPeerAddress(pkt->getPeerId()).serializeString()
+ << ". Ignoring." << std::endl;
+ return;
+ }
+
+ if ((client->chosen_mech != AUTH_MECHANISM_SRP)
+ && (client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD)) {
+ actionstream << "Server: got SRP _M packet, while auth"
+ << "is going on with mech " << client->chosen_mech
+ << " from " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " (wantSudo=" << wantSudo << "). Denying." << std::endl;
+ if (wantSudo) {
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+ }
+
+ std::string bytes_M;
+ *pkt >> bytes_M;
+
+ if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data)
+ != bytes_M.size()) {
+ actionstream << "Server: User " << client->getName()
+ << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " sent bytes_M with invalid length " << bytes_M.size() << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA);
+ return;
+ }
+
+ unsigned char *bytes_HAMK = 0;
+
+ srp_verifier_verify_session((SRPVerifier *) client->auth_data,
+ (unsigned char *)bytes_M.c_str(), &bytes_HAMK);
+
+ if (!bytes_HAMK) {
+ if (wantSudo) {
+ actionstream << "Server: User " << client->getName()
+ << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " tried to change their password, but supplied wrong"
+ << " (SRP) password for authentication." << std::endl;
+ DenySudoAccess(pkt->getPeerId());
+ return;
+ } else {
+ actionstream << "Server: User " << client->getName()
+ << " at " << getPeerAddress(pkt->getPeerId()).serializeString()
+ << " supplied wrong password (auth mechanism: SRP)."
+ << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD);
+ return;
+ }
+ }
+
+ if (client->create_player_on_auth_success) {
+ std::string playername = client->getName();
+ m_script->createAuth(playername, client->enc_pwd);
+
+ std::string checkpwd; // not used, but needed for passing something
+ if (!m_script->getAuth(playername, &checkpwd, NULL)) {
+ actionstream << "Server: " << playername << " cannot be authenticated"
+ << " (auth handler does not work?)" << std::endl;
+ DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL);
+ return;
+ }
+ client->create_player_on_auth_success = false;
+ }
+
+ acceptAuth(pkt->getPeerId(), wantSudo);
+}