diff --git a/chanserv/authcmds/lostpassword.c b/chanserv/authcmds/lostpassword.c new file mode 100644 index 00000000..5a09ef60 --- /dev/null +++ b/chanserv/authcmds/lostpassword.c @@ -0,0 +1,76 @@ +/* CMDNAME: lostpassword + * CMDALIASES: lostpass + * CMDLEVEL: QCMD_NOTAUTHED + * CMDARGS: 1 + * CMDDESC: Sends instructions for resetting your account to your registered email address. + * CMDFUNC: csa_dolostpw + * CMDPROTO: int csa_dolostpw(void *source, int cargc, char **cargv); + * CMDHELP: Usage: @UCOMMAND@ + * CMDHELP: Sends instructions for resetting your account to your registered email address, where: + * CMDHELP: email - your email address + */ + +#include "../chanserv.h" +#include "../authlib.h" +#include "../../lib/irc_string.h" +#include +#include + +int csa_dolostpw(void *source, int cargc, char **cargv) { + reguser *rup; + nick *sender=source; + time_t t; + int i, matched = 0; + + if (cargc<1) { + chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "lostpassword"); + return CMD_ERROR; + } + + t=time(NULL); + + for (i=0;inextbyname) { + if(!rup->email || strcasecmp(cargv[0],rup->email->content)) + continue; + + if(UHasStaffPriv(rup)) { + cs_log(sender,"LOSTPASSWORD FAIL privileged email %s",cargv[0]); + continue; + } + + matched = 1; + + if(rup->lockuntil && rup->lockuntil > t) { + chanservstdmessage(sender, QM_ACCOUNTLOCKED, rup->lockuntil); + continue; + } + + if(csa_checkthrottled(sender, rup, "LOSTPASSWORD")) + continue; + + rup->lockuntil=t; + rup->lastemailchange=t; + csdb_updateuser(rup); + + if(rup->lastauth) { + csdb_createmail(rup, QMAIL_LOSTPW); + } else { + csdb_createmail(rup, QMAIL_NEWACCOUNT); /* user hasn't authed yet and needs to do the captcha */ + } + + cs_log(sender,"LOSTPASSWORD OK username %s email %s", rup->username, rup->email->content); + chanservstdmessage(sender, QM_MAILQUEUED); + } + } + + if(!matched) { + cs_log(sender,"LOSTPASSWORD FAIL email %s",cargv[0]); + chanservstdmessage(sender, QM_BADEMAIL); + return CMD_ERROR; + } else { + chanservstdmessage(sender, QM_DONE); + } + + return CMD_OK; +} diff --git a/chanserv/authcmds/newpass.c b/chanserv/authcmds/newpass.c index b6be66aa..0052ba69 100644 --- a/chanserv/authcmds/newpass.c +++ b/chanserv/authcmds/newpass.c @@ -24,6 +24,7 @@ #include "../chanserv.h" #include "../authlib.h" #include "../../lib/irc_string.h" +#include "../../lib/hmac.h" #include "../../core/hooks.h" #include #include @@ -56,7 +57,7 @@ int csa_donewpw(void *source, int cargc, char **cargv) { return CMD_ERROR; } - if (!strcmp(cargv[0],cargv[1])) { + if (!hmac_strcmp(cargv[0],cargv[1])) { /* If they are the same then continue anyway but don't send the hook later. */ same=1; } diff --git a/chanserv/authcmds/requestpassword.c b/chanserv/authcmds/requestpassword.c index 887b3e6b..641da2a4 100644 --- a/chanserv/authcmds/requestpassword.c +++ b/chanserv/authcmds/requestpassword.c @@ -3,8 +3,8 @@ * * CMDNAME: requestpassword * CMDALIASES: requestpass - * CMDLEVEL: QCMD_NOTAUTHED - * CMDARGS: 2 + * CMDLEVEL: QCMD_HIDDEN + * CMDARGS: 1 * CMDDESC: Requests the current password by email. * CMDFUNC: csa_doreqpw * CMDPROTO: int csa_doreqpw(void *source, int cargc, char **cargv); @@ -20,51 +20,9 @@ #include int csa_doreqpw(void *source, int cargc, char **cargv) { - reguser *rup; nick *sender=source; - int i, matched = 0; - if (cargc<1) { - chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "requestpassword"); - return CMD_ERROR; - } - - for (i=0;inextbyname) { - if(!rup->email || strcasecmp(cargv[0],rup->email->content)) - continue; - - if(UHasStaffPriv(rup)) { - cs_log(sender,"REQUESTPASSWORD FAIL privileged email %s",cargv[0]); - continue; - } - - matched = 1; - - if(csa_checkthrottled(sender, rup, "REQUESTPASSWORD")) - continue; - - rup->lastemailchange=time(NULL); - csdb_updateuser(rup); - - if(rup->lastauth) { - csdb_createmail(rup, QMAIL_REQPW); - } else { - csdb_createmail(rup, QMAIL_NEWACCOUNT); /* user hasn't authed yet and needs to do the captcha */ - } - - cs_log(sender,"REQUESTPASSWORD OK username %s email %s", rup->username,rup->email->content); - chanservstdmessage(sender, QM_MAILQUEUED); - } - } - - if(!matched) { - cs_log(sender,"REQUESTPASSWORD FAIL email %s",cargv[0]); - chanservstdmessage(sender, QM_BADEMAIL); - return CMD_ERROR; - } else { - chanservstdmessage(sender, QM_DONE); - } + chanservstdmessage(sender, QM_CMDDEPRECATEDPREFER, "LOSTPASSWORD"); return CMD_OK; } diff --git a/chanserv/authcmds/resetpassword.c b/chanserv/authcmds/resetpassword.c new file mode 100644 index 00000000..f03c5891 --- /dev/null +++ b/chanserv/authcmds/resetpassword.c @@ -0,0 +1,114 @@ +/* CMDNAME: resetpassword + * CMDALIASES: resetpass + * CMDLEVEL: QCMD_SECURE | QCMD_NOTAUTHED + * CMDARGS: 4 + * CMDDESC: Change your password using a code that was sent to your email address. + * CMDFUNC: csa_dorespw + * CMDPROTO: int csa_dorespw(void *source, int cargc, char **cargv); + * CMDHELP: Usage: @UCOMMAND@ + * CMDHELP: Changes your account password using a code that was sent to your email address. + * CMDHELP: Your new password must be at least 6 characters long, contain at least one number + * CMDHELP: and one letter, and may not contain sequences of letters or numbers. Also note + * CMDHELP: that your password will be truncated to 10 characters. + * CMDHELP: Your new password will be sent to your registered email address. + * CMDHELP: Where: + * CMDHELP: username - your username + * CMDHELP: code - code you received in an email sent to your registered address + * CMDHELP: newpassword - your desired new password. Must be entered the same both times. + * CMDHELP: Note: due to the sensitive nature of this command, you must send the message to + * CMDHELP: Q@CServe.quakenet.org when using it. + */ + +#include "../chanserv.h" +#include "../authlib.h" +#include "../../lib/irc_string.h" +#include "../../lib/hmac.h" +#include +#include + +int csa_dorespw(void *source, int cargc, char **cargv) { + reguser *rup; + nick *sender=source; + char *username, *code, *newpass, *newpassconfirm; + unsigned int same=0; + int pq; + time_t t; + + if (cargc<4) { + chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "resetpassword"); + return CMD_ERROR; + } + + username=cargv[0]; + code=cargv[1]; + newpass=cargv[2]; + newpassconfirm=cargv[3]; + + if (!(rup=findreguser(sender, username))) + return CMD_ERROR; + + if (strcmp(newpass,newpassconfirm)) { + chanservstdmessage(sender, QM_PWDONTMATCH); /* Sorry, passwords do not match */ + cs_log(sender,"RESETPASS FAIL username %s new passwords don't match (%s vs %s)",rup->username,newpass,newpassconfirm); + return CMD_ERROR; + } + + if (!hmac_strcmp(rup->password,newpass)) { + /* If they are the same then continue anyway but don't send the hook later. */ + same=1; + } + + pq = csa_checkpasswordquality(newpass); + if(pq == QM_PWTOSHORT) { + chanservstdmessage(sender, QM_PWTOSHORT); /* new password too short */ + cs_log(sender,"RESETPASS FAIL username %s password too short %s (%zu characters)",rup->username,newpass,strlen(newpass)); + return CMD_ERROR; + } else if(pq == QM_PWTOWEAK) { + chanservstdmessage(sender, QM_PWTOWEAK); /* new password is weak */ + cs_log(sender,"RESETPASS FAIL username %s password too weak %s",rup->username,newpass); + return CMD_ERROR; + } else if(pq == QM_PWTOLONG) { + chanservstdmessage(sender, QM_PWTOLONG); /* new password too long */ + cs_log(sender,"RESETPASS FAIL username %s password too long %s",rup->username,newpass); + return CMD_ERROR; + } else if(pq == -1) { + /* all good */ + } else { + chanservsendmessage(sender, "Encountered an unknown error; please contact #help."); + return CMD_ERROR; + } + + if(UHasStaffPriv(rup) || !rup->lockuntil || hmac_strcmp(code, csc_generateresetcode(rup->lockuntil, rup->username))) { + chanservstdmessage(sender, QM_BADRESETCODE); + return CMD_ERROR; + } + + t=time(NULL); + + if(rup->lockuntil > t) { + chanservstdmessage(sender, QM_ACCOUNTLOCKED, rup->lockuntil); + return CMD_ERROR; + } + + rup->lockuntil=t+7*24*3600; + + if(rup->lastemail) { + freesstring(rup->lastemail); + rup->lastemail=NULL; + } + + rup->lastpasschange=t; + csdb_accounthistory_insert(sender, rup->password, newpass, NULL, NULL); + setpassword(rup, newpass); + + rup->lastauth=t; + chanservstdmessage(sender, QM_PWCHANGED); + cs_log(sender,"RESETPASS OK username %s", rup->username); + + csdb_updateuser(rup); + + if (!same) + triggerhook(HOOK_CHANSERV_PWCHANGE, sender); + + return CMD_OK; +} diff --git a/chanserv/authcmds/sendpassword.c b/chanserv/authcmds/sendpassword.c index 033e2f15..06de31a3 100644 --- a/chanserv/authcmds/sendpassword.c +++ b/chanserv/authcmds/sendpassword.c @@ -1,15 +1,12 @@ -/* Automatically generated by refactor.pl. - * - * - * CMDNAME: sendpassword +/* CMDNAME: sendpassword * CMDALIASES: sendpass * CMDLEVEL: QCMD_HELPER * CMDARGS: 1 - * CMDDESC: Sends the users current password by email. + * CMDDESC: Sends the user a reset code to their email address. * CMDFUNC: csa_dosendpw * CMDPROTO: int csa_dosendpw(void *source, int cargc, char **cargv); * CMDHELP: Usage: @UCOMMAND@ - * CMDHELP: Sends the password for the specified account to the specified users email address. + * CMDHELP: Sends the password for the specified account to the specified user's email address. */ #include "../chanserv.h" @@ -21,6 +18,7 @@ int csa_dosendpw(void *source, int cargc, char **cargv) { reguser *rup; nick *sender=source; + time_t t; if (cargc<1) { chanservstdmessage(sender, QM_NOTENOUGHPARAMS, "sendpassword"); @@ -32,20 +30,27 @@ int csa_dosendpw(void *source, int cargc, char **cargv) { if(UHasStaffPriv(rup)) { chanservstdmessage(sender, QM_REQUESTPASSPRIVUSER); - cs_log(sender,"SENDPASSWORD FAIL privilidged user %s",rup->username); + cs_log(sender,"SENDPASSWORD FAIL privileged user %s",rup->username); return CMD_ERROR; } - /* we don't reset the throttling timer - rup->lastemailchange=time(NULL); - csdb_updateuser(rup); - */ + t = time(NULL); - if(rup->lastauth) { - csdb_createmail(rup, QMAIL_REQPW); + if(rup->lockuntil && rup->lockuntil + 30 * 60 > t) { + // Send same reset code. + csdb_createmail(rup, QMAIL_NEWPW); } else { - csdb_createmail(rup, QMAIL_NEWACCOUNT); /* user hasn't authed yet and needs to do the captcha */ + rup->lockuntil=t; + rup->lastemailchange=t; + csdb_updateuser(rup); + + if(rup->lastauth) { + csdb_createmail(rup, QMAIL_LOSTPW); + } else { + csdb_createmail(rup, QMAIL_NEWACCOUNT); /* user hasn't authed yet and needs to do the captcha */ + } } + chanservstdmessage(sender, QM_MAILQUEUED); cs_log(sender,"SENDPASSWORD username %s", rup->username); diff --git a/chanserv/batcher/templates.py b/chanserv/batcher/templates.py index 648f39cd..2758d338 100644 --- a/chanserv/batcher/templates.py +++ b/chanserv/batcher/templates.py @@ -46,9 +46,14 @@ def generate_resetcode(config, obj): obj["lockuntil"] = time.ctime(obj["user.lockuntil"]) obj["resetline"] = "/MSG %(config.bot)s RESET #%(user.username)s %(resetcode)s" % obj +def generate_resetpassword(config, obj): + generate_resetcode(config, obj) + obj["resetline"] = "/MSG %(config.bot)s@%(config.server)s RESETPASSWORD #%(user.username)s %(resetcode)s " %obj + MAILTEMPLATES = { "mutators": { 1: generate_url, + 2: generate_resetpassword, 3: generate_resetcode, 5: generate_resetcode, 6: generate_activation_url, @@ -85,15 +90,15 @@ def generate_resetcode(config, obj): NB: Save this email for future reference. """, }, - 2: { "subject": "%(config.bot)s password request", "body": """ -Your username/password is: - -Username: %(user.username)s -Password: %(user.password)s + 2: { "subject": "%(config.bot)s password reset request", "body": """ +A password reset has been requested for your account. To reset your password, +please use: +%(resetline)s -To auth yourself to %(config.bot)s, type the following command +Where should be replaced with your desired password. - /MSG %(config.bot)s@%(config.server)s AUTH %(user.username)s %(user.password)s +If you did not issue this command, you can ignore this email and no changes will +be made to your account. """, }, 3: { "subject": "%(config.bot)s password change", "body": """ Your password has recently changed. If this was not requested by you, diff --git a/chanserv/chanserv.h b/chanserv/chanserv.h index 6b94adb1..53c93b03 100644 --- a/chanserv/chanserv.h +++ b/chanserv/chanserv.h @@ -259,7 +259,7 @@ #define VALID_ACCOUNT_NAME "\\A[a-z][a-z0-9\\-]+\\Z" #define QMAIL_NEWACCOUNT 1 /* new account */ -#define QMAIL_REQPW 2 /* requestpassword */ +#define QMAIL_LOSTPW 2 /* lostpassword */ #define QMAIL_NEWPW 3 /* new password */ #define QMAIL_RESET 4 /* reset account */ #define QMAIL_NEWEMAIL 5 /* new email address */ diff --git a/chanserv/chanserv_messages.h b/chanserv/chanserv_messages.h index cbd87f97..1788a89d 100644 --- a/chanserv/chanserv_messages.h +++ b/chanserv/chanserv_messages.h @@ -251,6 +251,7 @@ BeginMessages() { msg(QM_TOOMANYCHANNELS, "User is known on too many channels.", ""), msg(QM_PWINVALID, "Password contains invalid characters.", ""), msg(QM_TOOMANYAUTHATTEMPTS, "Too many auth attempts -- reconnect to QuakeNet to try again.", ""), + msg(QM_CMDDEPRECATEDPREFER, "This command has been removed -- please use $0 instead.", "s"), } EndMessages() #endif