static void ns_cmd_sendpass(sourceinfo_t *si, int parc, char *parv[]) { myuser_t *mu; char *name = parv[0]; char *newpass = NULL; char *key; metadata_t *md; enum specialoperation op = op_none; bool ismarked = false; char cmdtext[NICKLEN + 20]; hook_user_needforce_t needforce_hdata; if (!name) { command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SENDPASS"); command_fail(si, fault_needmoreparams, _("Syntax: SENDPASS <account>")); return; } if (parc > 1) { if (!strcasecmp(parv[1], "FORCE")) op = op_force; else if (!strcasecmp(parv[1], "CLEAR")) op = op_clear; else { command_fail(si, fault_badparams, STR_INVALID_PARAMS, "SENDPASS"); command_fail(si, fault_badparams, _("Syntax: SENDPASS <account> [FORCE|CLEAR]")); return; } } if (!(mu = myuser_find_by_nick(name))) { command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), name); return; } if (is_soper(mu) && !has_priv(si, PRIV_ADMIN)) { logcommand(si, CMDLOG_ADMIN, "failed SENDPASS \2%s\2 (is SOPER)", name); command_fail(si, fault_badparams, _("\2%s\2 belongs to a services operator; you need %s privilege to send the password."), name, PRIV_ADMIN); return; } if (mu->flags & MU_WAITAUTH) { command_fail(si, fault_badparams, _("\2%s\2 is not verified."), entity(mu)->name); return; } if ((md = metadata_find(mu, "private:mark:setter"))) { ismarked = true; if (op == op_none) { logcommand(si, CMDLOG_ADMIN, "failed SENDPASS \2%s\2 (marked by \2%s\2)", entity(mu)->name, md->value); command_fail(si, fault_badparams, _("This operation cannot be performed on %s, because the account has been marked by %s."), entity(mu)->name, md->value); if (has_priv(si, PRIV_MARK)) { snprintf(cmdtext, sizeof cmdtext, "SENDPASS %s FORCE", entity(mu)->name); command_fail(si, fault_badparams, _("Use %s to override this restriction."), cmdtext); } return; } else if (!has_priv(si, PRIV_MARK)) { logcommand(si, CMDLOG_ADMIN, "failed SENDPASS \2%s\2 (marked by \2%s\2)", entity(mu)->name, md->value); command_fail(si, fault_noprivs, STR_NO_PRIVILEGE, PRIV_MARK); return; } } needforce_hdata.si = si; needforce_hdata.mu = mu; needforce_hdata.allowed = 1; hook_call_user_needforce(&needforce_hdata); if (!needforce_hdata.allowed) { ismarked = true; if (op == op_none) { logcommand(si, CMDLOG_ADMIN, "failed SENDPASS \2%s\2 (marked)", entity(mu)->name); command_fail(si, fault_badparams, _("This operation cannot be performed on %s, because the account has been marked."), entity(mu)->name); if (has_priv(si, PRIV_MARK)) { snprintf(cmdtext, sizeof cmdtext, "SENDPASS %s FORCE", entity(mu)->name); command_fail(si, fault_badparams, _("Use %s to override this restriction."), cmdtext); } return; } else if (!has_priv(si, PRIV_MARK)) { logcommand(si, CMDLOG_ADMIN, "failed SENDPASS \2%s\2 (marked)", entity(mu)->name); command_fail(si, fault_noprivs, STR_NO_PRIVILEGE, PRIV_MARK); return; } } if (op == op_clear) { if (metadata_find(mu, "private:setpass:key")) { metadata_delete(mu, "private:setpass:key"); metadata_delete(mu, "private:sendpass:sender"); metadata_delete(mu, "private:sendpass:timestamp"); logcommand(si, CMDLOG_ADMIN, "SENDPASS:CLEAR: \2%s\2", entity(mu)->name); command_success_nodata(si, _("The password change key for \2%s\2 has been cleared."), entity(mu)->name); } else command_fail(si, fault_nochange, _("\2%s\2 did not have a password change key outstanding."), entity(mu)->name); return; } if (MOWGLI_LIST_LENGTH(&mu->logins) > 0) { command_fail(si, fault_noprivs, _("This operation cannot be performed on %s, because someone is logged in to it."), entity(mu)->name); return; } if (metadata_find(mu, "private:freeze:freezer")) { command_fail(si, fault_noprivs, _("%s has been frozen by the %s administration."), entity(mu)->name, me.netname); return; } if (command_find(si->service->commands, "SETPASS")) { if (metadata_find(mu, "private:setpass:key")) { command_fail(si, fault_alreadyexists, _("\2%s\2 already has a password change key outstanding."), entity(mu)->name); command_fail(si, fault_alreadyexists, _("Use SENDPASS %s CLEAR to clear it so that a new one can be sent."), entity(mu)->name); return; } if (ismarked) { wallops("%s sent the password for the \2MARKED\2 account %s.", get_oper_name(si), entity(mu)->name); if (md) command_success_nodata(si, _("Overriding MARK placed by %s on the account %s."), md->value, entity(mu)->name); else command_success_nodata(si, _("Overriding MARK on the account %s."), entity(mu)->name); } logcommand(si, CMDLOG_ADMIN, "SENDPASS: \2%s\2 (change key)", name); key = random_string(12); metadata_add(mu, "private:sendpass:sender", get_oper_name(si)); metadata_add(mu, "private:sendpass:timestamp", number_to_string(time(NULL))); if (!sendemail(si->su != NULL ? si->su : si->service->me, mu, EMAIL_SETPASS, mu->email, key)) { command_fail(si, fault_emailfail, _("Email send failed.")); free(key); return; } metadata_add(mu, "private:setpass:key", crypt_string(key, gen_salt())); free(key); command_success_nodata(si, _("The password change key for \2%s\2 has been sent to \2%s\2."), entity(mu)->name, mu->email); } else { if (ismarked) { wallops("%s sent the password for the \2MARKED\2 account %s.", get_oper_name(si), entity(mu)->name); if (md) command_success_nodata(si, _("Overriding MARK placed by %s on the account %s."), md->value, entity(mu)->name); else command_success_nodata(si, _("Overriding MARK on the account %s."), entity(mu)->name); } logcommand(si, CMDLOG_ADMIN, "SENDPASS: \2%s\2", name); newpass = random_string(12); metadata_add(mu, "private:sendpass:sender", get_oper_name(si)); metadata_add(mu, "private:sendpass:timestamp", number_to_string(time(NULL))); if (!sendemail(si->su != NULL ? si->su : si->service->me, mu, EMAIL_SENDPASS, mu->email, newpass)) { command_fail(si, fault_emailfail, _("Email send failed.")); free(newpass); return; } set_password(mu, newpass); free(newpass); command_success_nodata(si, _("The password for \2%s\2 has been sent to \2%s\2."), entity(mu)->name, mu->email); if (mu->flags & MU_NOPASSWORD) { mu->flags &= ~MU_NOPASSWORD; command_success_nodata(si, _("The \2%s\2 flag has been removed for account \2%s\2."), "NOPASSWORD", entity(mu)->name); } } }
static void ns_cmd_sendpass(struct sourceinfo *si, int parc, char *parv[]) { struct myuser *mu; char *name = parv[0]; char *key; enum specialoperation op = op_none; bool ismarked = false; if (!name) { command_fail(si, fault_needmoreparams, STR_INSUFFICIENT_PARAMS, "SENDPASS"); command_fail(si, fault_needmoreparams, _("Syntax: SENDPASS <account>")); return; } if (parc > 1) { if (!has_priv(si, PRIV_USER_SENDPASS)) { command_fail(si, fault_noprivs, STR_NOT_AUTHORIZED); return; } else if (!strcasecmp(parv[1], "FORCE")) op = op_force; else if (!strcasecmp(parv[1], "CLEAR")) op = op_clear; else { command_fail(si, fault_badparams, STR_INVALID_PARAMS, "SENDPASS"); command_fail(si, fault_badparams, _("Syntax: SENDPASS <account> [FORCE|CLEAR]")); return; } } if (!(mu = myuser_find_by_nick(name))) { command_fail(si, fault_nosuch_target, STR_IS_NOT_REGISTERED, name); return; } if (mu->flags & MU_WAITAUTH) { command_fail(si, fault_badparams, _("\2%s\2 is not verified."), entity(mu)->name); return; } if (metadata_find(mu, "private:mark:setter")) { ismarked = true; // don't want to disclose this, so just go ahead... } if (op == op_clear) { if (metadata_find(mu, "private:setpass:key")) { logcommand(si, CMDLOG_ADMIN, "SENDPASS:CLEAR: \2%s\2", entity(mu)->name); metadata_delete(mu, "private:setpass:key"); metadata_delete(mu, "private:sendpass:sender"); metadata_delete(mu, "private:sendpass:timestamp"); command_success_nodata(si, _("The password change key for \2%s\2 has been cleared."), entity(mu)->name); } else command_fail(si, fault_nochange, _("\2%s\2 did not have a password change key outstanding."), entity(mu)->name); return; } if (MOWGLI_LIST_LENGTH(&mu->logins) > 0) { if (si->smu == mu) command_fail(si, fault_already_authed, _("You are logged in and can change your password using the SET PASSWORD command.")); else command_fail(si, fault_noprivs, _("This operation cannot be performed on %s, because someone is logged in to it."), entity(mu)->name); return; } if (metadata_find(mu, "private:freeze:freezer")) { command_fail(si, fault_noprivs, _("%s has been frozen by the %s administration."), entity(mu)->name, me.netname); return; } if (metadata_find(mu, "private:setpass:key")) { command_fail(si, fault_alreadyexists, _("\2%s\2 already has a password change key outstanding."), entity(mu)->name); if (has_priv(si, PRIV_USER_SENDPASS)) command_fail(si, fault_alreadyexists, _("Use SENDPASS %s CLEAR to clear it so that a new one can be sent."), entity(mu)->name); return; } key = random_string(12); const char *const hash = crypt_password(key); if (!hash) { command_fail(si, fault_internalerror, _("Hash generation for password change key failed.")); sfree(key); return; } if (sendemail(si->su != NULL ? si->su : si->service->me, mu, EMAIL_SETPASS, mu->email, key)) { if (ismarked) wallops("%s sent the password for the \2MARKED\2 account %s.", get_oper_name(si), entity(mu)->name); logcommand(si, CMDLOG_ADMIN, "SENDPASS: \2%s\2 (change key)", name); metadata_add(mu, "private:sendpass:sender", get_oper_name(si)); metadata_add(mu, "private:sendpass:timestamp", number_to_string(time(NULL))); metadata_add(mu, "private:setpass:key", hash); command_success_nodata(si, _("The password change key for \2%s\2 has been sent to the corresponding email address."), entity(mu)->name); } else command_fail(si, fault_emailfail, _("Email send failed.")); sfree(key); }
static int mech_step(sasl_session_t *p, char *message, size_t len, char **out, size_t *out_len) { DH *dh = NULL; BF_KEY key; BIGNUM *their_key = NULL; myuser_t *mu; char *ptr, *secret = NULL, *password = NULL; int ret = ASASL_FAIL; uint16_t size; int secret_size; if (!p->mechdata) return ASASL_FAIL; dh = (DH*)p->mechdata; /* Their pub_key */ if (len < 2) goto end; size = ntohs(*(uint16_t *)message); message += 2; len -= 2; if (size > len) goto end; if ((their_key = BN_bin2bn((unsigned char *)message, size, NULL)) == NULL) goto end; message += size; len -= size; /* Username */ size = strlen(message); if (size >= NICKLEN) /* our base64 routines null-terminate - how polite */ goto end; p->username = strdup(message); message += size + 1; len -= size + 1; if ((mu = myuser_find_by_nick(p->username)) == NULL) goto end; /* AES-encrypted password remains */ /* Compute shared secret */ secret = (char*)malloc(DH_size(dh)); secret_size = DH_compute_key((unsigned char *)secret, their_key, dh); if (secret_size <= 0) goto end; /* Data must be multiple of block size, and let's be reasonable about size */ if (len == 0 || len % 8 || len > 128) goto end; /* Decrypt! */ BF_set_key(&key, secret_size, (unsigned char *)secret); ptr = password = (char*)malloc(len + 1); password[len] = '\0'; while (len) { BF_ecb_encrypt((unsigned char *)message, (unsigned char *)ptr, &key, BF_DECRYPT); message += 8; ptr += 8; len -= 8; } if (verify_password(mu, password)) ret = ASASL_DONE; end: if (their_key) BN_free(their_key); free(secret); free(password); return ret; }
static int mech_step(sasl_session_t *p, char *message, size_t len, char **out, size_t *out_len) { char authz[256]; char authc[256]; char pass[256]; myuser_t *mu; char *end; /* Copy the authzid */ end = memchr(message, '\0', len); if (end == NULL) return ASASL_FAIL; if (end - message > 255) return ASASL_FAIL; len -= end - message + 1; if (len <= 0) return ASASL_FAIL; memcpy(authz, message, end - message + 1); message = end + 1; /* Copy the authcid */ end = memchr(message, '\0', len); if (end == NULL) return ASASL_FAIL; if (end - message > 255) return ASASL_FAIL; len -= end - message + 1; if (len <= 0) return ASASL_FAIL; memcpy(authc, message, end - message + 1); message = end + 1; /* Copy the password */ end = memchr(message, '\0', len); if (end == NULL) end = message + len; if (end - message > 255) return ASASL_FAIL; memcpy(pass, message, end - message); pass[end - message] = '\0'; /* Done dissecting, now check. */ if(!(mu = myuser_find_by_nick(authc))) return ASASL_FAIL; /* Return ASASL_FAIL before p->username is set, * to prevent triggering bad_password(). */ if (mu->flags & MU_NOPASSWORD) return ASASL_FAIL; p->username = sstrdup(authc); p->authzid = sstrdup(authz); if (verify_password(mu, pass)) return ASASL_DONE; else { char description[300]; if ((add_login_history_entry = module_locate_symbol("nickserv/loginhistory", "add_login_history_entry")) != NULL) { snprintf(description, sizeof description, "Failed login: SASL (Plain)"); add_login_history_entry(mu, mu, description); } return ASASL_FAIL; } }
static int mech_step(sasl_session_t *p, char *message, int len, char **out, int *out_len) { DH *dh = NULL; AES_KEY key; BIGNUM *their_key = NULL; myuser_t *mu; char *secret = NULL, *userpw = NULL, *ptr = NULL; char iv[AES_BLOCK_SIZE]; int size, ret = ASASL_FAIL; if (!p->mechdata) return ASASL_FAIL; dh = (DH*)p->mechdata; /* Their pub_key */ if (len <= 2) goto end; size = ntohs(*(unsigned int*)message); message += 2; len -= 2; if (size >= len) goto end; if ((their_key = BN_bin2bn(message, size, NULL)) == NULL) goto end; message += size; len -= size; /* Data must be a multiple of the AES block size. (16) * Verify we also have an IV and at least one block of data. * Cap at a rather arbitrary limit of 272 (IV + 16 blocks of 16 each). */ if (len < sizeof(iv) + AES_BLOCK_SIZE || len % AES_BLOCK_SIZE || len > 272) goto end; /* Extract the IV */ memcpy(iv, message, sizeof(iv)); message += sizeof(iv); len -= sizeof(iv); /* Compute shared secret */ secret = malloc(DH_size(dh)); if ((size = DH_compute_key(secret, their_key, dh)) == -1) goto end; /* Decrypt! (AES_set_decrypt_key takes bits not bytes, hence multiply * by 8) */ AES_set_decrypt_key(secret, size * 8, &key); ptr = userpw = malloc(len + 1); userpw[len] = '\0'; AES_cbc_encrypt(message, userpw, len, &key, iv, AES_DECRYPT); /* Username */ size = strlen(ptr); if (size++ >= NICKLEN) /* our base64 routines null-terminate - how polite */ goto end; p->username = strdup(ptr); ptr += size; len -= size; if ((mu = myuser_find_by_nick(p->username)) == NULL) goto end; /* Password remains */ if (verify_password(mu, ptr)) ret = ASASL_DONE; end: if (their_key) BN_free(their_key); free(secret); free(userpw); return ret; }
static void ns_cmd_resetpass(sourceinfo_t *si, int parc, char *parv[]) { myuser_t *mu; metadata_t *md; char *name = parv[0]; char *newpass; if (!name) { command_fail(si, fault_needmoreparams, STR_INVALID_PARAMS, "RESETPASS"); command_fail(si, fault_needmoreparams, _("Syntax: RESETPASS <account>")); return; } if (!(mu = myuser_find_by_nick(name))) { command_fail(si, fault_nosuch_target, _("\2%s\2 is not registered."), name); return; } if (is_soper(mu) && !has_priv(si, PRIV_ADMIN)) { logcommand(si, CMDLOG_ADMIN, "failed RESETPASS \2%s\2 (is SOPER)", name); command_fail(si, fault_badparams, _("\2%s\2 belongs to a services operator; you need %s privilege to reset the password."), name, PRIV_ADMIN); return; } if ((md = metadata_find(mu, "private:mark:setter"))) { if (has_priv(si, PRIV_MARK)) { wallops("%s reset the password for the \2MARKED\2 account %s.", get_oper_name(si), entity(mu)->name); logcommand(si, CMDLOG_ADMIN, "RESETPASS: \2%s\2 (overriding mark by \2%s\2)", entity(mu)->name, md->value); command_success_nodata(si, _("Overriding MARK placed by %s on the account %s."), md->value, entity(mu)->name); } else { logcommand(si, CMDLOG_ADMIN, "failed RESETPASS \2%s\2 (marked by \2%s\2)", entity(mu)->name, md->value); command_fail(si, fault_badparams, _("This operation cannot be performed on %s, because the account has been marked by %s."), entity(mu)->name, md->value); return; } } else { wallops("%s reset the password for the account %s", get_oper_name(si), entity(mu)->name); logcommand(si, CMDLOG_ADMIN, "RESETPASS: \2%s\2", entity(mu)->name); } newpass = random_string(12); metadata_delete(mu, "private:setpass:key"); metadata_add(mu, "private:sendpass:sender", get_oper_name(si)); metadata_add(mu, "private:sendpass:timestamp", number_to_string(time(NULL))); set_password(mu, newpass); free(newpass); command_success_nodata(si, _("The password for \2%s\2 has been changed to \2%s\2."), entity(mu)->name, newpass); if (mu->flags & MU_NOPASSWORD) { mu->flags &= ~MU_NOPASSWORD; command_success_nodata(si, _("The \2%s\2 flag has been removed for account \2%s\2."), "NOPASSWORD", entity(mu)->name); } }