From ed36471a4e18a317b30eec5ac03a4a03777cd0b2 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Tue, 2 Aug 2022 12:04:11 -0700 Subject: [PATCH] fix BB license check bug; clean up license commands --- src/License.cc | 78 +++++++++++++++---------- src/License.hh | 15 +++++ src/ReceiveCommands.cc | 98 ++++++++++++++++++++++++-------- src/ServerState.cc | 3 +- tests/BB-CreateCharGame.test.txt | 21 ++----- tests/PC-BasicGame.test.txt | 4 +- 6 files changed, 146 insertions(+), 73 deletions(-) diff --git a/src/License.cc b/src/License.cc index 904e5cdd..29cd9080 100644 --- a/src/License.cc +++ b/src/License.cc @@ -85,55 +85,71 @@ void LicenseManager::set_autosave(bool autosave) { shared_ptr LicenseManager::verify_pc(uint32_t serial_number, const string& access_key) const { - auto& license = this->serial_number_to_license.at(serial_number); - if (!license->access_key.eq_n(access_key, 8)) { - throw invalid_argument("incorrect access key"); - } + try { + auto& license = this->serial_number_to_license.at(serial_number); + if (!license->access_key.eq_n(access_key, 8)) { + throw incorrect_access_key(); + } - if (license->ban_end_time && (license->ban_end_time >= now())) { - throw invalid_argument("user is banned"); + if (license->ban_end_time && (license->ban_end_time >= now())) { + throw invalid_argument("user is banned"); + } + return license; + } catch (const out_of_range&) { + throw missing_license(); } - return license; } shared_ptr LicenseManager::verify_gc(uint32_t serial_number, const string& access_key) const { - auto& license = this->serial_number_to_license.at(serial_number); - if (!license->access_key.eq_n(access_key, 12)) { - throw invalid_argument("incorrect access key"); + try { + auto& license = this->serial_number_to_license.at(serial_number); + if (!license->access_key.eq_n(access_key, 12)) { + throw incorrect_access_key(); + } + if (license->ban_end_time && (license->ban_end_time >= now())) { + throw invalid_argument("user is banned"); + } + return license; + } catch (const out_of_range&) { + throw missing_license(); } - if (license->ban_end_time && (license->ban_end_time >= now())) { - throw invalid_argument("user is banned"); - } - return license; } shared_ptr LicenseManager::verify_gc(uint32_t serial_number, const string& access_key, const string& password) const { - auto& license = this->serial_number_to_license.at(serial_number); - if (!license->access_key.eq_n(access_key, 12)) { - throw invalid_argument("incorrect access key"); + try { + auto& license = this->serial_number_to_license.at(serial_number); + if (!license->access_key.eq_n(access_key, 12)) { + throw incorrect_access_key(); + } + if (license->gc_password != password) { + throw incorrect_password(); + } + if (license->ban_end_time && (license->ban_end_time >= now())) { + throw invalid_argument("user is banned"); + } + return license; + } catch (const out_of_range&) { + throw missing_license(); } - if (license->gc_password != password) { - throw invalid_argument("incorrect password"); - } - if (license->ban_end_time && (license->ban_end_time >= now())) { - throw invalid_argument("user is banned"); - } - return license; } shared_ptr LicenseManager::verify_bb(const string& username, const string& password) const { - auto& license = this->bb_username_to_license.at(username); - if (license->bb_password != password) { - throw invalid_argument("incorrect password"); - } + try { + auto& license = this->bb_username_to_license.at(username); + if (license->bb_password != password) { + throw incorrect_password(); + } - if (license->ban_end_time && (license->ban_end_time >= now())) { - throw invalid_argument("user is banned"); + if (license->ban_end_time && (license->ban_end_time >= now())) { + throw invalid_argument("user is banned"); + } + return license; + } catch (const out_of_range&) { + throw missing_license(); } - return license; } size_t LicenseManager::count() const { diff --git a/src/License.hh b/src/License.hh index 6cfba6ff..62886763 100644 --- a/src/License.hh +++ b/src/License.hh @@ -44,6 +44,21 @@ struct License { std::string str() const; } __attribute__((packed)); +class incorrect_password : public std::invalid_argument { +public: + incorrect_password() : invalid_argument("incorrect password") { } +}; + +class incorrect_access_key : public std::invalid_argument { +public: + incorrect_access_key() : invalid_argument("incorrect access key") { } +}; + +class missing_license : public std::invalid_argument { +public: + missing_license() : invalid_argument("missing license") { } +}; + class LicenseManager { public: LicenseManager(); diff --git a/src/ReceiveCommands.cc b/src/ReceiveCommands.cc index f03c075c..4fc01045 100644 --- a/src/ReceiveCommands.cc +++ b/src/ReceiveCommands.cc @@ -160,16 +160,28 @@ void process_disconnect(shared_ptr s, shared_ptr c) { void process_verify_license_v3(shared_ptr s, shared_ptr c, uint16_t, uint32_t, const string& data) { // DB const auto& cmd = check_size_t(data); + c->flags |= flags_for_version(c->version, cmd.sub_version); uint32_t serial_number = stoul(cmd.serial_number, nullptr, 16); try { auto l = s->license_manager->verify_gc(serial_number, cmd.access_key, cmd.password); c->set_license(l); - } catch (const exception& e) { + send_command(c, 0x9A, 0x02); + + } catch (const incorrect_access_key& e) { + send_command(c, 0x9A, 0x03); + c->should_disconnect = true; + return; + + } catch (const incorrect_password& e) { + send_command(c, 0x9A, 0x07); + c->should_disconnect = true; + return; + + } catch (const missing_license& e) { if (!s->allow_unregistered_users) { - u16string message = u"Login failed: " + decode_sjis(e.what()); - send_message_box(c, message.c_str()); + send_command(c, 0x9A, 0x04); c->should_disconnect = true; return; } else { @@ -177,11 +189,9 @@ void process_verify_license_v3(shared_ptr s, shared_ptr c, cmd.password, true); s->license_manager->add(l); c->set_license(l); + send_command(c, 0x9A, 0x02); } } - - c->flags |= flags_for_version(c->version, cmd.sub_version); - send_command(c, 0x9A, 0x02); } void process_login_a_dc_pc_v3(shared_ptr s, shared_ptr c, @@ -207,28 +217,37 @@ void process_login_a_dc_pc_v3(shared_ptr s, shared_ptr c, throw logic_error("unsupported versioned command"); } c->set_license(l); + send_command(c, 0x9A, 0x02); - } catch (const exception& e) { + } catch (const incorrect_access_key& e) { + send_command(c, 0x9A, 0x03); + c->should_disconnect = true; + return; + + } catch (const incorrect_password& e) { + send_command(c, 0x9A, 0x07); + c->should_disconnect = true; + return; + + } catch (const missing_license& e) { // On V3, the client should have sent a different command containing the // password already, which should have created and added a temporary // license. So, if no license exists at this point, disconnect the client // even if unregistered clients are allowed. shared_ptr l; if ((c->version == GameVersion::GC) || (c->version == GameVersion::XB)) { - u16string message = u"Login failed: " + decode_sjis(e.what()); - send_message_box(c, message.c_str()); + send_command(c, 0x9A, 0x04); c->should_disconnect = true; return; } else if (c->version == GameVersion::PC) { l = LicenseManager::create_license_pc(serial_number, cmd.access_key, true); + s->license_manager->add(l); + c->set_license(l); + send_command(c, 0x9A, 0x02); } else { throw runtime_error("unsupported game version"); } - s->license_manager->add(l); - c->set_license(l); } - - send_command(c, 0x9C, 0x01); } void process_login_c_dc_pc_v3(shared_ptr s, shared_ptr c, @@ -255,11 +274,16 @@ void process_login_c_dc_pc_v3(shared_ptr s, shared_ptr c, throw logic_error("unsupported versioned command"); } c->set_license(l); + send_command(c, 0x9C, 0x01); - } catch (const exception& e) { + } catch (const incorrect_password& e) { + send_command(c, 0x9C, 0x00); + c->should_disconnect = true; + return; + + } catch (const missing_license& e) { if (!s->allow_unregistered_users) { - u16string message = u"Login failed: " + decode_sjis(e.what()); - send_message_box(c, message.c_str()); + send_command(c, 0x9C, 0x00); c->should_disconnect = true; return; } else { @@ -281,10 +305,9 @@ void process_login_c_dc_pc_v3(shared_ptr s, shared_ptr c, } s->license_manager->add(l); c->set_license(l); + send_command(c, 0x9C, 0x01); } } - - send_command(c, 0x9C, 0x01); } void process_login_d_e_pc_v3(shared_ptr s, shared_ptr c, @@ -349,13 +372,33 @@ void process_login_d_e_pc_v3(shared_ptr s, shared_ptr c, } c->set_license(l); - } catch (const exception& e) { - // See comment in 9A handler about why we do this even if unregistered users - // are allowed on the server - u16string message = u"Login failed: " + decode_sjis(e.what()); - send_message_box(c, message.c_str()); + } catch (const incorrect_access_key& e) { + send_command(c, 0x04, 0x03); c->should_disconnect = true; return; + + } catch (const incorrect_password& e) { + send_command(c, 0x04, 0x06); + c->should_disconnect = true; + return; + + } catch (const missing_license& e) { + // On V3, the client should have sent a different command containing the + // password already, which should have created and added a temporary + // license. So, if no license exists at this point, disconnect the client + // even if unregistered clients are allowed. + shared_ptr l; + if ((c->version == GameVersion::GC) || (c->version == GameVersion::XB)) { + send_command(c, 0x04, 0x04); + c->should_disconnect = true; + return; + } else if (c->version == GameVersion::PC) { + l = LicenseManager::create_license_pc(serial_number, base_cmd->access_key, true); + s->license_manager->add(l); + c->set_license(l); + } else { + throw runtime_error("unsupported game version"); + } } if ((c->flags & Client::Flag::EPISODE_3) && (s->ep3_menu_song >= 0)) { @@ -386,7 +429,14 @@ void process_login_bb(shared_ptr s, shared_ptr c, try { auto l = s->license_manager->verify_bb(cmd.username, cmd.password); c->set_license(l); - } catch (const exception& e) { + + } catch (const incorrect_password& e) { + u16string message = u"Login failed: " + decode_sjis(e.what()); + send_message_box(c, message.c_str()); + c->should_disconnect = true; + return; + + } catch (const missing_license& e) { if (!s->allow_unregistered_users) { u16string message = u"Login failed: " + decode_sjis(e.what()); send_message_box(c, message.c_str()); diff --git a/src/ServerState.cc b/src/ServerState.cc index 53f56e57..f9523eef 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -383,7 +383,8 @@ void ServerState::create_menus(shared_ptr config_json) { this->main_menu.emplace_back(MainMenuItemID::DISCONNECT, u"Disconnect", u"Disconnect", 0); this->main_menu.emplace_back(MainMenuItemID::CLEAR_LICENSE, u"Clear license", - u"Disconnect with an\ninvalid license error\nso you can enter a\ndifferent serial\nnumber, access key,\nor password", 0); + u"Disconnect with an\ninvalid license error\nso you can enter a\ndifferent serial\nnumber, access key,\nor password", + MenuItem::Flag::INVISIBLE_ON_BB); try { this->welcome_message = decode_sjis(d.at("WelcomeMessage")->as_string()); diff --git a/tests/BB-CreateCharGame.test.txt b/tests/BB-CreateCharGame.test.txt index 5a9a40e1..b01ca906 100644 --- a/tests/BB-CreateCharGame.test.txt +++ b/tests/BB-CreateCharGame.test.txt @@ -29855,7 +29855,7 @@ I 80350 2022-07-07 23:27:02 - [Commands] Sending to C-7 (version=BB command=00E6 0000000000000030 | FF FF FF FF FF FF FF FF 04 00 FF FF FF FF FF FF | 0000000000000040 | 02 01 00 00 | I 80350 2022-07-07 23:27:02 - [Commands] Sending to C-7 (version=BB command=0007 flag=00000003) -0000000000000000 | B8 00 07 00 03 00 00 00 11 00 00 11 FF FF FF FF | +0000000000000000 | 8C 00 07 00 02 00 00 00 11 00 00 11 FF FF FF FF | 0000000000000010 | 04 00 41 00 6C 00 65 00 78 00 61 00 6E 00 64 00 | A l e x a n d 0000000000000020 | 72 00 69 00 61 00 00 00 00 00 00 00 00 00 00 00 | r i a 0000000000000030 | 00 00 00 00 11 00 00 11 11 22 22 11 04 00 47 00 | "" G @@ -29863,10 +29863,7 @@ I 80350 2022-07-07 23:27:02 - [Commands] Sending to C-7 (version=BB command=0007 0000000000000050 | 62 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00 | b y 0000000000000060 | 11 00 00 11 11 88 88 11 04 00 44 00 69 00 73 00 | D i s 0000000000000070 | 63 00 6F 00 6E 00 6E 00 65 00 63 00 74 00 00 00 | c o n n e c t -0000000000000080 | 00 00 00 00 00 00 00 00 00 00 00 00 11 00 00 11 | -0000000000000090 | 11 99 99 11 04 00 43 00 6C 00 65 00 61 00 72 00 | C l e a r -00000000000000A0 | 20 00 6C 00 69 00 63 00 65 00 6E 00 73 00 65 00 | l i c e n s e -00000000000000B0 | 00 00 00 00 00 00 00 00 | +0000000000000080 | 00 00 00 00 00 00 00 00 00 00 00 00 | I 80350 2022-07-07 23:27:03 - [Commands] Received from C-7 (version=BB command=0010 flag=00000000) 0000000000000000 | 10 00 10 00 00 00 00 00 11 00 00 11 11 22 22 11 | "" I 80350 2022-07-07 23:27:03 - [Commands] Sending to C-7 (version=BB command=0019 flag=00000000) @@ -31210,7 +31207,7 @@ I 80350 2022-07-07 23:27:19 - [Commands] Sending to C-9 (version=BB command=00E6 0000000000000030 | FF FF FF FF FF FF FF FF 04 00 FF FF FF FF FF FF | 0000000000000040 | 02 01 00 00 | I 80350 2022-07-07 23:27:19 - [Commands] Sending to C-9 (version=BB command=0007 flag=00000003) -0000000000000000 | B8 00 07 00 03 00 00 00 11 00 00 11 FF FF FF FF | +0000000000000000 | 8C 00 07 00 02 00 00 00 11 00 00 11 FF FF FF FF | 0000000000000010 | 04 00 41 00 6C 00 65 00 78 00 61 00 6E 00 64 00 | A l e x a n d 0000000000000020 | 72 00 69 00 61 00 00 00 00 00 00 00 00 00 00 00 | r i a 0000000000000030 | 00 00 00 00 11 00 00 11 11 22 22 11 04 00 47 00 | "" G @@ -31218,10 +31215,7 @@ I 80350 2022-07-07 23:27:19 - [Commands] Sending to C-9 (version=BB command=0007 0000000000000050 | 62 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00 | b y 0000000000000060 | 11 00 00 11 11 88 88 11 04 00 44 00 69 00 73 00 | D i s 0000000000000070 | 63 00 6F 00 6E 00 6E 00 65 00 63 00 74 00 00 00 | c o n n e c t -0000000000000080 | 00 00 00 00 00 00 00 00 00 00 00 00 11 00 00 11 | -0000000000000090 | 11 99 99 11 04 00 43 00 6C 00 65 00 61 00 72 00 | C l e a r -00000000000000A0 | 20 00 6C 00 69 00 63 00 65 00 6E 00 73 00 65 00 | l i c e n s e -00000000000000B0 | 00 00 00 00 00 00 00 00 | +0000000000000080 | 00 00 00 00 00 00 00 00 00 00 00 00 | I 80350 2022-07-07 23:27:20 - [Commands] Received from C-9 (version=BB command=0010 flag=00000000) 0000000000000000 | 10 00 10 00 00 00 00 00 11 00 00 11 11 22 22 11 | "" I 80350 2022-07-07 23:27:20 - [Commands] Sending to C-9 (version=BB command=0019 flag=00000000) @@ -33783,7 +33777,7 @@ I 80350 2022-07-07 23:29:17 - [Commands] Sending to C-B (version=BB command=00E6 0000000000000030 | FF FF FF FF FF FF FF FF 04 00 FF FF FF FF FF FF | 0000000000000040 | 02 01 00 00 | I 80350 2022-07-07 23:29:17 - [Commands] Sending to C-B (version=BB command=0007 flag=00000003) -0000000000000000 | B8 00 07 00 03 00 00 00 11 00 00 11 FF FF FF FF | +0000000000000000 | 8C 00 07 00 02 00 00 00 11 00 00 11 FF FF FF FF | 0000000000000010 | 04 00 41 00 6C 00 65 00 78 00 61 00 6E 00 64 00 | A l e x a n d 0000000000000020 | 72 00 69 00 61 00 00 00 00 00 00 00 00 00 00 00 | r i a 0000000000000030 | 00 00 00 00 11 00 00 11 11 22 22 11 04 00 47 00 | "" G @@ -33791,10 +33785,7 @@ I 80350 2022-07-07 23:29:17 - [Commands] Sending to C-B (version=BB command=0007 0000000000000050 | 62 00 79 00 00 00 00 00 00 00 00 00 00 00 00 00 | b y 0000000000000060 | 11 00 00 11 11 88 88 11 04 00 44 00 69 00 73 00 | D i s 0000000000000070 | 63 00 6F 00 6E 00 6E 00 65 00 63 00 74 00 00 00 | c o n n e c t -0000000000000080 | 00 00 00 00 00 00 00 00 00 00 00 00 11 00 00 11 | -0000000000000090 | 11 99 99 11 04 00 43 00 6C 00 65 00 61 00 72 00 | C l e a r -00000000000000A0 | 20 00 6C 00 69 00 63 00 65 00 6E 00 73 00 65 00 | l i c e n s e -00000000000000B0 | 00 00 00 00 00 00 00 00 | +0000000000000080 | 00 00 00 00 00 00 00 00 00 00 00 00 | I 80350 2022-07-07 23:29:19 - [Commands] Received from C-B (version=BB command=0010 flag=00000000) 0000000000000000 | 10 00 10 00 00 00 00 00 11 00 00 11 11 88 88 11 | I 80350 2022-07-07 23:29:19 - [Server] Client disconnected: C-B on fd 29 diff --git a/tests/PC-BasicGame.test.txt b/tests/PC-BasicGame.test.txt index 8aed1f8c..10b1455a 100644 --- a/tests/PC-BasicGame.test.txt +++ b/tests/PC-BasicGame.test.txt @@ -103,7 +103,7 @@ I 80820 2022-07-07 23:34:04 - [Commands] Received from C-2 (version=PC command=9 00000000000000C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00000000000000D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 80820 2022-07-07 23:34:04 - [Commands] Sending to C-2 (version=PC command=9C flag=01) -0000000000000000 | 04 00 9C 01 | +0000000000000000 | 04 00 9A 02 | I 80820 2022-07-07 23:34:04 - [Commands] Received from C-2 (version=PC command=9D flag=01) 0000000000000000 | 50 01 9D 01 00 00 FF FF FF FF FF FF 00 00 FF FF | P 0000000000000010 | FF FF FF FF 29 00 00 00 00 01 00 00 00 00 00 00 | ) @@ -2105,7 +2105,7 @@ I 80820 2022-07-07 23:37:10 - [Commands] Received from C-4 (version=PC command=9 00000000000000C0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | 00000000000000D0 | 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | I 80820 2022-07-07 23:37:10 - [Commands] Sending to C-4 (version=PC command=9C flag=01) -0000000000000000 | 04 00 9C 01 | +0000000000000000 | 04 00 9A 02 | I 80820 2022-07-07 23:37:10 - [Commands] Received from C-4 (version=PC command=9D flag=01) 0000000000000000 | 50 01 9D 01 00 00 01 00 78 62 F8 10 00 00 FF FF | P xb 0000000000000010 | FF FF FF FF 29 00 00 00 00 01 00 00 00 00 00 00 | )