@@ -109,111 +109,59 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) {
109
109
std::string userLogin;
110
110
std::string userToken; // could be password or auth cookie
111
111
112
+ /*
113
+ * In this context, "cookie auth" just means the credentials were sent
114
+ * in the szCookie fields instead of szID and szPassword.
115
+ */
116
+ bool isCookieAuth = login->iLoginType == USE_COOKIE_FIELDS;
117
+
112
118
/*
113
119
* The std::string -> char* -> std::string maneuver should remove any
114
120
* trailing garbage after the null terminator.
115
121
*/
116
- if (login-> iLoginType == ( int32_t )LoginType::COOKIE ) {
122
+ if (isCookieAuth ) {
117
123
userLogin = std::string (AUTOU8 (login->szCookie_TEGid ).c_str ());
118
124
userToken = std::string (AUTOU8 (login->szCookie_authid ).c_str ());
119
125
} else {
120
126
userLogin = std::string (AUTOU16TOU8 (login->szID ).c_str ());
121
127
userToken = std::string (AUTOU16TOU8 (login->szPassword ).c_str ());
122
128
}
123
129
124
- // check username regex
125
- if (!CNLoginServer::isUsernameGood (userLogin)) {
126
- // send a custom error message
127
- INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
128
- std::string text = " Invalid login\n " ;
129
- text += " Login has to be 4 - 32 characters long and can't contain special characters other than dash and underscore" ;
130
- U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
131
- msg.iDuringTime = 10 ;
132
- sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
133
-
134
- // we still have to send login fail to prevent softlock
130
+ if (!CNLoginServer::checkUsername (sock, userLogin)) {
135
131
return loginFail (LoginError::LOGIN_ERROR, userLogin, sock);
136
132
}
137
133
138
- // we only interpret the token as a cookie if cookie login was used and it's allowed.
139
- // otherwise we interpret it as a password, and this maintains compatibility with
140
- // the auto-login trick used on older clients
141
- bool isCookieAuth = login->iLoginType == (int32_t )LoginType::COOKIE
142
- && CNLoginServer::isLoginTypeAllowed (LoginType::COOKIE);
143
-
144
- // password login checks
145
134
if (!isCookieAuth) {
146
- // bail if password auth isn't allowed
147
- if (!CNLoginServer::isLoginTypeAllowed (LoginType::PASSWORD)) {
148
- // send a custom error message
149
- INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
150
- std::string text = " Password login disabled\n " ;
151
- text += " This server has disabled logging in with plaintext passwords.\n " ;
152
- text += " Please contact an admin for assistance." ;
153
- U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
154
- msg.iDuringTime = 12 ;
155
- sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
156
-
157
- // we still have to send login fail to prevent softlock
158
- return loginFail (LoginError::LOGIN_ERROR, userLogin, sock);
159
- }
160
-
161
- // check regex
162
- if (!CNLoginServer::isPasswordGood (userToken)) {
163
- // send a custom error message
164
- INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
165
- std::string text = " Invalid password\n " ;
166
- text += " Password has to be 8 - 32 characters long" ;
167
- U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
168
- msg.iDuringTime = 10 ;
169
- sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
170
-
171
- // we still have to send login fail to prevent softlock
135
+ // password was sent in plaintext
136
+ if (!CNLoginServer::checkPassword (sock, userToken)) {
172
137
return loginFail (LoginError::LOGIN_ERROR, userLogin, sock);
173
138
}
174
139
}
175
140
176
141
Database::Account findUser = {};
177
142
Database::findAccount (&findUser, userLogin);
178
-
143
+
179
144
// account was not found
180
145
if (findUser.AccountID == 0 ) {
181
- // don't auto-create an account if it's a cookie auth for whatever reason
182
- if (settings::AUTOCREATEACCOUNTS && !isCookieAuth)
146
+ /*
147
+ * Don't auto-create accounts if it's a cookie login.
148
+ * It'll either be a bad cookie or a plaintext password sent by auto-login;
149
+ * either way, we only want to allow auto-creation if the user explicitly entered their credentials.
150
+ */
151
+ if (settings::AUTOCREATEACCOUNTS && !isCookieAuth) {
183
152
return newAccount (sock, userLogin, userToken, login->iClientVerC );
153
+ }
184
154
185
155
return loginFail (LoginError::ID_DOESNT_EXIST, userLogin, sock);
186
156
}
187
157
188
- if (isCookieAuth) {
189
- const char *cookie = userToken.c_str ();
190
- if (!Database::checkCookie (findUser.AccountID , cookie))
191
- return loginFail (LoginError::ID_AND_PASSWORD_DO_NOT_MATCH, userLogin, sock);
192
- } else {
193
- // simple password check
194
- if (!CNLoginServer::isPasswordCorrect (findUser.Password , userToken))
195
- return loginFail (LoginError::ID_AND_PASSWORD_DO_NOT_MATCH, userLogin, sock);
158
+ // make sure either a valid cookie or password was sent
159
+ if (!CNLoginServer::checkToken (sock, findUser, userToken, isCookieAuth)) {
160
+ return loginFail (LoginError::ID_AND_PASSWORD_DO_NOT_MATCH, userLogin, sock);
196
161
}
197
162
198
- // is the account banned
199
- if (findUser.BannedUntil > getTimestamp ()) {
200
- // send a custom error message
201
- INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
202
-
203
- // ceiling devision
204
- int64_t remainingDays = (findUser.BannedUntil -getTimestamp ()) / 86400 + ((findUser.BannedUntil - getTimestamp ()) % 86400 != 0 );
205
-
206
- std::string text = " Your account has been banned. \n Reason: " ;
207
- text += findUser.BanReason ;
208
- text += " \n Ban expires in " + std::to_string (remainingDays) + " day" ;
209
- if (remainingDays > 1 )
210
- text += " s" ;
211
-
212
- U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
213
- msg.iDuringTime = 99999999 ;
214
- sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
215
- // don't send fail packet
216
- return ;
163
+ if (CNLoginServer::checkBan (sock, findUser)) {
164
+ return ; // don't send fail packet
217
165
}
218
166
219
167
/*
@@ -659,12 +607,12 @@ bool CNLoginServer::exitDuplicate(int accountId) {
659
607
return false ;
660
608
}
661
609
662
- bool CNLoginServer::isUsernameGood (std::string login) {
610
+ bool CNLoginServer::isUsernameGood (std::string& login) {
663
611
const std::regex loginRegex (" [a-zA-Z0-9_-]{4,32}" );
664
612
return (std::regex_match (login, loginRegex));
665
613
}
666
614
667
- bool CNLoginServer::isPasswordGood (std::string password) {
615
+ bool CNLoginServer::isPasswordGood (std::string& password) {
668
616
const std::regex passwordRegex (" [a-zA-Z0-9!@#$%^&*()_+]{8,32}" );
669
617
return (std::regex_match (password, passwordRegex));
670
618
}
@@ -680,16 +628,107 @@ bool CNLoginServer::isCharacterNameGood(std::string Firstname, std::string Lastn
680
628
return (std::regex_match (Firstname, firstnamecheck) && std::regex_match (Lastname, lastnamecheck));
681
629
}
682
630
683
- bool CNLoginServer::isLoginTypeAllowed (LoginType loginType ) {
631
+ bool CNLoginServer::isAuthMethodAllowed (AuthMethod authMethod ) {
684
632
// the config file specifies "comma-separated" but tbh we don't care
685
- switch (loginType ) {
686
- case LoginType ::PASSWORD:
633
+ switch (authMethod ) {
634
+ case AuthMethod ::PASSWORD:
687
635
return settings::AUTHMETHODS.find (" password" ) != std::string::npos;
688
- case LoginType ::COOKIE:
636
+ case AuthMethod ::COOKIE:
689
637
return settings::AUTHMETHODS.find (" cookie" ) != std::string::npos;
690
638
default :
691
639
break ;
692
640
}
693
641
return false ;
694
642
}
643
+
644
+ bool CNLoginServer::checkPassword (CNSocket* sock, std::string& password) {
645
+ // check password auth allowed
646
+ if (!CNLoginServer::isAuthMethodAllowed (AuthMethod::PASSWORD)) {
647
+ // send a custom error message
648
+ INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
649
+ std::string text = " Password login disabled\n " ;
650
+ text += " This server has disabled logging in with plaintext passwords.\n " ;
651
+ text += " Please contact an admin for assistance." ;
652
+ U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
653
+ msg.iDuringTime = 12 ;
654
+ sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
655
+
656
+ return false ;
657
+ }
658
+
659
+ // check regex
660
+ if (!CNLoginServer::isPasswordGood (password)) {
661
+ // send a custom error message
662
+ INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
663
+ std::string text = " Invalid password\n " ;
664
+ text += " Password has to be 8 - 32 characters long" ;
665
+ U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
666
+ msg.iDuringTime = 10 ;
667
+ sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
668
+
669
+ return false ;
670
+ }
671
+
672
+ return true ;
673
+ }
674
+
675
+ bool CNLoginServer::checkUsername (CNSocket* sock, std::string& username) {
676
+ // check username regex
677
+ if (!CNLoginServer::isUsernameGood (username)) {
678
+ // send a custom error message
679
+ INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
680
+ std::string text = " Invalid login\n " ;
681
+ text += " Login has to be 4 - 32 characters long and can't contain special characters other than dash and underscore" ;
682
+ U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
683
+ msg.iDuringTime = 10 ;
684
+ sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
685
+
686
+ return false ;
687
+ }
688
+
689
+ return true ;
690
+ }
691
+
692
+ bool CNLoginServer::checkToken (CNSocket* sock, Database::Account& account, std::string& token, bool isCookieAuth) {
693
+ // check for valid cookie first
694
+ if (isCookieAuth && CNLoginServer::isAuthMethodAllowed (AuthMethod::COOKIE)) {
695
+ const char *cookie = token.c_str ();
696
+ if (Database::checkCookie (account.AccountID , cookie)) {
697
+ return true ;
698
+ }
699
+ }
700
+
701
+ // cookie check failed; check to see if it's a plaintext password sent by auto-login
702
+ if (CNLoginServer::isAuthMethodAllowed (AuthMethod::PASSWORD)
703
+ && CNLoginServer::isPasswordCorrect (account.Password , token)) {
704
+ return true ;
705
+ }
706
+
707
+ return false ;
708
+ }
709
+
710
+ bool CNLoginServer::checkBan (CNSocket* sock, Database::Account& account) {
711
+ // check if the account is banned
712
+ if (account.BannedUntil > getTimestamp ()) {
713
+ // send a custom error message
714
+ INITSTRUCT (sP_FE2CL_GM_REP_PC_ANNOUNCE , msg);
715
+
716
+ // ceiling devision
717
+ int64_t remainingDays = (account.BannedUntil -getTimestamp ()) / 86400 + ((account.BannedUntil - getTimestamp ()) % 86400 != 0 );
718
+
719
+ std::string text = " Your account has been banned. \n Reason: " ;
720
+ text += account.BanReason ;
721
+ text += " \n Ban expires in " + std::to_string (remainingDays) + " day" ;
722
+ if (remainingDays > 1 )
723
+ text += " s" ;
724
+
725
+ U8toU16 (text, msg.szAnnounceMsg , sizeof (msg.szAnnounceMsg ));
726
+ msg.iDuringTime = 99999999 ;
727
+ sock->sendPacket (msg, P_FE2CL_GM_REP_PC_ANNOUNCE);
728
+
729
+ return true ;
730
+ }
731
+
732
+ return false ;
733
+ }
695
734
#pragma endregion
0 commit comments