- sendto_one(target_p, form_str(ERR_SASLFAIL), me.name, EmptyString(target_p->name) ? "*" : target_p->name);
- else if(*parv[4] == 'S') {
- sendto_one(target_p, form_str(RPL_SASLSUCCESS), me.name, EmptyString(target_p->name) ? "*" : target_p->name);
- target_p->localClient->sasl_complete = 1;
- ServerStats.is_ssuc++;
+ {
+ if (in_progress) {
+ sendto_one(target_p, form_str(ERR_SASLFAIL), me.name, EmptyString(target_p->name) ? "*" : target_p->name);
+ }
+ /* Failures with zero messages are just "unknown mechanism" errors; don't count those. */
+ if(target_p->localClient->sasl_messages > 0)
+ {
+ if(*target_p->name)
+ {
+ /* Allow 2 tries before rate-limiting as some clients try EXTERNAL
+ * then PLAIN right after it if the auth failed, causing the client to be
+ * rate-limited immediately and not being able to login with SASL.
+ */
+ if (target_p->localClient->sasl_failures++ > 0)
+ target_p->localClient->sasl_next_retry = rb_current_time() + (1 << MIN(target_p->localClient->sasl_failures + 1, 8));
+ }
+ else if(throttle_add((struct sockaddr*)&target_p->localClient->ip))
+ {
+ exit_client(target_p, target_p, &me, "Too many failed authentication attempts");
+ return;
+ }
+ }
+ }
+ else if(*parv[4] == 'S')
+ {
+ if (in_progress) {
+ sendto_one(target_p, form_str(RPL_SASLSUCCESS), me.name, EmptyString(target_p->name) ? "*" : target_p->name);
+ target_p->localClient->sasl_failures = 0;
+ target_p->localClient->sasl_complete = 1;
+ ServerStats.is_ssuc++;
+ }