From d2d734ce0983fbed72e69f555f57f29de04f30b3 Mon Sep 17 00:00:00 2001 From: shennetsind Date: Sat, 9 Mar 2013 00:04:28 -0300 Subject: Hercules Renewal'd Pin Code Feature is not, I repeat, NOT complete. the decryption is not fully functional which leads to dial values different from the ones the player used. Credits: lemongrass3110 for the base yommy for the packets LightFighter for the decrypt function (altho its not stable :P) Signed-off-by: shennetsind --- src/char/CMakeLists.txt | 2 + src/char/Makefile.in | 4 +- src/char/char.c | 106 ++++++++++++++++++--------- src/char/char.h | 23 +++++- src/char/pincode.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++ src/char/pincode.h | 41 +++++++++++ src/login/account.h | 26 ++++--- src/login/account_sql.c | 14 +++- src/login/login.c | 47 +++++++++++- 9 files changed, 396 insertions(+), 58 deletions(-) create mode 100644 src/char/pincode.c create mode 100644 src/char/pincode.h (limited to 'src') diff --git a/src/char/CMakeLists.txt b/src/char/CMakeLists.txt index 8c71a3b4c..9413ff303 100644 --- a/src/char/CMakeLists.txt +++ b/src/char/CMakeLists.txt @@ -23,6 +23,7 @@ set( SQL_CHAR_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/int_quest.h" "${CMAKE_CURRENT_SOURCE_DIR}/int_storage.h" "${CMAKE_CURRENT_SOURCE_DIR}/inter.h" + "${CMAKE_CURRENT_SOURCE_DIR}/pincode.h" ) set( SQL_CHAR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/char.c" @@ -37,6 +38,7 @@ set( SQL_CHAR_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/int_quest.c" "${CMAKE_CURRENT_SOURCE_DIR}/int_storage.c" "${CMAKE_CURRENT_SOURCE_DIR}/inter.c" + "${CMAKE_CURRENT_SOURCE_DIR}/pincode.c" ) set( DEPENDENCIES common_sql ) set( LIBRARIES ${GLOBAL_LIBRARIES} ) diff --git a/src/char/Makefile.in b/src/char/Makefile.in index a64145dea..a320579b5 100644 --- a/src/char/Makefile.in +++ b/src/char/Makefile.in @@ -16,8 +16,8 @@ COMMON_SQL_OBJ = ../common/obj_sql/sql.o COMMON_H = ../common/sql.h CHAR_OBJ = obj_sql/char.o obj_sql/inter.o obj_sql/int_party.o obj_sql/int_guild.o \ - obj_sql/int_storage.o obj_sql/int_pet.o obj_sql/int_homun.o obj_sql/int_mail.o obj_sql/int_auction.o obj_sql/int_quest.o obj_sql/int_mercenary.o obj_sql/int_elemental.o -CHAR_H = char.h inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h int_mail.h int_auction.h int_quest.h int_mercenary.h int_elemental.h + obj_sql/int_storage.o obj_sql/int_pet.o obj_sql/int_homun.o obj_sql/int_mail.o obj_sql/int_auction.o obj_sql/int_quest.o obj_sql/int_mercenary.o obj_sql/int_elemental.o obj_sql/pincode.o +CHAR_H = char.h inter.h int_party.h int_guild.h int_storage.h int_pet.h int_homun.h int_mail.h int_auction.h int_quest.h int_mercenary.h int_elemental.h pincode.h HAVE_MYSQL=@HAVE_MYSQL@ ifeq ($(HAVE_MYSQL),yes) diff --git a/src/char/char.c b/src/char/char.c index aed678684..4223fdded 100644 --- a/src/char/char.c +++ b/src/char/char.c @@ -21,6 +21,7 @@ #include "int_storage.h" #include "char.h" #include "inter.h" +#include "pincode.h" #include #include @@ -83,7 +84,7 @@ struct mmo_map_server { unsigned short map[MAX_MAP_PER_SERVER]; } server[MAX_MAP_SERVERS]; -int login_fd=-1, char_fd=-1; +int char_fd=-1; char userid[24]; char passwd[24]; char server_name[20]; @@ -121,20 +122,6 @@ struct s_subnet { } subnet[16]; int subnet_count = 0; -struct char_session_data { - bool auth; // whether the session is authed or not - int account_id, login_id1, login_id2, sex; - int found_char[MAX_CHARS]; // ids of chars on this account - char email[40]; // e-mail (default: a@a.com) by [Yor] - time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) - int group_id; // permission - uint8 char_slots; - uint32 version; - uint8 clienttype; - char new_name[NAME_LENGTH]; - char birthdate[10+1]; // YYYY-MM-DD -}; - int max_connect_user = -1; int gm_allow_group = -1; int autosave_interval = DEFAULT_AUTOSAVE_INTERVAL; @@ -2149,13 +2136,12 @@ int parse_fromlogin(int fd) { break; case 0x2717: // account data - if (RFIFOREST(fd) < 63) + if (RFIFOREST(fd) < 72) return 0; // find the authenticated session with this account id ARR_FIND( 0, fd_max, i, session[i] && (sd = (struct char_session_data*)session[i]->session_data) && sd->auth && sd->account_id == RFIFOL(fd,2) ); - if( i < fd_max ) - { + if( i < fd_max ) { int server_id; memcpy(sd->email, RFIFOP(fd,6), 40); sd->expiration_time = (time_t)RFIFOL(fd,46); @@ -2167,6 +2153,8 @@ int parse_fromlogin(int fd) { } else if ( !sd->char_slots )/* no value aka 0 in sql */ sd->char_slots = MAX_CHARS;/* cap to maximum */ safestrncpy(sd->birthdate, (const char*)RFIFOP(fd,52), sizeof(sd->birthdate)); + safestrncpy(sd->pincode, (const char*)RFIFOP(fd,63), sizeof(sd->pincode)); + sd->pincode_change = RFIFOL(fd,68); ARR_FIND( 0, ARRAYLENGTH(server), server_id, server[server_id].fd > 0 && server[server_id].map[0] ); // continued from char_auth_ok... if( server_id == ARRAYLENGTH(server) || //server not online, bugreport:2359 @@ -2180,19 +2168,12 @@ int parse_fromlogin(int fd) { } else { // send characters to player mmo_char_send006b(i, sd); -#if PACKETVER >= 20110309 - // PIN code system, disabled - WFIFOHEAD(i, 12); - WFIFOW(i, 0) = 0x08B9; - WFIFOW(i, 2) = 0; - WFIFOW(i, 4) = 0; - WFIFOL(i, 6) = sd->account_id; - WFIFOW(i, 10) = 0; - WFIFOSET(i, 12); +#if PACKETVER >= 20110309 + pincode->handle(i, sd); #endif } } - RFIFOSKIP(fd,63); + RFIFOSKIP(fd,72); break; // login-server alive packet @@ -4190,6 +4171,50 @@ int parse_char(int fd) } return 0; // avoid processing of followup packets here + // checks the entered pin + case 0x8b8: + if( RFIFOREST(fd) < 10 ) + return 0; + + if( RFIFOL(fd,2) == sd->account_id ) + pincode->check( fd, sd ); + + RFIFOSKIP(fd,10); + break; + + // request for PIN window + case 0x8c5: + if( RFIFOREST(fd) < 6 ) + return 0; + + if( RFIFOL(fd,2) == sd->account_id ) + pincode->state( fd, sd, PINCODE_NOTSET ); + + RFIFOSKIP(fd,6); + break; + + // pincode change request + case 0x8be: + if( RFIFOREST(fd) < 14 ) + return 0; + + if( RFIFOL(fd,2) == sd->account_id ) + pincode->change( fd, sd ); + + RFIFOSKIP(fd,14); + break; + + // activate PIN system and set first PIN + case 0x8ba: + if( RFIFOREST(fd) < 10 ) + return 0; + + if( RFIFOL(fd,2) == sd->account_id ) + pincode->new( fd, sd ); + + RFIFOSKIP(fd,10); + break; + // unknown packet received default: ShowError("parse_char: Received unknown packet "CL_WHITE"0x%x"CL_RESET" from ip '"CL_WHITE"%s"CL_RESET"'! Disconnecting!\n", RFIFOW(fd,0), ip2str(ipl, NULL)); @@ -4544,7 +4569,17 @@ void sql_config_read(const char* cfgName) fclose(fp); ShowInfo("Done reading %s.\n", cfgName); } - +void char_config_dispatch(char *w1, char *w2) { + bool (*dispatch_to[]) (char *w1, char *w2) = { + /* as many as it needs */ + pincode->config_read + }; + int i, len = ARRAYLENGTH(dispatch_to); + for(i = 0; i < len; i++) { + if( (*dispatch_to[i])(w1,w2) ) + break;/* we found who this belongs to, can stop */ + } +} int char_config_read(const char* cfgName) { char line[1024], w1[1024], w2[1024]; @@ -4695,7 +4730,8 @@ int char_config_read(const char* cfgName) guild_exp_rate = atoi(w2); } else if (strcmpi(w1, "import") == 0) { char_config_read(w2); - } + } else + char_config_dispatch(w1,w2); } fclose(fp); @@ -4774,6 +4810,8 @@ int do_init(int argc, char **argv) mapindex_init(); start_point.map = mapindex_name2id("new_zone01"); + pincode_defaults(); + char_config_read((argc < 2) ? CHAR_CONF_NAME : argv[1]); char_lan_config_read((argc > 3) ? argv[3] : LAN_CONF_NAME); sql_config_read(SQL_CONF_NAME); @@ -4783,7 +4821,7 @@ int do_init(int argc, char **argv) ShowNotice("Please edit your 'login' table to create a proper inter-server user/password (gender 'S')\n"); ShowNotice("And then change the user/password to use in conf/char_athena.conf (or conf/import/char_conf.txt)\n"); } - + inter_init_sql((argc > 2) ? argv[2] : inter_cfgName); // inter server configuration auth_db = idb_alloc(DB_OPT_RELEASE_DATA); @@ -4791,8 +4829,7 @@ int do_init(int argc, char **argv) mmo_char_sql_init(); char_read_fame_list(); //Read fame lists. - if ((naddr_ != 0) && (!login_ip || !char_ip)) - { + if ((naddr_ != 0) && (!login_ip || !char_ip)) { char ip_str[16]; ip2str(addr_[0], ip_str); @@ -4824,8 +4861,7 @@ int do_init(int argc, char **argv) add_timer_func_list(online_data_cleanup, "online_data_cleanup"); add_timer_interval(gettick() + 1000, online_data_cleanup, 0, 0, 600 * 1000); - if( console ) - { + if( console ) { //##TODO invoke a CONSOLE_START plugin event } diff --git a/src/char/char.h b/src/char/char.h index e16350cb3..d75dc21f9 100644 --- a/src/char/char.h +++ b/src/char/char.h @@ -7,8 +7,7 @@ #include "../config/core.h" #include "../common/core.h" // CORE_ST_LAST -enum E_CHARSERVER_ST -{ +enum E_CHARSERVER_ST { CHARSERVER_ST_RUNNING = CORE_ST_LAST, CHARSERVER_ST_SHUTDOWN, CHARSERVER_ST_LAST @@ -16,6 +15,24 @@ enum E_CHARSERVER_ST struct mmo_charstatus; +struct char_session_data { + bool auth; // whether the session is authed or not + int account_id, login_id1, login_id2, sex; + int found_char[MAX_CHARS]; // ids of chars on this account + char email[40]; // e-mail (default: a@a.com) by [Yor] + time_t expiration_time; // # of seconds 1/1/1970 (timestamp): Validity limit of the account (0 = unlimited) + int group_id; // permission + uint8 char_slots; + uint32 version; + uint8 clienttype; + char pincode[4+1]; + uint16 pincode_seed; + uint16 pincode_try; + uint32 pincode_change; + char new_name[NAME_LENGTH]; + char birthdate[10+1]; // YYYY-MM-DD +}; + #define MAX_MAP_SERVERS 30 #define DEFAULT_AUTOSAVE_INTERVAL 300*1000 @@ -39,7 +56,7 @@ int char_family(int pl1,int pl2,int pl3); int request_accreg2(int account_id, int char_id); int save_accreg2(unsigned char* buf, int len); - +int login_fd; extern int char_name_option; extern char char_name_letters[]; extern bool char_gm_read; diff --git a/src/char/pincode.c b/src/char/pincode.c new file mode 100644 index 000000000..610f9b12f --- /dev/null +++ b/src/char/pincode.c @@ -0,0 +1,191 @@ +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file + +#include "../common/cbasetypes.h" +#include "../common/mmo.h" +#include "../common/random.h" +#include "../common/showmsg.h" +#include "../common/socket.h" +#include "../common/strlib.h" +#include "char.h" +#include "pincode.h" + +#include + +int enabled = PINCODE_OK; +int changetime = 0; +int maxtry = 3; +unsigned long multiplier = 0x3498, baseSeed = 0x881234; + +void pincode_handle ( int fd, struct char_session_data* sd ) { + if( pincode->enabled ){ + // PIN code system enabled + if( sd->pincode[0] == '\0' ){ + // No PIN code has been set yet + pincode->state( fd, sd, PINCODE_NOTSET ); + } else { + if( !pincode->changetime || !( time(NULL) > (sd->pincode_change+*pincode->changetime) ) ){ + // Ask user for his PIN code + pincode->state( fd, sd, PINCODE_ASK ); + } else { + // User hasnt changed his PIN code too long + pincode->state( fd, sd, PINCODE_EXPIRED ); + } + } + } else { + // PIN code system, disabled + pincode->state( fd, sd, PINCODE_OK ); + } +} + +void pincode_check(int fd, struct char_session_data* sd) { + char pin[5]; + + safestrncpy((char*)pin, (char*)RFIFOP(fd, 6), 4+1); + + pincode->decrypt(sd->pincode_seed, pin); + + if( pincode->compare( fd, sd, pin ) ){ + pincode->state( fd, sd, PINCODE_OK ); + } +} + +int pincode_compare(int fd, struct char_session_data* sd, char* pin) { + if( strcmp( sd->pincode, pin ) == 0 ){ + sd->pincode_try = 0; + return 1; + } else { + pincode->state( fd, sd, PINCODE_WRONG ); + + if( pincode->maxtry && ++sd->pincode_try >= *pincode->maxtry ){ + pincode->error( sd->account_id ); + } + + return 0; + } +} + +void pincode_change(int fd, struct char_session_data* sd) { + char oldpin[5], newpin[5]; + + safestrncpy(oldpin, (char*)RFIFOP(fd,6), 4+1); + pincode->decrypt(sd->pincode_seed,oldpin); + if( !pincode->compare( fd, sd, oldpin ) ) + return; + + safestrncpy(newpin, (char*)RFIFOP(fd,10), 4+1); + pincode->decrypt(sd->pincode_seed,newpin); + pincode->update( sd->account_id, newpin ); + + pincode->state( fd, sd, PINCODE_OK ); +} + +void pincode_setnew(int fd, struct char_session_data* sd) { + char newpin[5]; + + safestrncpy(newpin, (char*)RFIFOP(fd,6), 4+1); + pincode->decrypt(sd->pincode_seed,newpin); + + pincode->update( sd->account_id, newpin ); + + pincode->state( fd, sd, PINCODE_OK ); +} + +// 0 = disabled / pin is correct +// 1 = ask for pin - client sends 0x8b8 +// 2 = create new pin - client sends 0x8ba +// 3 = pin must be changed - client 0x8be +// 4 = create new pin ?? - client sends 0x8ba +// 5 = client shows msgstr(1896) +// 6 = client shows msgstr(1897) Unable to use your KSSN number +// 7 = char select window shows a button - client sends 0x8c5 +// 8 = pincode was incorrect +void pincode_sendstate(int fd, struct char_session_data* sd, uint16 state) { + WFIFOHEAD(fd, 12); + WFIFOW(fd, 0) = 0x8b9; + WFIFOL(fd, 2) = sd->pincode_seed = rnd() % 0xFFFF; + WFIFOL(fd, 6) = sd->account_id; + WFIFOW(fd,10) = state; + WFIFOSET(fd,12); +} + +void pincode_notifyLoginPinUpdate(int account_id, char* pin) { + WFIFOHEAD(login_fd,11); + WFIFOW(login_fd,0) = 0x2738; + WFIFOL(login_fd,2) = account_id; + strncpy( (char*)WFIFOP(login_fd,6), pin, 5 ); + WFIFOSET(login_fd,11); +} + +void pincode_notifyLoginPinError(int account_id) { + WFIFOHEAD(login_fd,6); + WFIFOW(login_fd,0) = 0x2739; + WFIFOL(login_fd,2) = account_id; + WFIFOSET(login_fd,6); +} + +void pincode_decrypt(unsigned long userSeed, char* pin) { + int i, pos; + char tab[10] = {0,1,2,3,4,5,6,7,8,9}; + + for( i = 1; i < 10; i++ ){ + userSeed = *pincode->baseSeed + userSeed * *pincode->multiplier; + pos = userSeed % (i + 1); + if( i != pos ){ + tab[i] ^= tab[pos]; + tab[pos] ^= tab[i]; + tab[i] ^= tab[pos]; + } + } + + for( i = 0; i < 4; i++ ){ + pin[i] = tab[pin[i]- '0']; + } + + sprintf(pin, "%d%d%d%d", pin[0], pin[1], pin[2], pin[3]); +} + +bool pincode_config_read(char *w1, char *w2) { + + while ( true ) { + + if ( strcmpi(w1, "pincode_enabled") == 0 ) { + enabled = atoi(w2); + } else if ( strcmpi(w1, "pincode_changetime") == 0 ) { + changetime = atoi(w2)*60; + } else if ( strcmpi(w1, "pincode_maxtry") == 0 ) { + maxtry = atoi(w2); + if( maxtry > 3 ) { + ShowWarning("pincode_maxtry is too high (%d); maximum allowed: 3! capping to 3...\n",maxtry); + maxtry = 3; + } + } else + return false; + + break; + } + + return true; +} + +void pincode_defaults(void) { + pincode = &pincode_s; + + pincode->enabled = &enabled; + pincode->changetime = &changetime; + pincode->maxtry = &maxtry; + pincode->multiplier = &multiplier; + pincode->baseSeed = &baseSeed; + + pincode->handle = pincode_handle; + pincode->decrypt = pincode_decrypt; + pincode->error = pincode_notifyLoginPinError; + pincode->update = pincode_notifyLoginPinUpdate; + pincode->state = pincode_sendstate; + pincode->new = pincode_setnew; + pincode->change = pincode_change; + pincode->compare = pincode_compare; + pincode->check = pincode_check; + pincode->config_read = pincode_config_read; + +} \ No newline at end of file diff --git a/src/char/pincode.h b/src/char/pincode.h new file mode 100644 index 000000000..209817c78 --- /dev/null +++ b/src/char/pincode.h @@ -0,0 +1,41 @@ +// Copyright (c) Hercules Dev Team, licensed under GNU GPL. +// See the LICENSE file + +#ifndef _PINCODE_H_ +#define _PINCODE_H_ + +#include "char.h" + +#define PINCODE_OK 0 +#define PINCODE_ASK 1 +#define PINCODE_NOTSET 2 +#define PINCODE_EXPIRED 3 +#define PINCODE_UNUSED 7 +#define PINCODE_WRONG 8 + +/* Pincode Interface */ +struct pincode_interface { + /* vars */ + int *enabled; + int *changetime; + int *maxtry; + unsigned long *multiplier; + unsigned long *baseSeed; + /* handler */ + void (*handle) (int fd, struct char_session_data* sd); + void (*decrypt) (unsigned long userSeed, char* pin); + void (*error) (int account_id); + void (*update) (int account_id, char* pin); + void (*state) (int fd, struct char_session_data* sd, uint16 state); + void (*new) (int fd, struct char_session_data* sd); + void (*change) (int fd, struct char_session_data* sd); + int (*compare) (int fd, struct char_session_data* sd, char* pin); + void (*check) (int fd, struct char_session_data* sd); + bool (*config_read) (char *w1, char *w2); +} pincode_s; + +struct pincode_interface *pincode; + +void pincode_defaults(void); + +#endif /* _PINCODE_H_ */ \ No newline at end of file diff --git a/src/login/account.h b/src/login/account.h index 56708d5e9..be5bad988 100644 --- a/src/login/account.h +++ b/src/login/account.h @@ -38,18 +38,20 @@ struct mmo_account { int account_id; char userid[NAME_LENGTH]; - char pass[32+1]; // 23+1 for plaintext, 32+1 for md5-ed passwords - char sex; // gender (M/F/S) - char email[40]; // e-mail (by default: a@a.com) - int group_id; // player group id - uint8 char_slots; // this accounts maximum character slots (maximum is limited to MAX_CHARS define in char server) - unsigned int state; // packet 0x006a value + 1 (0: compte OK) - time_t unban_time; // (timestamp): ban time limit of the account (0 = no ban) - time_t expiration_time; // (timestamp): validity limit of the account (0 = unlimited) - unsigned int logincount;// number of successful auth attempts - char lastlogin[24]; // date+time of last successful login - char last_ip[16]; // save of last IP of connection - char birthdate[10+1]; // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00) + char pass[32+1]; // 23+1 for plaintext, 32+1 for md5-ed passwords + char sex; // gender (M/F/S) + char email[40]; // e-mail (by default: a@a.com) + int group_id; // player group id + uint8 char_slots; // this accounts maximum character slots (maximum is limited to MAX_CHARS define in char server) + unsigned int state; // packet 0x006a value + 1 (0: compte OK) + time_t unban_time; // (timestamp): ban time limit of the account (0 = no ban) + time_t expiration_time; // (timestamp): validity limit of the account (0 = unlimited) + unsigned int logincount; // number of successful auth attempts + unsigned int pincode_change;// (timestamp): last time of pincode change + char pincode[4+1]; // pincode value + char lastlogin[24]; // date+time of last successful login + char last_ip[16]; // save of last IP of connection + char birthdate[10+1]; // assigned birth date (format: YYYY-MM-DD, default: 0000-00-00) int account_reg2_num; struct global_reg account_reg2[ACCOUNT_REG2_NUM]; // account script variables (stored on login server) }; diff --git a/src/login/account_sql.c b/src/login/account_sql.c index f8c39efc9..6f21cbb6c 100644 --- a/src/login/account_sql.c +++ b/src/login/account_sql.c @@ -522,7 +522,7 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc // retrieve login entry for the specified account if( SQL_ERROR == Sql_Query(sql_handle, - "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots` FROM `%s` WHERE `account_id` = %d", + "SELECT `account_id`,`userid`,`user_pass`,`sex`,`email`,`group_id`,`state`,`unban_time`,`expiration_time`,`logincount`,`lastlogin`,`last_ip`,`birthdate`,`character_slots`,`pincode`,`pincode_change` FROM `%s` WHERE `account_id` = %d", db->account_db, account_id ) ) { Sql_ShowDebug(sql_handle); @@ -549,7 +549,9 @@ static bool mmo_auth_fromsql(AccountDB_SQL* db, struct mmo_account* acc, int acc Sql_GetData(sql_handle, 11, &data, NULL); safestrncpy(acc->last_ip, data, sizeof(acc->last_ip)); Sql_GetData(sql_handle, 12, &data, NULL); safestrncpy(acc->birthdate, data, sizeof(acc->birthdate)); Sql_GetData(sql_handle, 13, &data, NULL); acc->char_slots = atoi(data); - + Sql_GetData(sql_handle, 14, &data, NULL); safestrncpy(acc->pincode, data, sizeof(acc->pincode)); + Sql_GetData(sql_handle, 15, &data, NULL); acc->pincode_change = atol(data); + Sql_FreeResult(sql_handle); @@ -597,7 +599,7 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo if( is_new ) {// insert into account table if( SQL_SUCCESS != SqlStmt_Prepare(stmt, - "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO `%s` (`account_id`, `userid`, `user_pass`, `sex`, `email`, `group_id`, `state`, `unban_time`, `expiration_time`, `logincount`, `lastlogin`, `last_ip`, `birthdate`, `character_slots`, `pincode`, `pincode_change`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", db->account_db) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_INT, (void*)&acc->account_id, sizeof(acc->account_id)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid)) @@ -613,6 +615,8 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo || SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots)) + || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_STRING, (void*)&acc->pincode, sizeof(acc->pincode)) + || SQL_SUCCESS != SqlStmt_BindParam(stmt, 15, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { SqlStmt_ShowDebug(stmt); @@ -621,7 +625,7 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo } else {// update account table - if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id) + if( SQL_SUCCESS != SqlStmt_Prepare(stmt, "UPDATE `%s` SET `userid`=?,`user_pass`=?,`sex`=?,`email`=?,`group_id`=?,`state`=?,`unban_time`=?,`expiration_time`=?,`logincount`=?,`lastlogin`=?,`last_ip`=?,`birthdate`=?,`character_slots`=?,`pincode`=?,`pincode_change`=? WHERE `account_id` = '%d'", db->account_db, acc->account_id) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 0, SQLDT_STRING, (void*)acc->userid, strlen(acc->userid)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 1, SQLDT_STRING, (void*)acc->pass, strlen(acc->pass)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 2, SQLDT_ENUM, (void*)&acc->sex, sizeof(acc->sex)) @@ -635,6 +639,8 @@ static bool mmo_auth_tosql(AccountDB_SQL* db, const struct mmo_account* acc, boo || SQL_SUCCESS != SqlStmt_BindParam(stmt, 10, SQLDT_STRING, (void*)&acc->last_ip, strlen(acc->last_ip)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 11, SQLDT_STRING, (void*)&acc->birthdate, strlen(acc->birthdate)) || SQL_SUCCESS != SqlStmt_BindParam(stmt, 12, SQLDT_UCHAR, (void*)&acc->char_slots, sizeof(acc->char_slots)) + || SQL_SUCCESS != SqlStmt_BindParam(stmt, 13, SQLDT_STRING, (void*)&acc->pincode, strlen(acc->pincode)) + || SQL_SUCCESS != SqlStmt_BindParam(stmt, 14, SQLDT_LONG, (void*)&acc->pincode_change, sizeof(acc->pincode_change)) || SQL_SUCCESS != SqlStmt_Execute(stmt) ) { SqlStmt_ShowDebug(stmt); diff --git a/src/login/login.c b/src/login/login.c index e174672cc..c16cce342 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -559,6 +559,7 @@ int parse_fromchar(int fd) int group_id = 0; uint8 char_slots = 0; char birthdate[10+1] = ""; + char pincode[4+1] = "\0\0\0\0\0"; int account_id = RFIFOL(fd,2); RFIFOSKIP(fd,6); @@ -570,10 +571,13 @@ int parse_fromchar(int fd) expiration_time = acc.expiration_time; group_id = acc.group_id; char_slots = acc.char_slots; + safestrncpy(pincode, acc.pincode, sizeof(pincode)); safestrncpy(birthdate, acc.birthdate, sizeof(birthdate)); + if( strlen(pincode) == 0 ) + memset(pincode,'\0',sizeof(pincode)); } - WFIFOHEAD(fd,63); + WFIFOHEAD(fd,72); WFIFOW(fd,0) = 0x2717; WFIFOL(fd,2) = account_id; safestrncpy((char*)WFIFOP(fd,6), email, 40); @@ -581,7 +585,9 @@ int parse_fromchar(int fd) WFIFOB(fd,50) = (unsigned char)group_id; WFIFOB(fd,51) = char_slots; safestrncpy((char*)WFIFOP(fd,52), birthdate, 10+1); - WFIFOSET(fd,63); + safestrncpy((char*)WFIFOP(fd,63), pincode, 4+1 ); + WFIFOL(fd,68) = acc.pincode_change; + WFIFOSET(fd,72); } break; @@ -907,6 +913,41 @@ int parse_fromchar(int fd) RFIFOSKIP(fd,2); break; + case 0x2738: //Change PIN Code for a account + if( RFIFOREST(fd) < 11 ) + return 0; + else { + struct mmo_account acc; + + if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ) { + strncpy( acc.pincode, (char*)RFIFOP(fd,6), 5 ); + acc.pincode_change = time( NULL ); + accounts->save(accounts, &acc); + } + RFIFOSKIP(fd,11); + } + break; + + case 0x2739: // PIN Code was entered wrong too often + if( RFIFOREST(fd) < 6 ) + return 0; + else { + struct mmo_account acc; + + if( accounts->load_num(accounts, &acc, RFIFOL(fd,2) ) ) { + struct online_login_data* ld; + + if( ( ld = (struct online_login_data*)idb_get(online_db,acc.account_id) ) == NULL ) + return 0; + + login_log( host2ip(acc.last_ip), acc.userid, 100, "PIN Code check failed" ); + } + + remove_online_user(acc.account_id); + RFIFOSKIP(fd,6); + } + break; + default: ShowError("parse_fromchar: Unknown packet 0x%x from a char-server! Disconnecting!\n", command); set_eof(fd); @@ -958,6 +999,8 @@ int mmo_auth_new(const char* userid, const char* pass, const char sex, const cha safestrncpy(acc.lastlogin, "0000-00-00 00:00:00", sizeof(acc.lastlogin)); safestrncpy(acc.last_ip, last_ip, sizeof(acc.last_ip)); safestrncpy(acc.birthdate, "0000-00-00", sizeof(acc.birthdate)); + safestrncpy(acc.pincode, "\0", sizeof(acc.pincode)); + acc.pincode_change = 0; acc.char_slots = 0; if( !accounts->create(accounts, &acc) ) -- cgit v1.2.3-70-g09d2