// *Sandstorm's failed Password System in a Nut-Shell* // // *You will need to replace certain things yourself as my* // // *Mud is FAR from stock.* // // *This file contains my password system, and my block system* // // *As they are tighly wound together to aid in the protection of my* // // *Mud.* // // *Example: CD is CHAR_DATA, sendf is printf_to_char, LINK_SINGLE* // // *is the standard linking code for making a singly linked list (i know, evil)* // // *Bad Passwords*// // *Why did i do this? Because i was raised by a security professional* // // *and am one myself, password protection is a VERY good thing.* // // *If you know what the failed passwords were, then you have a good* // // *Know-how of how close they were to cracking your password. Which* // // *btw, is better then not knowing that someone has tried to crack your password* // // *Plus if they were close to getting it, you can easily change your password* // // *Thus offsetting how close they were.* // // *Block data* // // *Okay, the internal blocking system parses through the muds updater. And it only holds* // // *the blocked data for 30 minutes, when its time it up, it clears, this is just a temporary block* // // *to block out people with bad passwords, i know, i know, 30 minutes of blocking someone* // // *for forgetting their own password, harsh eh, but they would be the first to complain if someone* // // *broke into their account and there failed attempts weren't blocked. * // // *Perm-Block* // // *Okay, the block data contains afew things, however, i have discovered that blocking people perm* // // *is fun, especialy when they try 37 times to break into an account and fail.* // // *So this block system will count 'how' many times an ip has been blocked, and if it has been blocked* // // *1 too many times, it will get perm-blocked, the perm-block is actauly allot less then one might think.* // // *But 30 days is an aweful-long time.* // // *Gets rid of the block-data after a day of not having to block someone.* // // *Contact Me with questions, comments, whatever: ldevil@hotmail.com* // // *This code uses the diku/merc/rom and smaug licences, you must agree to them all to use * // // *This code.* // // merc.h // // *Failed Data* // typedef struct failed_data FAILED_DATA; typedef struct block_data BLOCK_DATA; struct failed_data { FAILED_DATA *next; char *login_password; char *ip; char *ident; }; struct block_data { BLOCK_DATA *next; char *ip; char *ident; int count; int timer; int permtimer; int rebock_count; bool blocked; bool perm; bool was_blocked; time_t last_blocked; }; #define MAX_ATTEMPTS 3 // *Saves time on doing the whole void do_whatever(CD *ch, char *argument) makes life easier.* // #define MUDCMD(name) void name(CD *ch, char *argument) #define PULSE_BLOCK (4 * PULSE_PER_SECOND) // in pc_data; add FAILED_DATA *failed; with the externs add extern BLOCK_DATA *block_list; with the prototypes void write_blocked(void); void read_blocked(void); // *act_wiz or ban.c... Your choice ;)* // MUDCMD(do_blocked) { BLOCK_DATA *blocked; BUFFER *output = NewBuf(); int count = 0; BufPrintf(output, "[Num] %16s %15s %8s %8s", "Ip Addr", "Host Name", "blocked", "perm"); for(blocked = block_list; blocked; blocked = blocked->next) { count++; BufPrintf(output, "[%3d] %16s %15s %8s %8s\n\r", count, blocked->ip, blocked->ident, blocked->blocked ? "Yes" : "No", blocked->perm ? "Yes" : "No"); } BufPrintf(output, "There are %d items on the blocked list.\n\r", count); page_to_char(buf_string(output), ch); free_buf(output); return; } // *comm.c* // with the rest of the local variables in comm.c Add BLOCK_DATA *block_list; where it says 'wrong password' put this bellow it. bad_password(d->character, argument); Further Down, where it says do_look(ch, "auto"); put this right above it.. view_bad_passwords(ch); in init_descriptor right before it sends the greeting, add this // *Are you blocked?* // if(check_blocked(dnew)) { write_to_buffer(d, "You are currently blocked for failed password attempts.\n\r" ,0); CloseSocket(dnew); return; } // *Add this function before init_descriptor* // // *Check to see if they are blocked* // bool check_blocked(DESCRIPTOR *d) { BLOCK_DATA *block; // *Are you blocked?* // for(block = block_list; block; block = block->next) { // *Ensure they are blocked before checking* // if(block->blocked) { // *Got the IP yet?* // if(!StrCmp(block->ip, d->host)) return true; // *Got their Ident yet?* // if(!StrCmp(block->ident, d->ident)) return true; } } // *not blocked... YAY!* // return false; } // *Add these functions before the nanny becuase they are not globaly defined.* // // *Log the failed password-count, and the password used.* // void bad_password(CD *ch, char *password) { FAILED_DATA *fail; BLOCK_DATA *block; // Add a new Failure. CREATE(fail, FAILED_DATA, 1); fail->login_password = StrDup(password); fail->ip = StrDup(ch->desc->host); fail->ident = StrDup(ch->desc->ident); LINK_SINGLE(fail, next, ch->pcdata->failed); for(block = block_list; block; block = block->next) { if(!StrCmp(block->ip, fail->ip)) { // *Failed attempts are getting up there.* // block->count++; // *Reset their timer, so they will ALWAYS have the 30 minute wait.* // block->timer = 0; if(block->count == MAX_ATTEMPTS) { sendf(ch,"You have been blocked for 30 minutes.\n\r"); block->blocked = true; block->last_blocked = ctime(¤t_time); if(block->was_blocked) { block->reblock_count++; // *Uh oh asshole, perm now.* // if(block->reblock_count == MAX_ATTEMPTS) block->perm = true; } } break; } } // *Not blocked eh? Start the counter!* // if(!block) { CREATE(block, BLOCK_DATA, 1); LINK_SINGLE(block, next, block_list); block->ip = StrDup(fail->ip); block->ident = StrDup(fail->ip); block->count = 1; block->reblock_count = 0; block->blocked = false; block->perm = false; // Start the timer.. Will update every minute by 1 block->timer = 0; block->last_blocked = ctime(¤t_time); } // *Wiznet the failure* // wiznet_printf(NULL, NULL, WIZ_PASSWORD, 0, IMMORTAL, "%s has a failed login attempt from site %s:%s.",ch->name, fail->ip, fail->ident); } // *View the failed attempts* // void view_bad_passwords(CD *ch) { FAILED_DATA *fail, *fail_next; int counter = 0; // *Found a bad attempt* // if(ch->pcdata->failed) { sendf(ch, "[Num] %16s %15s %15s", "Password", "IP", "Ident"); } for(fail = ch->pcdata->failed; fail; fail = fail_next) { fail_next = fail->next; sendf(ch, "[%3d] %16s %15s %15s", counter, fail->password, fail->ip, fail->ident); counter++; // *Remove it from the list.* // UNLINK_SINGLE(fail, next, FAILED_DATA, ch->pcdata->failed); // *Dispose of the failed data.* // DISPOSE(failed->password); DISPOSE(failed->ip); DISPOSE(failed->ident); DISPOSE(failed); } if(counter == 0) sendf(ch, "There have been no failed login attempts.\n\r" ); else sendf(ch, "You have had %d failed login attempt%s.\n\r", counter, counter > 1 ? "s" : ""); // *Just ending the function* // return; } // *in db.c* // in boot_db, near the end after area-loading is done, add in this. read_blocked(); after boot_db, before it goes into all the 'area' loading stuff, add these two functions. void write_blocked(void) { FILE *fp; BLOCK_DATA *block; if((fp = FileOpen(BLOCK_FILE, "w")) == NULL) { log_string("Odd, no block file."); // And then we continue; } for(block = block_list; block; block = block->next) { fprintf(fp, "BLOCK %s~ %s~ %d %d %ld %d\n", block->ip, block->ident, block->blocked, block->perm, block->last_blocked, block->reblock_count); } fprintf(fp, "End"); FileClose(fp); } void read_blocked(void) { FILE *fp; if((fp = FileOpen(BLOCK_FILE, "r")) == NULL) { perror(BLOCK_FILE); return; } for( ;; ) { BLOCK_DATA *block; char *wword = fread_word(fp); // *Sick eh?* // if(!StrCmp(wword, "End")) break; CREATE(block, BLOCK_DATA, 1); block->ip = fread_string(fp); block->ident = fread_string(fp); block->blocked = fread_number(fp); block->perm = fread_number(fp); block->last_blocked = fread_number(fp); block->reblock_count = fread_number(fp); // *Block them :)* // LINK_SINGLE(block, next, block_list); } FileClose(fp); } interp.c Add do_blocked to both interp.c and interp.h // *Save the failed password attempt* // save.c in fwrite_char Add FAILED_DATA *fail; and somewhere else, with all the for-loops. Add.. for(fail = ch->pcdata->failed; fail; fail = fail->next) { fprintf(fp, "Failed %s~ %s~ %s~", fail->login_password, fail->ip, fail->ident); } in fread_char, under 'F' Add if(!StrCmp(wword, "Failed")) { FAILED_DATA *fail; CREATE(fail, FAILED_DATA, 1); // *So sick, yet so easy.* // fail->login_password = fread_string(fp); fail->ip = fread_string(fp); fail->ident = fread_string(fp); LINK_SINGLE(fail, next, ch->pcdata->failed); found = true; break; } in update.c Add this *before* update_handler void update_blocks(void) { BLOCK_DATA *block, *block_next; // *parse through the list.* // for(block = block_list; block; block = block_next) { int month = block->last_blocked->tm_mon; int year = block->last_blocked->tm_year; bool remove = false;; block_next = block->next; block->timer++; // *Uh oh, the year is less then the system's year, this = bad! remove them!* // if(year < current_time->tm_year) { remove = true; } else if(month < current_time->tm_mon && !block->perm) // Okay, back to normality { if(day > current_time->tm_wday) remove = false; else remove = true; } // *Yuppers, we gotta remove you.* // if(remove) { UNLINK_SINGLE(block, next, BLOCK_DATA, block_list); DISPOSE(block->ip); DISPOSE(block->ident); DISPOSE(block); continue; } // *Older then 30 minutes. Remove the block.* // if(block->timer > 30 && !block->perm) { // *Close the blocked data* // block->count = 0; block->blocked = false; block->perm = false; block->was_blocked = true; } else if(block->timer > 720000 && block->perm) // *Older then 1 month (i think)* // { // *Close the blocked data* // block->count = 0; block->blocked = false; block->was_blocked = true; block->perm = false; } } // *Thats right, everytime we update, re-write the file.* // write_blocked(); } in update_handler Add this. static int pulse_block; if(--pulse_block % PULSE_BLOCK) { pulse_block = PULSE_BLOCK; update_blocks(); }