From e0eb6d1e455dd5cb9cfe1f657b22953b14697fc1 Mon Sep 17 00:00:00 2001 From: codemaster Date: Sat, 13 Nov 2004 17:36:40 +0000 Subject: Updated and optimized with FREYA's additions. git-svn-id: https://rathena.svn.sourceforge.net/svnroot/rathena/athena@159 54d463be-8e91-2dee-dedb-b68131a5f0ec --- src/login/login.c | 470 +++++++++++++++++++++++++++++++++--------------------- src/login/login.h | 7 +- 2 files changed, 292 insertions(+), 185 deletions(-) diff --git a/src/login/login.c b/src/login/login.c index e51024d9b..912b209ee 100644 --- a/src/login/login.c +++ b/src/login/login.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "../common/core.h" #include "../common/socket.h" @@ -25,6 +26,7 @@ #include "../common/version.h" #include "../common/db.h" #include "../common/lock.h" +#include "../common/malloc.h" #ifdef PASSWORDENC #include "md5calc.h" @@ -45,6 +47,7 @@ int subnetmaski[4]; char account_filename[1024] = "save/account.txt"; char GM_account_filename[1024] = "conf/GM_account.txt"; char login_log_filename[1024] = "log/login.log"; +FILE *log_fp = NULL; char login_log_unknown_packets_filename[1024] = "log/login_unknown_packets.log"; char date_format[32] = "%Y-%m-%d %H:%M:%S"; int save_unknown_packets = 0; @@ -105,7 +108,7 @@ int auth_fifo_pos = 0; struct auth_dat { int account_id, sex; - char userid[24], pass[24], lastlogin[24]; + char userid[24], pass[33], lastlogin[24]; // 33 for 32 + NULL terminated int logincount; int state; // packet 0x006a value + 1 (0: compte OK) char email[40]; // e-mail (by default: a@a.com) @@ -116,7 +119,7 @@ struct auth_dat { char memo[255]; // a memo field int account_reg2_num; struct global_reg account_reg2[ACCOUNT_REG2_NUM]; -} *auth_dat; +} *auth_dat = NULL; int auth_num = 0, auth_max = 0; @@ -131,10 +134,19 @@ int auth_before_save_file = 0; // Counter. First save when 1st char-server do co int admin_state = 0; char admin_pass[24] = ""; +int GM_num; +int GM_max=256; char gm_pass[64] = ""; int level_new_gm = 60; -static struct dbt *gm_account_db; +struct gm_account *gm_account_db; + +int dynamic_pass_failure_ban = 1; +int dynamic_pass_failure_ban_time = 5; +int dynamic_pass_failure_ban_how_many = 3; +int dynamic_pass_failure_ban_how_long = 1; + +int use_md5_passwds = 0; int console = 0; @@ -142,27 +154,28 @@ int console = 0; // Writing function of logs file //------------------------------ int login_log(char *fmt, ...) { - FILE *logfp; va_list ap; struct timeval tv; char tmpstr[2048]; - va_start(ap, fmt); + if(!log_fp) + log_fp = fopen(login_log_filename, "a"); - logfp = fopen(login_log_filename, "a"); - if (logfp) { + log_fp = fopen(login_log_filename, "a"); + if (log_fp) { if (fmt[0] == '\0') // jump a line if no message - fprintf(logfp, RETCODE); + fprintf(log_fp, RETCODE); else { + va_start(ap, fmt); gettimeofday(&tv, NULL); strftime(tmpstr, 24, date_format, localtime(&(tv.tv_sec))); sprintf(tmpstr + strlen(tmpstr), ".%03d: %s", (int)tv.tv_usec / 1000, fmt); - vfprintf(logfp, tmpstr, ap); + vfprintf(log_fp, tmpstr, ap); + va_end(ap); } - fclose(logfp); + fflush(log_fp); // under cygwin or windows, if software is stopped, data are not written in the file -> fflush at every line } - va_end(ap); return 0; } @@ -171,12 +184,11 @@ int login_log(char *fmt, ...) { // and returns its level (or 0 if it isn't a GM account or if not found) //---------------------------------------------------------------------- int isGM(int account_id) { - struct gm_account *p; - - p = numdb_search(gm_account_db, account_id); - if (p == NULL) - return 0; - return p->level; + int i; + for(i=0; i < GM_num; i++) + if(gm_account_db[i].account_id == account_id) + return gm_account_db[i].level; + return 0; } //------------------------------------------------------- @@ -184,14 +196,15 @@ int isGM(int account_id) { //------------------------------------------------------- int read_gm_account() { char line[512]; - struct gm_account *p; FILE *fp; - int c = 0; - int GM_level; + int account_id, level; + int i, line_counter; struct stat file_stat; - free(gm_account_db); - gm_account_db = numdb_init(); + if(gm_account_db) free(gm_account_db); + GM_num = 0; + if(GM_max < 0) GM_max = 256; + gm_account_db = (struct gm_account*)aCalloc(GM_max, sizeof(struct gm_account)); // get last modify time/date if (stat(GM_account_filename, &file_stat)) @@ -206,48 +219,54 @@ int read_gm_account() { login_log(" Actually, there is no GM accounts on the server." RETCODE); return 1; } + + line_counter = 0; // limited to 4000, because we send information to char-servers (more than 4000 GM accounts???) // int (id) + int (level) = 8 bytes * 4000 = 32k (limit of packets in windows) - while(fgets(line, sizeof(line)-1, fp) && c < 4000) { + while(fgets(line, sizeof(line)-1, fp) && GM_num < 4000) { + line_counter++; if ((line[0] == '/' && line[1] == '/') || line[0] == '\0' || line[0] == '\n' || line[0] == '\r') continue; - p = calloc(sizeof(struct gm_account), 1); - if (p == NULL) { - printf("read_gm_account: memory allocation failure (malloc)!\n"); - exit(0); - } - if (sscanf(line, "%d %d", &p->account_id, &p->level) != 2 && sscanf(line, "%d: %d", &p->account_id, &p->level) != 2) - printf("read_gm_account: file [%s], invalid 'id_acount level' format.\n", GM_account_filename); - else if (p->level <= 0) - printf("read_gm_account: file [%s] %dth account (invalid level [0 or negative]: %d).\n", GM_account_filename, c+1, p->level); + if (sscanf(line, "%d %d", &account_id, &level) != 2 && sscanf(line, "%d: %d", &account_id, &level) != 2) + printf("read_gm_account: file [%s], invalid 'id_acount level' format (line #%d).\n", GM_account_filename, line_counter); + else if (level <= 0) + printf("read_gm_account: file [%s] %dth account (line #%d) (invalid level [0 or negative]: %d).\n", GM_account_filename, GM_num+1, line_counter, level); else { - if (p->level > 99) { - printf("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, c+1, p->level); - p->level = 99; - } - if ((GM_level = isGM(p->account_id)) > 0) { // if it's not a new account - if (GM_level == p->level) - printf("read_gm_account: GM account %d defined twice (same level: %d).\n", p->account_id, p->level); - else - printf("read_gm_account: GM account %d defined twice (levels: %d and %d).\n", p->account_id, GM_level, p->level); - } - if (GM_level != p->level) { // if new account or new level - numdb_insert(gm_account_db, p->account_id, p); - //printf("GM account:%d, level: %d->%d\n", p->account_id, GM_level, p->level); - if (GM_level == 0) { // if new account - c++; - if (c >= 4000) { - printf("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); - login_log("***WARNING: 4000 GM accounts found. Next GM accounts are not readed." RETCODE); + if (level > 99) { + printf("read_gm_account: file [%s] %dth account (invalid level, but corrected: %d->99).\n", GM_account_filename, GM_num+1, level); + level = 99; + } + for(i = 0; i < GM_num; i++) + if (gm_account_db[i].account_id == account_id) { + if (gm_account_db[i].level == level) + printf("read_gm_account: GM account %d defined twice (same level: %d).\n", account_id, level); + else { + printf("read_gm_account: GM account %d defined twice (levels: %d and %d).\n", account_id, gm_account_db[i].level, level); + gm_account_db[i].level = level; } + break; + } + // if new account + if (i == GM_num) { + if (GM_num >= GM_max) { + GM_max += 256; + gm_account_db = realloc(gm_account_db, sizeof(struct gm_account) * GM_max); + memset(gm_account_db + (GM_max - 256), 0, sizeof(struct gm_account) * 256); + } + gm_account_db[GM_num].account_id = account_id; + gm_account_db[GM_num].level = level; + GM_num++; + if (GM_num >= 4000) { + printf("***WARNING: 4000 GM accounts found. Next GM accounts are not readed.\n"); + login_log("***WARNING: 4000 GM accounts found. Next GM accounts are not readed." RETCODE); } } } } fclose(fp); - printf("read_gm_account: file '%s' readed (%d GM accounts found).\n", GM_account_filename, c); - login_log("read_gm_account: file '%s' readed (%d GM accounts found)." RETCODE, GM_account_filename, c); + printf("read_gm_account: file '%s' readed (%d GM accounts found).\n", GM_account_filename, GM_num); + login_log("read_gm_account: file '%s' readed (%d GM accounts found)." RETCODE, GM_account_filename, GM_num); return 0; } @@ -286,7 +305,8 @@ int check_ipmask(unsigned int ip, const unsigned char *str) { int check_ip(unsigned int ip) { int i; unsigned char *p = (unsigned char *)&ip; - char buf[32]; + char buf[16]; + char * access_ip; enum { ACF_DEF, ACF_ALLOW, ACF_DENY } flag = ACF_DEF; if (access_allownum == 0 && access_denynum == 0) @@ -304,21 +324,20 @@ int check_ip(unsigned int ip) { sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); for(i = 0; i < access_allownum; i++) { - const char *p = access_allow + i * ACO_STRSIZE; - if (memcmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) { - flag = ACF_ALLOW; - if (access_order == ACO_ALLOW_DENY) + access_ip = access_allow + i * ACO_STRSIZE; + if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, access_ip)) { + if(access_order == ACO_ALLOW_DENY) return 1; // With 'allow, deny' (deny if not allow), allow has priority + flag = ACF_ALLOW; break; } } for(i = 0; i < access_denynum; i++) { - const char *p = access_deny + i * ACO_STRSIZE; - if (memcmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) { - flag = ACF_DENY; + access_ip = access_deny + i * ACO_STRSIZE; + if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, access_ip)) { + //flag = ACF_DENY; // not necessary to define flag return 0; // At this point, if it's 'deny', we refuse connection. - break; } } @@ -335,7 +354,8 @@ int check_ip(unsigned int ip) { int check_ladminip(unsigned int ip) { int i; unsigned char *p = (unsigned char *)&ip; - char buf[32]; + char buf[16]; + char * access_ip; if (access_ladmin_allownum == 0) return 1; // When there is no restriction, all IP are authorised. @@ -352,8 +372,8 @@ int check_ladminip(unsigned int ip) { sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]); for(i = 0; i < access_ladmin_allownum; i++) { - const char *p = access_ladmin_allow + i * ACO_STRSIZE; - if (memcmp(p, buf, strlen(p)) == 0 || check_ipmask(ip, p)) { + access_ip = access_ladmin_allow + i * ACO_STRSIZE; + if (memcmp(access_ip, buf, strlen(access_ip)) == 0 || check_ipmask(ip, access_ip)) { return 1; } } @@ -402,12 +422,9 @@ int e_mail_check(unsigned char *email) { strstr(last_arobas, "..") != NULL) return 0; - for(ch = 1; ch < 32; ch++) { - if (strchr(last_arobas, ch) != NULL) { + for(ch = 1; ch < 32; ch++) + if (strchr(last_arobas, ch) != NULL) return 0; - break; - } - } if (strchr(last_arobas, ' ') != NULL || strchr(last_arobas, ';') != NULL) @@ -484,11 +501,10 @@ int mmo_auth_init(void) { int GM_count = 0; int server_count = 0; - auth_dat = calloc(sizeof(struct auth_dat) * 256, 1); auth_max = 256; + auth_dat = (struct auth_dat*)aCalloc(auth_max, sizeof(struct auth_dat)); - fp = fopen(account_filename, "r"); - if (fp == NULL) { + if ((fp = fopen(account_filename, "r")) == NULL) { // no account file -> no account -> no login, including char-server (ERROR) printf("\033[1;31mmmo_auth_init: Accounts file [%s] not found.\033[0m\n", account_filename); return 0; @@ -498,8 +514,19 @@ int mmo_auth_init(void) { if (line[0] == '/' && line[1] == '/') continue; line[sizeof(line)-1] = '\0'; + // remove carriage return if exist + while(line[0] != '\0' && (line[strlen(line)-1] == '\n' || line[strlen(line)-1] == '\r')) + line[strlen(line)-1] = '\0'; p = line; + memset(userid, 0, sizeof(userid)); + memset(pass, 0, sizeof(pass)); + memset(lastlogin, 0, sizeof(lastlogin)); + memset(email, 0, sizeof(email)); + memset(error_message, 0, sizeof(error_message)); + memset(last_ip, 0, sizeof(last_ip)); + memset(memo, 0, sizeof(memo)); + // database version reading (v2) if (((i = sscanf(line, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%c\t%d\t%d\t" "%[^\t]\t%[^\t]\t%ld\t%[^\t]\t%[^\t]\t%ld%n", @@ -795,14 +822,16 @@ int mmo_auth_init(void) { void mmo_auth_sync(void) { FILE *fp; int i, j, k, lock; + int account_id; int id[auth_num]; char line[65536]; // Sorting before save for(i = 0; i < auth_num; i++) { id[i] = i; + account_id = auth_dat[i].account_id; for(j = 0; j < i; j++) { - if (auth_dat[i].account_id < auth_dat[id[j]].account_id) { + if (account_id < auth_dat[id[j]].account_id) { for(k = i; k > j; k--) id[k] = id[k-1]; id[j] = i; // id[i] @@ -812,9 +841,9 @@ void mmo_auth_sync(void) { } // Data save - fp = lock_fopen(account_filename, &lock); - if (fp == NULL) + if ((fp = lock_fopen(account_filename, &lock)) == NULL) return; + fprintf(fp, "// Accounts file: here are saved all information about the accounts.\n"); fprintf(fp, "// Structure: ID, account name, password, last login time, sex, # of logins, state, email, error message for state 7, validity time, last (accepted) login ip, memo field, ban timestamp, repeated(register text, register value)\n"); fprintf(fp, "// Some explanations:\n"); @@ -872,10 +901,9 @@ int check_auth_sync(int tid, unsigned int tick, int id, int data) { // Packet send to all char-servers, except one (wos: without our self) //-------------------------------------------------------------------- int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { - int i, c; + int i, c, fd; for(i = 0, c = 0; i < MAX_SERVERS; i++) { - int fd; if ((fd = server_fd[i]) >= 0 && fd != sfd) { memcpy(WFIFOP(fd,0), buf, len); WFIFOSET(fd, len); @@ -890,17 +918,16 @@ int charif_sendallwos(int sfd, unsigned char *buf, unsigned int len) { //----------------------------------------------------- void send_GM_accounts() { int i; - char buf[32000]; - int GM_value; + char buf[32767]; int len; len = 4; WBUFW(buf,0) = 0x2732; - for(i = 0; i < auth_num; i++) + for(i = 0; i < GM_num; i++) // send only existing accounts. We can not create a GM account when server is online. - if ((GM_value = isGM(auth_dat[i].account_id)) > 0) { - WBUFL(buf,len) = auth_dat[i].account_id; - WBUFB(buf,len+4) = (unsigned char)GM_value; + if (gm_account_db[i].level > 0) { + WBUFL(buf,len) = gm_account_db[i].account_id; + WBUFB(buf,len+4) = (unsigned char)gm_account_db[i].level; len += 5; } WBUFW(buf,2) = len; @@ -945,6 +972,7 @@ int mmo_auth_new(struct mmo_account* account, char sex, char* email) { if (auth_num >= auth_max) { auth_max += 256; auth_dat = realloc(auth_dat, sizeof(struct auth_dat) * auth_max); + memset(auth_dat, 0, sizeof(struct auth_dat) * auth_max); } memset(&auth_dat[i], '\0', sizeof(struct auth_dat)); @@ -957,7 +985,7 @@ int mmo_auth_new(struct mmo_account* account, char sex, char* email) { strncpy(auth_dat[i].userid, account->userid, 24); auth_dat[i].userid[23] = '\0'; - strncpy(auth_dat[i].pass, account->passwd, 24); + strncpy(auth_dat[i].pass, account->passwd, 32); auth_dat[i].pass[23] = '\0'; memcpy(auth_dat[i].lastlogin, "-", 2); @@ -1009,9 +1037,14 @@ int mmo_auth(struct mmo_account* account, int fd) { struct timeval tv; char tmpstr[256]; int len, newaccount = 0; +#ifdef PASSWORDENC + struct login_session_data *ld; +#endif + int encpasswdok; char md5str[64], md5bin[32]; char ip[16]; unsigned char *sin_addr = (unsigned char *)&session[fd]->client_addr.sin_addr; + char user_password[256]; sprintf(ip, "%d.%d.%d.%d", sin_addr[0], sin_addr[1], sin_addr[2], sin_addr[3]); @@ -1040,15 +1073,18 @@ int mmo_auth(struct mmo_account* account, int fd) { } if (i != auth_num) { - int encpasswdok = 0; - struct login_session_data *ld; if (newaccount) { login_log("Attempt of creation of an already existant account (account: %s_%c, pass: %s, received pass: %s, ip: %s)" RETCODE, account->userid, account->userid[len+1], auth_dat[i].pass, account->passwd, ip); return 1; // 1 = Incorrect Password } - ld = session[fd]->session_data; + if(use_md5_passwds) + MD5_String(account->passwd, user_password); + else + memcpy(user_password, account->passwd, 25); + encpasswdok = 0; #ifdef PASSWORDENC + ld = session[fd]->session_data; if (account->passwdenc > 0) { int j = account->passwdenc; if (!ld) { @@ -1059,11 +1095,9 @@ int mmo_auth(struct mmo_account* account, int fd) { j = 1; do { if (j == 1) { - strncpy(md5str, ld->md5key, sizeof(ld->md5key)); // 20 - strcat(md5str, auth_dat[i].pass); // 24 + sprintf(md5str, "%s%s", ld->md5key, auth_dat[i].pass); // 20 + 24 } else if (j == 2) { - strncpy(md5str, auth_dat[i].pass, sizeof(auth_dat[i].pass)); // 24 - strcat(md5str, ld->md5key); // 20 + sprintf(md5str, "%s%s", auth_dat[i].pass, ld->md5key); // 24 + 20 } else md5str[0] = '\0'; md5str[sizeof(md5str)-1] = '\0'; // 64 @@ -1112,10 +1146,8 @@ int mmo_auth(struct mmo_account* account, int fd) { case 9: // 8 = No MSG (actually, all states after 9 except 99 are No MSG, use only this) case 100: // 99 = This ID has been totally erased return auth_dat[i].state - 1; - break; default: return 99; // 99 = ID has been totally erased - break; } } @@ -1189,6 +1221,10 @@ int char_anti_freeze_system(int tid, unsigned int tick, int id, int data) { login_log("Char-server anti-freeze system: char-server #%d '%s' is freezed -> disconnection." RETCODE, i, server[i].name); session[server_fd[i]]->eof = 1; + } else { + // send alive packet to check connection + WFIFOW(server_fd[i],0) = 0x2718; + WFIFOSET(server_fd[i],2); } } } @@ -1203,6 +1239,7 @@ int parse_fromchar(int fd) { int i, j, id; unsigned char *p = (unsigned char *) &session[fd]->client_addr.sin_addr; char ip[16]; + int acc; sprintf(ip, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); @@ -1307,11 +1344,11 @@ int parse_fromchar(int fd) { break; // we receive a e-mail creation of an account with a default e-mail (no answer) - case 0x2715: { - int acc; - char email[40]; + case 0x2715: if (RFIFOREST(fd) < 46) return 0; + { + char email[40]; acc = RFIFOL(fd,2); // speed up memcpy(email, RFIFOP(fd,6), 40); email[39] = '\0'; @@ -1335,11 +1372,11 @@ int parse_fromchar(int fd) { login_log("Char-server '%s': Attempt to create an e-mail on an account with a default e-mail REFUSED - account doesn't exist or e-mail of account isn't default e-mail (account: %d, ip: %s)." RETCODE, server[id].name, acc, ip); } + } RFIFOSKIP(fd,46); break; // We receive an e-mail/limited time request, because a player comes back from a map-server to the char-server - } case 0x2716: if (RFIFOREST(fd) < 6) return 0; @@ -1356,10 +1393,9 @@ int parse_fromchar(int fd) { break; } } - if (i == auth_num) { + if (i == auth_num) login_log("Char-server '%s': e-mail of the account %d NOT found (ip: %s)." RETCODE, server[id].name, RFIFOL(fd,2), ip); - } RFIFOSKIP(fd,6); break; @@ -1367,7 +1403,6 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; { - int acc; unsigned char buf[10]; FILE *fp; acc = RFIFOL(fd,4); @@ -1424,7 +1459,6 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 86) return 0; { - int acc; char actual_email[40], new_email[40]; acc = RFIFOL(fd,2); memcpy(actual_email, RFIFOP(fd,6), 40); @@ -1510,7 +1544,6 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 18) return 0; { - int acc; acc = RFIFOL(fd,2); for(i = 0; i < auth_num; i++) { if (auth_dat[i].account_id == acc) { @@ -1564,10 +1597,9 @@ int parse_fromchar(int fd) { break; } } - if (i == auth_num) { + if (i == auth_num) login_log("Char-server '%s': Error of ban request (account: %d not found, ip: %s)." RETCODE, server[id].name, acc, ip); - } RFIFOSKIP(fd,18); } return 0; @@ -1576,7 +1608,7 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 6) return 0; { - int acc, sex; + int sex; acc = RFIFOL(fd,2); for(i = 0; i < auth_num; i++) { // printf("%d,", auth_dat[i].account_id); @@ -1606,10 +1638,9 @@ int parse_fromchar(int fd) { break; } } - if (i == auth_num) { + if (i == auth_num) login_log("Char-server '%s': Error of sex change (account: %d not found, sex would be reversed, ip: %s)." RETCODE, server[id].name, acc, ip); - } RFIFOSKIP(fd,6); } return 0; @@ -1618,7 +1649,7 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; { - int acc, p; + int p; acc = RFIFOL(fd,4); for(i = 0; i < auth_num; i++) { if (auth_dat[i].account_id == acc) { @@ -1655,7 +1686,6 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 6) return 0; { - int acc; acc = RFIFOL(fd,2); for(i = 0; i < auth_num; i++) { if (auth_dat[i].account_id == acc) { @@ -1670,10 +1700,9 @@ int parse_fromchar(int fd) { break; } } - if (i == auth_num) { + if (i == auth_num) login_log("Char-server '%s': Error of UnBan request (account: %d not found, ip: %s)." RETCODE, server[id].name, acc, ip); - } RFIFOSKIP(fd,6); } return 0; @@ -1681,7 +1710,7 @@ int parse_fromchar(int fd) { if (RFIFOREST(fd) < 4 || RFIFOREST(fd) < RFIFOW(fd,2)) return 0; { - int acc, sex, i = 0; + int sex, i = 0; acc = RFIFOL(fd,4); sex = RFIFOB(fd,8); if (sex != 0 && sex != 1) @@ -1859,7 +1888,8 @@ int parse_admin(int fd) { { struct mmo_account ma; ma.userid = RFIFOP(fd, 2); - ma.passwd = RFIFOP(fd, 26); + memcpy(ma.passwd, RFIFOP(fd, 26), 24); + ma.passwd[24] = '\0'; memcpy(ma.lastlogin, "-", 2); ma.sex = RFIFOB(fd,50); WFIFOW(fd,0) = 0x7931; @@ -2056,16 +2086,16 @@ int parse_admin(int fd) { remove_control_chars(account_name); i = search_account_index(account_name); if (i != -1) { + char pass[25]; memcpy(WFIFOP(fd,6), auth_dat[i].userid, 24); - if (strcmp(auth_dat[i].pass, RFIFOP(fd,26)) == 0) { + memcpy(pass, RFIFOP(fd,26), 24); + pass[24] = '\0'; + remove_control_chars(pass); + if (strcmp(auth_dat[i].pass, pass) == 0) { WFIFOL(fd,2) = auth_dat[i].account_id; login_log("'ladmin': Check of password OK (account: %s, password: %s, ip: %s)" RETCODE, auth_dat[i].userid, auth_dat[i].pass, ip); } else { - char pass[24]; - memcpy(pass, RFIFOP(fd,26), 24); - pass[23] = '\0'; - remove_control_chars(pass); login_log("'ladmin': Failure of password check (account: %s, proposed pass: %s, ip: %s)" RETCODE, auth_dat[i].userid, pass, ip); } @@ -2789,28 +2819,30 @@ int parse_login(int fd) { account.userid = RFIFOP(fd,6); account.userid[23] = '\0'; remove_control_chars(account.userid); - account.passwd = RFIFOP(fd,30); if (RFIFOW(fd,0) == 0x64) { - account.passwd[23] = '\0'; - remove_control_chars(account.passwd); + memcpy(account.passwd, RFIFOP(fd,30), 24); + account.passwd[24] = '\0'; + } else { + memcpy(account.passwd, RFIFOP(fd,30), 32); + account.passwd[32] = '\0'; } + remove_control_chars(account.passwd); #ifdef PASSWORDENC account.passwdenc = (RFIFOW(fd,0) == 0x64) ? 0 : PASSWORDENC; #else account.passwdenc = 0; #endif - if (RFIFOW(fd,0) == 0x64) { + if (RFIFOW(fd,0) == 0x64) login_log("Request for connection (non encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); - } else { + else login_log("Request for connection (encryption mode) of %s (ip: %s)." RETCODE, account.userid, ip); - } if (!check_ip(session[fd]->client_addr.sin_addr.s_addr)) { login_log("Connection refused: IP isn't authorised (deny/allow, ip: %s)." RETCODE, ip); WFIFOW(fd,0) = 0x6a; - WFIFOB(fd,2) = 0x03; - WFIFOSET(fd,3); + WFIFOB(fd,2) = 3; // 3 = Rejected from Server + WFIFOSET(fd,23); RFIFOSKIP(fd,(RFIFOW(fd,0) == 0x64) ? 55 : 47); break; } @@ -2904,23 +2936,21 @@ int parse_login(int fd) { session[fd]->eof = 1; return 0; } - ld = session[fd]->session_data = calloc(sizeof(*ld), 1); + ld = session[fd]->session_data = (struct login_session_data*)aCalloc(1, sizeof(struct login_session_data)); if (!ld) { printf("login: Request for md5 key: memory allocation failure (malloc)!\n"); session[fd]->eof = 1; return 0; } - if (RFIFOW(fd,0) == 0x01db) { + if (RFIFOW(fd,0) == 0x01db) login_log("Sending request of the coding key (ip: %s)" RETCODE, ip); - } else { + else login_log("'ladmin': Sending request of the coding key (ip: %s)" RETCODE, ip); - } // Creation of the coding key memset(ld->md5key, '\0', sizeof(ld->md5key)); ld->md5keylen = rand() % 4 + 12; for(i = 0; i < ld->md5keylen; i++) ld->md5key[i] = rand() % 255 + 1; - RFIFOSKIP(fd,2); WFIFOW(fd,0) = 0x01dc; WFIFOW(fd,2) = 4 + ld->md5keylen; @@ -2938,8 +2968,8 @@ int parse_login(int fd) { account.userid = RFIFOP(fd,2); account.userid[23] = '\0'; remove_control_chars(account.userid); - account.passwd = RFIFOP(fd,26); - account.passwd[23] = '\0'; + memcpy(account.passwd, RFIFOP(fd,26), 24); + account.passwd[24] = '\0'; remove_control_chars(account.passwd); account.passwdenc = 0; server_name = RFIFOP(fd,60); @@ -2980,8 +3010,18 @@ int parse_login(int fd) { WFIFOW(fd,2) = len; WFIFOSET(fd,len); } else { - login_log("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)" RETCODE, - server_name, account.userid, account.passwd, ip); + if (server_fd[account.account_id] != -1) { + printf("Connection of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)\n", + server_name, account.account_id, account.userid, account.passwd, ip); + printf("You must probably wait that the freeze system detect the disconnection.\n"); + login_log("Connexion of the char-server '%s' REFUSED - already connected (account: %ld-%s, pass: %s, ip: %s)" RETCODE, + server_name, account.account_id, account.userid, account.passwd, ip); + login_log("You must probably wait that the freeze system detect the disconnection." RETCODE); + } else { + printf("Connection of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s).\n", server_name, account.userid, account.passwd, ip); + login_log("Connexion of the char-server '%s' REFUSED (account: %s, pass: %s, ip: %s)" RETCODE, + server_name, account.userid, account.passwd, ip); + } WFIFOW(fd,0) = 0x2711; WFIFOB(fd,2) = 3; WFIFOSET(fd,3); @@ -3020,8 +3060,8 @@ int parse_login(int fd) { struct login_session_data *ld = session[fd]->session_data; if (RFIFOW(fd,2) == 0) { // non encrypted password unsigned char* password; - password = RFIFOP(fd,4); - password[23] = '\0'; + memcpy(password, RFIFOP(fd,4), 24); + password[24] = '\0'; remove_control_chars(password); // If remote administration is enabled and password sent by client matches password read from login server configuration file if ((admin_state == 1) && (strcmp(password, admin_pass) == 0)) { @@ -3039,11 +3079,9 @@ int parse_login(int fd) { else { char md5str[64] = "", md5bin[32]; if (RFIFOW(fd,2) == 1) { - strncpy(md5str, ld->md5key, sizeof(ld->md5key)); // 20 - strcat(md5str, admin_pass); // 24 + sprintf(md5str, "%s%s", ld->md5key, admin_pass); // 20 24 } else if (RFIFOW(fd,2) == 2) { - strncpy(md5str, admin_pass, sizeof(admin_pass)); // 24 - strcat(md5str, ld->md5key); // 20 + sprintf(md5str, "%s%s", admin_pass, ld->md5key); // 24 20 } MD5_String2binary(md5str, md5bin); // If remote administration is enabled and password hash sent by client matches hash of password read from login server configuration file @@ -3112,28 +3150,35 @@ int parse_login(int fd) { return 0; } +//----------------------- // Console Command Parser [Wizputer] +//----------------------- int parse_console(char *buf) { - char *type,*command; - - type = (char *)malloc(64); - command = (char *)malloc(64); - - memset(type,0,64); - memset(command,0,64); - - printf("Console: %s\n",buf); - - if ( sscanf(buf, "%[^:]:%[^\n]", type , command ) < 2 ) - sscanf(buf,"%[^\n]",type); - - printf("Type of command: %s || Command: %s \n",type,command); + char command[256]; + + memset(command,0,sizeof(command)); - free(buf); - free(type); - free(command); + sscanf(buf, "%[^\n]", command); - return 0; + login_log("Console command :%s" RETCODE, command); + + if(strcmpi("shutdown", command) == 0 || + strcmpi("exit", command) == 0 || + strcmpi("quit", command) == 0 || + strcmpi("end", command) == 0) + exit(0); + else if(strcmpi("alive", command) == 0 || + strcmpi("status", command) == 0) + printf("\033[0;36mConsole: \033[0m\033[1mI'm Alive.\033[0m\n"); + else if(strcmpi("help", command) == 0) { + printf("\033[32mHelp of commands:\033[0m\n"); + printf(" To shutdown the server:\n"); + printf(" 'shutdown|exit|qui|end'\n"); + printf(" To know if server is alive:\n"); + printf(" 'alive|status'\n"); + } + + return 0; } @@ -3182,12 +3227,14 @@ int login_lan_config_read(const char *lancfgName) { continue; line[sizeof(line)-1] = '\0'; + memset(w2, 0, sizeof(w2)); if (sscanf(line,"%[^:]: %[^\r\n]", w1, w2) != 2) continue; remove_control_chars(w1); remove_control_chars(w2); if (strcmpi(w1, "lan_char_ip") == 0) { // Read Char-Server Lan IP Address + memset(lan_char_ip, 0, sizeof(lan_char_ip)); h = gethostbyname(w2); if (h != NULL) { sprintf(lan_char_ip, "%d.%d.%d.%d", (unsigned char)h->h_addr[0], (unsigned char)h->h_addr[1], (unsigned char)h->h_addr[2], (unsigned char)h->h_addr[3]); @@ -3253,8 +3300,7 @@ int login_config_read(const char *cfgName) { char line[1024], w1[1024], w2[1024]; FILE *fp; - fp = fopen(cfgName, "r"); - if (fp == NULL) { + if ((fp = fopen(cfgName, "r")) == NULL) { printf("Configuration file (%s) not found.\n", cfgName); return 1; } @@ -3265,6 +3311,7 @@ int login_config_read(const char *cfgName) { continue; line[sizeof(line)-1] = '\0'; + memset(w2, 0, sizeof(w2)); if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) == 2) { remove_control_chars(w1); remove_control_chars(w2); @@ -3272,11 +3319,12 @@ int login_config_read(const char *cfgName) { if (strcmpi(w1, "admin_state") == 0) { admin_state = config_switch(w2); } else if (strcmpi(w1, "admin_pass") == 0) { + memset(admin_pass, 0, sizeof(admin_pass)); strncpy(admin_pass, w2, sizeof(admin_pass)); admin_pass[sizeof(admin_pass)-1] = '\0'; } else if (strcmpi(w1, "ladminallowip") == 0) { if (strcmpi(w2, "clear") == 0) { - if (access_ladmin_allow) + if (access_ladmin_allow) free(access_ladmin_allow); access_ladmin_allow = NULL; access_ladmin_allownum = 0; @@ -3286,19 +3334,20 @@ int login_config_read(const char *cfgName) { if (access_ladmin_allow) free(access_ladmin_allow); // set to all - access_ladmin_allow = calloc(ACO_STRSIZE, 1); + access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); access_ladmin_allownum = 1; access_ladmin_allow[0] = '\0'; } else if (w2[0] && !(access_ladmin_allownum == 1 && access_ladmin_allow[0] == '\0')) { // don't add IP if already 'all' if (access_ladmin_allow) access_ladmin_allow = realloc(access_ladmin_allow, (access_ladmin_allownum+1) * ACO_STRSIZE); else - access_ladmin_allow = calloc(ACO_STRSIZE, 1); + access_ladmin_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); strncpy(access_ladmin_allow + (access_ladmin_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE); access_ladmin_allow[access_ladmin_allownum * ACO_STRSIZE - 1] = '\0'; } } } else if (strcmpi(w1, "gm_pass") == 0) { + memset(gm_pass, 0, sizeof(gm_pass)); strncpy(gm_pass, w2, sizeof(gm_pass)); gm_pass[sizeof(gm_pass)-1] = '\0'; } else if (strcmpi(w1, "level_new_gm") == 0) { @@ -3308,17 +3357,23 @@ int login_config_read(const char *cfgName) { } else if (strcmpi(w1, "login_port") == 0) { login_port = atoi(w2); } else if (strcmpi(w1, "account_filename") == 0) { + memset(account_filename, 0, sizeof(account_filename)); strncpy(account_filename, w2, sizeof(account_filename)); account_filename[sizeof(account_filename)-1] = '\0'; } else if (strcmpi(w1, "gm_account_filename") == 0) { + memset(GM_account_filename, 0, sizeof(GM_account_filename)); strncpy(GM_account_filename, w2, sizeof(GM_account_filename)); GM_account_filename[sizeof(GM_account_filename)-1] = '\0'; } else if (strcmpi(w1, "gm_account_filename_check_timer") == 0) { gm_account_filename_check_timer = atoi(w2); + } else if (strcmpi(w1, "use_MD5_passwords") == 0) { + use_md5_passwds = config_switch(w2); } else if (strcmpi(w1, "login_log_filename") == 0) { + memset(login_log_filename, 0, sizeof(login_log_filename)); strncpy(login_log_filename, w2, sizeof(login_log_filename)); login_log_filename[sizeof(login_log_filename)-1] = '\0'; } else if (strcmpi(w1, "login_log_unknown_packets_filename") == 0) { + memset(login_log_unknown_packets_filename, 0, sizeof(login_log_unknown_packets_filename)); strncpy(login_log_unknown_packets_filename, w2, sizeof(login_log_unknown_packets_filename)); login_log_unknown_packets_filename[sizeof(login_log_unknown_packets_filename)-1] = '\0'; } else if (strcmpi(w1, "save_unknown_packets") == 0) { @@ -3330,6 +3385,7 @@ int login_config_read(const char *cfgName) { } else if (strcmpi(w1, "display_parse_fromchar") == 0) { display_parse_fromchar = config_switch(w2); // 0: no, 1: yes (without packet 0x2714), 2: all packets } else if (strcmpi(w1, "date_format") == 0) { // note: never have more than 19 char for the date! + memset(date_format, 0, sizeof(date_format)); switch (atoi(w2)) { case 0: strcpy(date_format, "%d-%m-%Y %H:%M:%S"); // 31-12-2004 23:59:59 @@ -3372,14 +3428,14 @@ int login_config_read(const char *cfgName) { if (access_allow) free(access_allow); // set to all - access_allow = calloc(ACO_STRSIZE, 1); + access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); access_allownum = 1; access_allow[0] = '\0'; } else if (w2[0] && !(access_allownum == 1 && access_allow[0] == '\0')) { // don't add IP if already 'all' if (access_allow) access_allow = realloc(access_allow, (access_allownum+1) * ACO_STRSIZE); else - access_allow = calloc(ACO_STRSIZE, 1); + access_allow = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); strncpy(access_allow + (access_allownum++) * ACO_STRSIZE, w2, ACO_STRSIZE); access_allow[access_allownum * ACO_STRSIZE - 1] = '\0'; } @@ -3396,18 +3452,28 @@ int login_config_read(const char *cfgName) { if (access_deny) free(access_deny); // set to all - access_deny = calloc(ACO_STRSIZE, 1); + access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); access_denynum = 1; access_deny[0] = '\0'; } else if (w2[0] && !(access_denynum == 1 && access_deny[0] == '\0')) { // don't add IP if already 'all' if (access_deny) access_deny = realloc(access_deny, (access_denynum+1) * ACO_STRSIZE); else - access_deny = calloc(ACO_STRSIZE, 1); + access_deny = (char*)aCalloc(ACO_STRSIZE, sizeof(char)); strncpy(access_deny + (access_denynum++) * ACO_STRSIZE, w2, ACO_STRSIZE); access_deny[access_denynum * ACO_STRSIZE - 1] = '\0'; } } + // dynamic password error ban + } else if (strcmpi(w1, "dynamic_pass_failure_ban") == 0) { + dynamic_pass_failure_ban = config_switch(w2); + } else if (strcmpi(w1, "dynamic_pass_failure_ban_time") == 0) { + dynamic_pass_failure_ban_time = atoi(w2); + } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_many") == 0) { + dynamic_pass_failure_ban_how_many = atoi(w2); + } else if (strcmpi(w1, "dynamic_pass_failure_ban_how_long") == 0) { + dynamic_pass_failure_ban_how_long = atoi(w2); + // Anti-Freeze } else if(strcmpi(w1,"anti_freeze_enable")==0){ anti_freeze_enable = config_switch(w2); } else if (strcmpi(w1, "anti_freeze_interval") == 0) { @@ -3561,6 +3627,24 @@ void display_conf_warnings(void) { } } + if (dynamic_pass_failure_ban != 0) { + if (dynamic_pass_failure_ban_time < 1) { + printf("***WARNING: Invalid value for dynamic_pass_failure_ban_time (%d) parameter\n", dynamic_pass_failure_ban_time); + printf(" -> set to 5 (5 minutes to look number of invalid passwords.\n"); + dynamic_pass_failure_ban_time = 5; + } + if (dynamic_pass_failure_ban_how_many < 1) { + printf("***WARNING: Invalid value for dynamic_pass_failure_ban_how_many (%d) parameter\n", dynamic_pass_failure_ban_how_many); + printf(" -> set to 3 (3 invalid passwords before to temporarily ban.\n"); + dynamic_pass_failure_ban_how_many = 3; + } + if (dynamic_pass_failure_ban_how_long < 1) { + printf("***WARNING: Invalid value for dynamic_pass_failure_ban_how_long (%d) parameter\n", dynamic_pass_failure_ban_how_long); + printf(" -> set to 1 (1 minute of temporarily ban.\n"); + dynamic_pass_failure_ban_how_long = 1; + } + } + return; } @@ -3616,6 +3700,11 @@ void save_config_in_log(void) { else login_log("- to check GM accounts file modifications every %d seconds." RETCODE, gm_account_filename_check_timer); + if (use_md5_passwds == 0) + login_log("- to save password in plain text." RETCODE); + else + login_log("- to save password with MD5 encrypting." RETCODE); + // not necessary to log the 'login_log_filename', we are inside :) login_log("- with the unknown packets file name: '%s'." RETCODE, login_log_unknown_packets_filename); @@ -3698,6 +3787,15 @@ void save_config_in_log(void) { for(i = 0; i < access_denynum; i++) login_log(" %s" RETCODE, (char *)(access_deny + i * ACO_STRSIZE)); } + + // dynamic password error ban + if (dynamic_pass_failure_ban == 0) + login_log("- with NO dynamic password error ban." RETCODE); + else { + printf("- with a dynamic password error ban:" RETCODE); + printf(" After %d invalid password in %d minutes" RETCODE, dynamic_pass_failure_ban_how_many, dynamic_pass_failure_ban_time); + printf(" IP is banned for %d minutes" RETCODE, dynamic_pass_failure_ban_how_long); + } } } @@ -3728,15 +3826,23 @@ void do_final(void) { mmo_auth_sync(); - free(auth_dat); - free(gm_account_db); + if(auth_dat) free(auth_dat); + if(gm_account_db) free(gm_account_db); for (i = 0; i < MAX_SERVERS; i++) { - if ((fd = server_fd[i]) >= 0) + if ((fd = server_fd[i]) >= 0) { + server_fd[i] = -1; + memset(&server[i], 0, sizeof(struct mmo_char_server)); + close(fd); delete_session(fd); + } } + close(login_fd); delete_session(login_fd); login_log("----End of login-server (normal end with closing of all files)." RETCODE); + + if(log_fp) + fclose(log_fp); } //------------------------------ @@ -3758,22 +3864,22 @@ int do_init(int argc, char **argv) { for(i = 0; i < MAX_SERVERS; i++) server_fd[i] = -1; - gm_account_db = numdb_init(); - + //gm_account_db = numdb_init(); + gm_account_db = NULL; + GM_num = 0; + GM_max = 0; read_gm_account(); mmo_auth_init(); - set_termfunc(mmo_auth_sync); +// set_termfunc(mmo_auth_sync); set_defaultparse(parse_login); login_fd = make_listen_port(login_port); - add_timer_func_list(check_auth_sync, "check_auth_sync"); - - i = add_timer_interval(gettick() + 60000, check_auth_sync, 0, 0, 60000); // every 60 sec we check if we must save accounts file (only if necessary to save) - if(anti_freeze_enable > 0) { add_timer_func_list(char_anti_freeze_system, "char_anti_freeze_system"); i = add_timer_interval(gettick() + 1000, char_anti_freeze_system, 0, 0, ANTI_FREEZE_INTERVAL * 1000); } + add_timer_func_list(check_auth_sync, "check_auth_sync"); + i = add_timer_interval(gettick() + 60000, check_auth_sync, 0, 0, 60000); // every 60 sec we check if we must save accounts file (only if necessary to save) // add timer to check GM accounts file modification j = gm_account_filename_check_timer; @@ -3791,17 +3897,15 @@ int do_init(int argc, char **argv) { add_timer_func_list(check_GM_file, "check_GM_file"); i = add_timer_interval(gettick() + j * 1000, check_GM_file, 0, 0, j * 1000); // every x sec we check if gm file has been changed - if ( console ) { - set_defaultconsoleparse(parse_console); + if(console) { + set_defaultconsoleparse(parse_console); start_console(); } login_log("The login-server is ready (Server is listening on the port %d)." RETCODE, login_port); - printf("The login-server is \033[1;32mready\033[0m (Server is listening on the port %d).\n\n", login_port); atexit(do_final); + return 0; } - - diff --git a/src/login/login.h b/src/login/login.h index b810a5412..8589bf004 100644 --- a/src/login/login.h +++ b/src/login/login.h @@ -13,9 +13,10 @@ #define START_ACCOUNT_NUM 2000000 #define END_ACCOUNT_NUM 100000000 +int login_port; struct mmo_account { char* userid; - char* passwd; + char passwd[33]; int passwdenc; long account_id; @@ -27,7 +28,7 @@ struct mmo_account { }; struct mmo_char_server { - char name[20]; + char name[21]; long ip; short port; int users; @@ -35,4 +36,6 @@ struct mmo_char_server { int new; }; +struct mmo_char_server server[MAX_SERVERS]; +int server_fd[MAX_SERVERS]; #endif -- cgit v1.2.3-70-g09d2