commit 57a8716f72dd5c75c98ead085fbd8d7f12660da6
parent f82c7a5cdb2238e8c32649227daee05a0f885155
Author: default <nobody@localhost>
Date: Thu, 19 Dec 2024 18:54:15 +0100
Added bad login throttling.
Diffstat:
M | data.c | | | 105 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | html.c | | | 15 | ++++++++++++--- |
M | snac.h | | | 3 | +++ |
3 files changed, 120 insertions(+), 3 deletions(-)
diff --git a/data.c b/data.c
@@ -3821,3 +3821,108 @@ xs_str *make_url(const char *href, const char *proxy, int by_token)
return url;
}
+
+
+/** bad login throttle **/
+
+xs_str *_badlogin_fn(const char *addr)
+{
+ xs *md5 = xs_md5_hex(addr, strlen(addr));
+ xs *dir = xs_fmt("%s/badlogin", srv_basedir);
+
+ mkdirx(dir);
+
+ return xs_fmt("%s/%s", dir, md5);
+}
+
+
+int _badlogin_read(const char *fn, int *failures)
+/* reads a badlogin file */
+{
+ int ok = 0;
+ FILE *f;
+
+ pthread_mutex_lock(&data_mutex);
+
+ if ((f = fopen(fn, "r")) != NULL) {
+ xs *l = xs_readline(f);
+ fclose(f);
+
+ if (sscanf(l, "%d", failures) == 1)
+ ok = 1;
+ }
+
+ pthread_mutex_unlock(&data_mutex);
+
+ return ok;
+}
+
+
+int badlogin_check(const char *user, const char *addr)
+/* checks if this address is authorized to try a login */
+{
+ int valid = 1;
+
+ if (xs_type(addr) == XSTYPE_STRING) {
+ xs *fn = _badlogin_fn(addr);
+ double mt = mtime(fn);
+
+ if (mt > 0) {
+ int badlogin_expire = xs_number_get(xs_dict_get_def(srv_config,
+ "badlogin_expire", "300"));
+
+ mt += badlogin_expire;
+
+ /* if file is expired, delete and give pass */
+ if (mt < time(NULL)) {
+ srv_debug(1, xs_fmt("Login from %s for %s allowed again", addr, user));
+ unlink(fn);
+ }
+ else {
+ int failures;
+
+ if (_badlogin_read(fn, &failures)) {
+ int badlogin_max = xs_number_get(xs_dict_get_def(srv_config,
+ "badlogin_retries", "5"));
+
+ if (failures >= badlogin_max) {
+ valid = 0;
+
+ xs *d = xs_str_iso_date((time_t) mt);
+
+ srv_debug(1,
+ xs_fmt("Login from %s for %s forbidden until %s", addr, user, d));
+ }
+ }
+ }
+ }
+ }
+
+ return valid;
+}
+
+
+void badlogin_inc(const char *user, const char *addr)
+/* increments a bad login from this address */
+{
+ if (xs_type(addr) == XSTYPE_STRING) {
+ int failures = 0;
+ xs *fn = _badlogin_fn(addr);
+ FILE *f;
+
+ _badlogin_read(fn, &failures);
+
+ pthread_mutex_lock(&data_mutex);
+
+ if ((f = fopen(fn, "w")) != NULL) {
+ failures++;
+
+ fprintf(f, "%d %s %s\n", failures, addr, user);
+ fclose(f);
+
+ srv_log(xs_fmt("Registered %d login failure(s) from %s for %s", failures, addr, user));
+ }
+
+ pthread_mutex_unlock(&data_mutex);
+ }
+}
diff --git a/html.c b/html.c
@@ -29,9 +29,18 @@ int login(snac *snac, const xs_dict *headers)
xs *l1 = xs_split_n(s2, ":", 1);
if (xs_list_len(l1) == 2) {
- logged_in = check_password(
- xs_list_get(l1, 0), xs_list_get(l1, 1),
- xs_dict_get(snac->config, "passwd"));
+ const char *user = xs_list_get(l1, 0);
+ const char *pwd = xs_list_get(l1, 1);
+ const char *addr = xs_or(xs_dict_get(headers, "remote-addr"),
+ xs_dict_get(headers, "x-forwarded-for"));
+
+ if (badlogin_check(user, addr)) {
+ logged_in = check_password(user, pwd,
+ xs_dict_get(snac->config, "passwd"));
+
+ if (!logged_in)
+ badlogin_inc(user, addr);
+ }
}
}
diff --git a/snac.h b/snac.h
@@ -425,3 +425,6 @@ typedef struct {
t_announcement *announcement(double after);
xs_str *make_url(const char *href, const char *proxy, int by_token);
+
+int badlogin_check(const char *user, const char *addr);
+void badlogin_inc(const char *user, const char *addr);