Browse Source

ckdb - add OK events (OVENTS) to allow limiting valid accesses

master
kanoi 9 years ago
parent
commit
5f4bbd74e4
  1. 57
      src/ckdb.c
  2. 60
      src/ckdb.h
  3. 331
      src/ckdb_data.c
  4. 377
      src/ckdb_dbio.c

57
src/ckdb.c

@ -495,6 +495,11 @@ K_STORE *events_store;
// Emulate a list for lock checking // Emulate a list for lock checking
K_LIST *event_limits_free; K_LIST *event_limits_free;
// OVENTS (OK EVENTS)
K_TREE *ovents_root;
K_LIST *ovents_free;
K_STORE *ovents_store;
/* N.B. these limits are not production quality /* N.B. these limits are not production quality
* They'll block anyone who makes a mistake 2 or 3 times :) * They'll block anyone who makes a mistake 2 or 3 times :)
* Use optioncontrol OC_LIMITS to set/store them in the database */ * Use optioncontrol OC_LIMITS to set/store them in the database */
@ -528,6 +533,33 @@ EVENT_LIMITS e_limits[] = {
// All access to above and below limits requires the event_limits_free lock // All access to above and below limits requires the event_limits_free lock
int event_limits_hash_lifetime = 24*60*60; int event_limits_hash_lifetime = 24*60*60;
/* N.B. these limits are not production quality
* They'll block anyone who does anything more than once a minute
* Use optioncontrol OC_OLIMITS to set/store them in the database */
EVENT_LIMITS o_limits[] = {
// Homepage valid access - most web access includes Homepage - so this isn't actually counted
{ OVENTID_HOMEPAGE, "HOMEPAGE", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// Blocks valid access
{ OVENTID_BLOCKS, "BLOCKS", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// API valid access
{ OVENTID_API, "API", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// Add/Update single payment address
{ OVENTID_ONEADDR, "ONEADDR", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// Add/Update multi payment address
{ OVENTID_MULTIADDR, "MULTIADDR", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// Workers valid access
{ OVENTID_WORKERS, "WORKERS", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// Other valid access
{ OVENTID_OTHER, "OTHER", 60, 1, 10*60, 10, 60, 1, 10*60, 10, 24*60*60 },
// Terminated by NULL name
{ -1, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
// mulitply IP limit by this to get IPC limit
double ovent_limits_ipc_factor = 2.0;
// maximum lifetime of all o_limits - set by code
int o_limits_max_lifetime = -1;
// AUTHS authorise.id.json={...} // AUTHS authorise.id.json={...}
K_TREE *auths_root; K_TREE *auths_root;
K_LIST *auths_free; K_LIST *auths_free;
@ -1226,6 +1258,11 @@ static void alloc_storage()
events_ipc_root = new_ktree(NULL, cmp_events_ipc, events_free); events_ipc_root = new_ktree(NULL, cmp_events_ipc, events_free);
events_hash_root = new_ktree(NULL, cmp_events_hash, events_free); events_hash_root = new_ktree(NULL, cmp_events_hash, events_free);
ovents_free = k_new_list("OKEvents", sizeof(OVENTS),
ALLOC_OVENTS, LIMIT_OVENTS, true);
ovents_store = k_new_store(ovents_free);
ovents_root = new_ktree(NULL, cmp_ovents, ovents_free);
auths_free = k_new_list("Auths", sizeof(AUTHS), auths_free = k_new_list("Auths", sizeof(AUTHS),
ALLOC_AUTHS, LIMIT_AUTHS, true); ALLOC_AUTHS, LIMIT_AUTHS, true);
auths_store = k_new_store(auths_free); auths_store = k_new_store(auths_free);
@ -1308,12 +1345,13 @@ static void alloc_storage()
DLPRIO(userinfo, 50); DLPRIO(userinfo, 50);
// Uses event_limits // Uses event_limits
DLPRIO(optioncontrol, 48); DLPRIO(optioncontrol, 49);
// Needs to check users and ips and uses events_limits // Needs to check users and ips and uses events_limits
DLPRIO(events, 47); DLPRIO(events, 48);
DLPRIO(ovents, 47);
// events_limits 46 (events-1) above users // events_limits 46 (events-2) above users
DLPRIO(auths, 44); DLPRIO(auths, 44);
DLPRIO(users, 43); DLPRIO(users, 43);
@ -1526,7 +1564,10 @@ static void dealloc_storage()
FREE_ALL(poolstats); FREE_ALL(poolstats);
FREE_TREE(events_user); FREE_TREE(events_user);
FREE_TREE(events_ip); FREE_TREE(events_ip);
FREE_TREE(events_ipc);
FREE_TREE(events_hash);
FREE_LISTS(events); FREE_LISTS(events);
FREE_ALL(ovents);
FREE_ALL(ips); FREE_ALL(ips);
FREE_ALL(auths); FREE_ALL(auths);
@ -6068,9 +6109,17 @@ int main(int argc, char **argv)
#if LOCK_CHECK #if LOCK_CHECK
DLPRIO(process_pplns, 99); DLPRIO(process_pplns, 99);
DLPRIO(workers_db, 98); DLPRIO(workers_db, 98);
DLPRIO(event_limits, 46); // events-1 DLPRIO(event_limits, 46); // events-2
#endif #endif
// set initial value
o_limits_max_lifetime = -1;
i = -1;
while (o_limits[++i].name) {
if (o_limits_max_lifetime < o_limits[i].lifetime)
o_limits_max_lifetime = o_limits[i].lifetime;
}
if (confirm_sharesummary) { if (confirm_sharesummary) {
// TODO: add a system lock to stop running 2 at once? // TODO: add a system lock to stop running 2 at once?
confirm_summaries(); confirm_summaries();

60
src/ckdb.h

@ -51,7 +51,7 @@
#define DB_VLOCK "1" #define DB_VLOCK "1"
#define DB_VERSION "1.0.4" #define DB_VERSION "1.0.4"
#define CKDB_VERSION DB_VERSION"-1.952" #define CKDB_VERSION DB_VERSION"-1.953"
#define WHERE_FFL " - from %s %s() line %d" #define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__ #define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -2045,6 +2045,53 @@ extern K_LIST *event_limits_free;
#define EVENT_OK -1 #define EVENT_OK -1
// Homepage valid access
#define OVENTID_HOMEPAGE 0
// Blocks valid access
#define OVENTID_BLOCKS 1
// API valid access
#define OVENTID_API 2
// Add/Update single payment address
#define OVENTID_ONEADDR 3
// Add/Update multi payment address
#define OVENTID_MULTIADDR 4
// Workers valid access
#define OVENTID_WORKERS 5
// Other valid access
#define OVENTID_OTHER 6
#define OVENTID_NONE 7
// OVENTS RAM only (OK EVENTS)
typedef struct ovents {
// user, ip or ipc
char key[TXT_SML+1];
// One record per hour time/3600
int hour;
// per id per minute
int count[OVENTID_NONE * 60];
HISTORYDATECONTROLFIELDS;
} OVENTS;
#define ALLOC_OVENTS 1000
#define LIMIT_OVENTS 0
#define INIT_OVENTS(_item) INIT_GENERIC(_item, ovents)
#define DATA_OVENTS(_var, _item) DATA_GENERIC(_var, _item, ovents, true)
#define DATA_OVENTS_NULL(_var, _item) DATA_GENERIC(_var, _item, ovents, false)
extern K_TREE *ovents_root;
extern K_LIST *ovents_free;
extern K_STORE *ovents_store;
#define SEC_TO_HOUR(_s) ((int)((_s) / 3600))
#define TV_TO_HOUR(_tv) SEC_TO_HOUR((_tv)->tv_sec)
#define SEC_TO_MIN(_s) ((int)(((_s) % 3600) / 60))
#define TV_TO_MIN(_tv) SEC_TO_MIN((_tv)->tv_sec)
#define IDMIN(_id, _min) ((_id) * 60 + (_min))
#define OC_OLIMITS "ovent_limits_"
#define OVENT_OK EVENT_OK
/* user limits are checked for matching id+user /* user limits are checked for matching id+user
* ip limits are checked for matching id+ip, * ip limits are checked for matching id+ip,
* however, it requires more than one user found with the given ip * however, it requires more than one user found with the given ip
@ -2073,6 +2120,13 @@ extern EVENT_LIMITS e_limits[];
// Has a fixed limit of 1 event allowed // Has a fixed limit of 1 event allowed
extern int event_limits_hash_lifetime; extern int event_limits_hash_lifetime;
// o_limits uses the event_limits_free lock (since it must)
extern EVENT_LIMITS o_limits[];
// multiply IP limit by this to get IPC limit
extern double ovent_limits_ipc_factor;
// maximum lifetime of all o_limits - set by code
extern int o_limits_max_lifetime;
// AUTHS authorise.id.json={...} // AUTHS authorise.id.json={...}
typedef struct auths { typedef struct auths {
int64_t authid; int64_t authid;
@ -2782,6 +2836,10 @@ extern int check_events(EVENTS *events);
extern char *_reply_event(int event, char *buf, bool fre); extern char *_reply_event(int event, char *buf, bool fre);
#define reply_event(_event, _buf) _reply_event(_event, _buf, false) #define reply_event(_event, _buf) _reply_event(_event, _buf, false)
#define reply_event_free(_event, _buf) _reply_event(_event, _buf, true) #define reply_event_free(_event, _buf) _reply_event(_event, _buf, true)
extern cmp_t cmp_ovents(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_ovents(char *key, int hour, K_TREE_CTX *ctx);
extern K_ITEM *last_ovents(char *key, K_TREE_CTX *ctx);
extern int check_ovents(int id, char *u_key, char *i_key, char *c_key, tv_t *now);
extern cmp_t cmp_auths(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_auths(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_poolstats(K_ITEM *a, K_ITEM *b); extern cmp_t cmp_poolstats(K_ITEM *a, K_ITEM *b);
extern void dsp_userstats(K_ITEM *item, FILE *stream); extern void dsp_userstats(K_ITEM *item, FILE *stream);

331
src/ckdb_data.c

@ -4498,7 +4498,7 @@ static const char *cause_str(enum event_cause cause)
} }
} }
// return EVENT_OK or timeout seconds // return EVENT_OK or -timeout seconds
int check_events(EVENTS *events) int check_events(EVENTS *events)
{ {
bool alert = false, user1, user2; bool alert = false, user1, user2;
@ -4539,8 +4539,7 @@ int check_events(EVENTS *events)
// Check hash - same hash passfail on more than one valid User // Check hash - same hash passfail on more than one valid User
if (events->id == EVENTID_PASSFAIL && *(events->hash)) { if (events->id == EVENTID_PASSFAIL && *(events->hash)) {
e_item = last_events_hash(events->id, events->hash, ctx); e_item = last_events_hash(events->id, events->hash, ctx);
if (e_item) { DATA_EVENTS_NULL(e, e_item);
DATA_EVENTS(e, e_item);
user1 = false; user1 = false;
while (e_item && e->id == events->id && while (e_item && e->id == events->id &&
strcmp(e->hash, events->hash) == 0 && strcmp(e->hash, events->hash) == 0 &&
@ -4551,13 +4550,14 @@ int check_events(EVENTS *events)
if (secs >= event_limits_hash_lifetime) { if (secs >= event_limits_hash_lifetime) {
tmp_item = e_item; tmp_item = e_item;
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
// Discard the old event - e is still tmp_item // Discard the old event - e is still old
remove_from_ktree(events_hash_root, tmp_item); remove_from_ktree(events_hash_root, tmp_item);
if (--(e->trees) < 1) { if (--(e->trees) < 1) {
// No longer in any of the event trees // No longer in any of the event trees
k_unlink_item(events_store, tmp_item); k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item); k_add_head(events_free, tmp_item);
} }
DATA_EVENTS_NULL(e, e_item);
continue; continue;
} }
if (!alert) { if (!alert) {
@ -4588,18 +4588,16 @@ int check_events(EVENTS *events)
} }
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
} DATA_EVENTS_NULL(e, e_item);
} }
} }
// Check User // Check User
e_item = last_events_user(events->id, events->createby, ctx); e_item = last_events_user(events->id, events->createby, ctx);
if (e_item) { DATA_EVENTS_NULL(e, e_item);
DATA_EVENTS(e, e_item);
count = 0; count = 0;
while (e_item && e->id == events->id && while (e_item && e->id == events->id &&
strcmp(e->createby, events->createby) == 0 && strcmp(e->createby, events->createby) == 0 &&
CURRENT(&(e->expirydate))) { CURRENT(&(e->expirydate))) {
count++;
// rounded down seconds // rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate)); secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old? // Is this event too old?
@ -4613,8 +4611,10 @@ int check_events(EVENTS *events)
k_unlink_item(events_store, tmp_item); k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item); k_add_head(events_free, tmp_item);
} }
DATA_EVENTS_NULL(e, e_item);
continue; continue;
} }
count++;
if (alert == false && if (alert == false &&
secs <= e_limits[events->id].user_low_time && secs <= e_limits[events->id].user_low_time &&
count > e_limits[events->id].user_low_time_limit) { count > e_limits[events->id].user_low_time_limit) {
@ -4636,12 +4636,11 @@ int check_events(EVENTS *events)
STRNCPY(name, e_limits[events->id].name); STRNCPY(name, e_limits[events->id].name);
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
} DATA_EVENTS_NULL(e, e_item);
} }
// Check IP // Check IP
e_item = last_events_ip(events->id, events->createinet, ctx); e_item = last_events_ip(events->id, events->createinet, ctx);
if (e_item) { DATA_EVENTS_NULL(e, e_item);
DATA_EVENTS(e, e_item);
count = 0; count = 0;
// Remember the first username // Remember the first username
STRNCPY(createby, e->createby); STRNCPY(createby, e->createby);
@ -4649,7 +4648,6 @@ int check_events(EVENTS *events)
while (e_item && e->id == events->id && while (e_item && e->id == events->id &&
strcmp(e->createinet, events->createinet) == 0 && strcmp(e->createinet, events->createinet) == 0 &&
CURRENT(&(e->expirydate))) { CURRENT(&(e->expirydate))) {
count++;
// rounded down seconds // rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate)); secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old? // Is this event too old?
@ -4663,8 +4661,10 @@ int check_events(EVENTS *events)
k_unlink_item(events_store, tmp_item); k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item); k_add_head(events_free, tmp_item);
} }
DATA_EVENTS_NULL(e, e_item);
continue; continue;
} }
count++;
// Allow username case typing errors // Allow username case typing errors
if (strcasecmp(createby, e->createby) != 0) if (strcasecmp(createby, e->createby) != 0)
user2 = true; user2 = true;
@ -4689,17 +4689,17 @@ int check_events(EVENTS *events)
STRNCPY(name, e_limits[events->id].name); STRNCPY(name, e_limits[events->id].name);
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
DATA_EVENTS_NULL(e, e_item);
} }
/* If the ip alert was a single user then it's not an ip failure /* If the ip alert was a single user then it's not an ip failure
* since the User check already covers that */ * since the User check already covers that */
if (alert && (cause == CAUSE_IP_LO || cause == CAUSE_IP_HI) && if (alert && (cause == CAUSE_IP_LO || cause == CAUSE_IP_HI) &&
user2 == false) user2 == false)
alert = false; alert = false;
}
// Check IPC (Class C IP) use same rules as for IP // Check IPC (Class C IP) use same rules as for IP
e_item = last_events_ipc(events->id, events->ipc, ctx); e_item = last_events_ipc(events->id, events->ipc, ctx);
if (e_item) { DATA_EVENTS_NULL(e, e_item);
DATA_EVENTS(e, e_item);
count = 0; count = 0;
// Remember the first username // Remember the first username
STRNCPY(createby, e->createby); STRNCPY(createby, e->createby);
@ -4707,7 +4707,6 @@ int check_events(EVENTS *events)
while (e_item && e->id == events->id && while (e_item && e->id == events->id &&
strcmp(e->ipc, events->ipc) == 0 && strcmp(e->ipc, events->ipc) == 0 &&
CURRENT(&(e->expirydate))) { CURRENT(&(e->expirydate))) {
count++;
// rounded down seconds // rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate)); secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old? // Is this event too old?
@ -4720,8 +4719,10 @@ int check_events(EVENTS *events)
k_unlink_item(events_store, tmp_item); k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item); k_add_head(events_free, tmp_item);
} }
DATA_EVENTS_NULL(e, e_item);
continue; continue;
} }
count++;
// Allow username case typing errors // Allow username case typing errors
if (strcasecmp(createby, e->createby) != 0) if (strcasecmp(createby, e->createby) != 0)
user2 = true; user2 = true;
@ -4746,13 +4747,14 @@ int check_events(EVENTS *events)
STRNCPY(name, e_limits[events->id].name); STRNCPY(name, e_limits[events->id].name);
} }
e_item = prev_in_ktree(ctx); e_item = prev_in_ktree(ctx);
DATA_EVENTS_NULL(e, e_item);
} }
/* If the ipc alert was a single user then it's not an ipc failure /* If the ipc alert was a single user then it's not an ipc failure
* since the User check already covers that */ * since the User check already covers that */
if (alert && (cause == CAUSE_IPC_LO || cause == CAUSE_IPC_HI) && if (alert && (cause == CAUSE_IPC_LO || cause == CAUSE_IPC_HI) &&
user2 == false) user2 == false)
alert = false; alert = false;
}
K_RUNLOCK(event_limits_free); K_RUNLOCK(event_limits_free);
K_WUNLOCK(events_free); K_WUNLOCK(events_free);
if (alert) { if (alert) {
@ -4786,15 +4788,17 @@ int check_events(EVENTS *events)
exit(0); exit(0);
} }
} }
return lifetime; return -lifetime;
} }
return EVENT_OK; return EVENT_OK;
} }
static char lurt[] = "alert="; static char lurt[] = "alert=";
static size_t lurtsiz = sizeof(lurt); static size_t lurtsiz = sizeof(lurt);
static char toomany[] = "Too many failures, come back later"; static char tmf[] = "Too many failures, come back later";
static size_t toomanysiz = sizeof(toomany); static size_t tmfsiz = sizeof(tmf);
static char tma[] = "Too many accesses, come back later";
static size_t tmasiz = sizeof(tma);
char *_reply_event(int event, char *buf, bool fre) char *_reply_event(int event, char *buf, bool fre)
{ {
@ -4814,16 +4818,299 @@ char *_reply_event(int event, char *buf, bool fre)
len = strlen(buf); len = strlen(buf);
len += 1 + lurtsiz; len += 1 + lurtsiz;
len += toomanysiz + 1; if (event < 0)
len += tmfsiz;
else
len += tmasiz;
reply = malloc(len); reply = malloc(len);
if (!reply) if (!reply)
quithere(1, "malloc (%d) OOM", (int)len); quithere(1, "malloc (%d) OOM", (int)len);
snprintf(reply, len, "%s%c%s%s", buf, FLDSEP, lurt, toomany); snprintf(reply, len, "%s%c%s%s", buf, FLDSEP, lurt,
event < 0 ? tmf : tma);
if (fre) if (fre)
free(buf); free(buf);
return reply; return reply;
} }
// order by key asc,hour asc,expirydate desc
cmp_t cmp_ovents(K_ITEM *a, K_ITEM *b)
{
OVENTS *ea, *eb;
DATA_OVENTS(ea, a);
DATA_OVENTS(eb, b);
cmp_t c = CMP_STR(ea->key, eb->key);
if (c == 0) {
c = CMP_INT(ea->hour, eb->hour);
if (c == 0) {
c = CMP_TV(eb->expirydate, ea->expirydate);
}
}
return c;
}
K_ITEM *find_ovents(char *key, int hour, K_TREE_CTX *ctx)
{
K_TREE_CTX ctx0[1];
OVENTS ovents;
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
STRNCPY(ovents.key, key);
ovents.hour = hour;
copy_tv(&(ovents.expirydate), &default_expiry);
INIT_OVENTS(&look);
look.data = (void *)(&ovents);
return find_in_ktree(ovents_root, &look, ctx);
}
K_ITEM *last_ovents(char *key, K_TREE_CTX *ctx)
{
K_TREE_CTX ctx0[1];
OVENTS ovents;
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
STRNCPY(ovents.key, key);
ovents.hour = SEC_TO_HOUR(DATE_S_EOT);
copy_tv(&(ovents.expirydate), &default_expiry);
INIT_OVENTS(&look);
look.data = (void *)(&ovents);
return find_before_in_ktree(ovents_root, &look, ctx);
}
enum was_alert {
ALERT_NO,
ALERT_LO,
ALERT_HI
};
// ovents must be W locked and event_limits R locked
static enum was_alert check_one_ovent(int id, char *key, tv_t *now,
int low_time, int low_time_limit,
int hi_time, int hi_time_limit)
{
K_TREE_CTX ctx[1];
K_ITEM *o_item = NULL, *tmp_item;
OVENTS *ovents = NULL;
int low_count, hi_count, hour, min;
enum was_alert ret = ALERT_NO;
bool alert = false;
o_item = last_ovents(key, ctx);
if (!o_item)
return ret;
low_count = hi_count = 0;
hour = TV_TO_HOUR(now);
DATA_OVENTS(ovents, o_item);
while (o_item && strcmp(key, ovents->key) == 0 &&
CURRENT(&(ovents->expirydate))) {
// Is this event too old?
if (((hour * 3600) - ((ovents->hour + 1) * 3600)) >
o_limits_max_lifetime) {
tmp_item = o_item;
// Get the prev event
o_item = prev_in_ktree(ctx);
DATA_OVENTS_NULL(ovents, o_item);
// Discard the old event
remove_from_ktree(ovents_root, tmp_item);
k_unlink_item(ovents_store, tmp_item);
k_add_head(ovents_free, tmp_item);
continue;
}
if (alert == false) {
min = hour * 3600 - low_time - ovents->hour * 3600;
if (min < 0)
min = 0;
while (min < 60) {
low_count += ovents->count[IDMIN(id, min)];
min++;
}
if (low_count > low_time_limit) {
alert = true;
ret = ALERT_LO;
}
}
if (alert == false) {
min = hour * 3600 - hi_time - ovents->hour * 3600;
if (min < 0)
min = 0;
while (min < 60) {
hi_count += ovents->count[IDMIN(id, min)];
min++;
}
if (hi_count > hi_time_limit) {
alert = true;
ret = ALERT_HI;
}
}
o_item = prev_in_ktree(ctx);
DATA_OVENTS_NULL(ovents, o_item);
}
return ret;
}
// return OVENT_OK or +timeout seconds
int check_ovents(int id, char *u_key, char *i_key, char *c_key, tv_t *now)
{
K_ITEM *i_item;
enum was_alert was;
bool alert = false;
char *st = NULL;
enum event_cause cause = CAUSE_NONE;
char cmd[MAX_ALERT_CMD+1];
int tyme, limit, lifetime;
char name[TXT_SML+1];
pid_t pid;
K_RLOCK(event_limits_free);
if (ckdb_alert_cmd)
STRNCPY(cmd, ckdb_alert_cmd);
else
cmd[0] = '\0';
K_RUNLOCK(event_limits_free);
// No way to send an alert, so don't test
if (!cmd[0])
return OVENT_OK;
if (i_key[0]) {
K_RLOCK(ips_free);
i_item = find_ips(IPS_GROUP_OK, i_key);
if (!i_item)
i_item = find_ips(IPS_GROUP_OK, i_key);
K_RUNLOCK(ips_free);
if (i_item)
return OVENT_OK;
}
K_WLOCK(events_free);
K_RLOCK(event_limits_free);
if (u_key[0]) {
was = check_one_ovent(id, u_key, now,
o_limits[id].user_low_time,
o_limits[id].user_low_time_limit,
o_limits[id].user_hi_time,
o_limits[id].user_hi_time_limit);
if (was != ALERT_NO) {
alert = true;
if (was == ALERT_LO) {
cause = CAUSE_USER_LO;
tyme = o_limits[id].user_low_time;
limit = o_limits[id].user_low_time_limit;
} else {
cause = CAUSE_USER_HI;
tyme = o_limits[id].user_hi_time;
limit = o_limits[id].user_hi_time_limit;
}
lifetime = o_limits[id].lifetime;
STRNCPY(name, o_limits[id].name);
}
}
/* If we already have the alert, the check_one_ovent()
* cleanup isn't needed since the first call cleans up all */
if (alert == false && i_key[0]) {
was = check_one_ovent(id, i_key, now,
o_limits[id].ip_low_time,
o_limits[id].ip_low_time_limit,
o_limits[id].ip_hi_time,
o_limits[id].ip_hi_time_limit);
if (was != ALERT_NO) {
alert = true;
if (was == ALERT_LO) {
cause = CAUSE_IP_LO;
tyme = o_limits[id].ip_low_time;
limit = o_limits[id].ip_low_time_limit;
} else {
cause = CAUSE_IP_HI;
tyme = o_limits[id].ip_hi_time;
limit = o_limits[id].ip_hi_time_limit;
}
lifetime = o_limits[id].lifetime;
STRNCPY(name, o_limits[id].name);
}
}
if (alert == false && c_key[0]) {
was = check_one_ovent(id, c_key, now,
o_limits[id].ip_low_time,
(int)(ovent_limits_ipc_factor *
(double)(o_limits[id].ip_low_time_limit)),
o_limits[id].ip_hi_time,
(int)(ovent_limits_ipc_factor *
(double)(o_limits[id].ip_hi_time_limit)));
if (was != ALERT_NO) {
alert = true;
if (was == ALERT_LO) {
cause = CAUSE_IPC_LO;
tyme = o_limits[id].ip_low_time;
limit = (int)(ovent_limits_ipc_factor *
(double)(o_limits[id].ip_low_time_limit));
} else {
cause = CAUSE_IPC_HI;
tyme = o_limits[id].ip_hi_time;
limit = (int)(ovent_limits_ipc_factor *
(double)(o_limits[id].ip_hi_time_limit));
}
lifetime = o_limits[id].lifetime;
STRNCPY(name, o_limits[id].name);
}
}
K_RUNLOCK(event_limits_free);
K_WUNLOCK(events_free);
if (alert) {
LOGERR("%s() OLERT ID:%d %s Lim:%d Time:%d Life:%d '%s/%s' "
"'%s' '%s'", __func__,
id, name, limit, tyme, lifetime, i_key, c_key,
st = safe_text(u_key), cause_str(cause));
FREENULL(st);
if (!i_key[0]) {
LOGERR("%s() OLERT ID:%d '%s' can't ban, no IP",
__func__, id, st = safe_text(u_key));
FREENULL(st);
} else {
ips_add(IPS_GROUP_BAN, i_key, (char *)cause_str(cause),
true, false, lifetime, false);
pid = fork();
if (pid < 0) {
LOGERR("%s() OLERT failed to fork (%d)",
__func__, errno);
} else {
if (pid == 0) {
char buf1[16], buf2[16];
char buf3[16], buf4[16];
snprintf(buf1, sizeof(buf1),
"%d", id);
snprintf(buf2, sizeof(buf2),
"%d", limit);
snprintf(buf3, sizeof(buf3),
"%d", tyme);
snprintf(buf4, sizeof(buf4),
"%d", lifetime);
st = safe_text_nonull(u_key);
execl(cmd, cmd, buf1, name, buf2, buf3,
buf4, i_key, st,
cause_str(cause), NULL);
LOGERR("%s() OLERT fork failed to "
"execute (%d)",
__func__, errno);
FREENULL(st);
exit(0);
}
}
}
return lifetime;
}
return OVENT_OK;
}
// order by userid asc,createdate asc,authid asc,expirydate desc // order by userid asc,createdate asc,authid asc,expirydate desc
cmp_t cmp_auths(K_ITEM *a, K_ITEM *b) cmp_t cmp_auths(K_ITEM *a, K_ITEM *b)
{ {

377
src/ckdb_dbio.c

@ -2453,10 +2453,11 @@ void oc_diff_percent(OPTIONCONTROL *oc, __maybe_unused const char *from)
} }
/* An event limit setting looks like: /* An event limit setting looks like:
* OC_LIMITS + events_limits.name + '_' + Item * OC_LIMITS + event_limits.name + '_' + Item
* Item is one of the field names in event_limits * Item is one of the field names in event_limits
* e.g. user_low_time, user_low_time_limit etc * e.g. user_low_time, user_low_time_limit etc
* as below in the if tests */ * as below in the if tests
* lifetime values can't = -EVENT_OK */
void oc_event_limits(OPTIONCONTROL *oc, const char *from) void oc_event_limits(OPTIONCONTROL *oc, const char *from)
{ {
bool processed = false; bool processed = false;
@ -2490,7 +2491,14 @@ void oc_event_limits(OPTIONCONTROL *oc, const char *from)
} else if (strcmp(ptr2, "ip_hi_time_limit") == 0) { } else if (strcmp(ptr2, "ip_hi_time_limit") == 0) {
e_limits[i].ip_hi_time_limit = val; e_limits[i].ip_hi_time_limit = val;
} else if (strcmp(ptr2, "lifetime") == 0) { } else if (strcmp(ptr2, "lifetime") == 0) {
if (val != -(EVENT_OK))
e_limits[i].lifetime = val; e_limits[i].lifetime = val;
else {
LOGERR("%s(%s): ERR: lifetime can't be"
" %d in '%s'",
from, __func__, -(EVENT_OK),
oc->optionname);
}
} else { } else {
LOGERR("%s(%s): ERR: Unknown %s item '%s' " LOGERR("%s(%s): ERR: Unknown %s item '%s' "
"in '%s'", "in '%s'",
@ -2503,7 +2511,14 @@ void oc_event_limits(OPTIONCONTROL *oc, const char *from)
} }
if (!processed) { if (!processed) {
if (strcmp(ptr, "hash_lifetime") == 0) { if (strcmp(ptr, "hash_lifetime") == 0) {
if (val != -(EVENT_OK))
event_limits_hash_lifetime = val; event_limits_hash_lifetime = val;
else {
LOGERR("%s(%s): ERR: lifetime can't be"
" %d in '%s'",
from, __func__, -(EVENT_OK),
oc->optionname);
}
processed = true; processed = true;
} }
} }
@ -2514,6 +2529,85 @@ void oc_event_limits(OPTIONCONTROL *oc, const char *from)
} }
} }
/* An ovent limit setting looks like:
* OC_OLIMITS + event_limits.name + '_' + Item
* Item is one of the field names in event_limits
* e.g. user_low_time, user_low_time_limit etc
* as below in the if tests
* lifetime values can't = OVENT_OK */
void oc_ovent_limits(OPTIONCONTROL *oc, const char *from)
{
bool processed = false, lifetime_changed = false;
size_t len;
char *ptr, *ptr2;
int val, i;
K_WLOCK(event_limits_free);
val = atoi(oc->optionvalue);
ptr = oc->optionname + strlen(OC_OLIMITS);
i = -1;
while (o_limits[++i].name) {
len = strlen(o_limits[i].name);
if (strncmp(ptr, o_limits[i].name, len) == 0 &&
ptr[len] == '_') {
ptr2 = ptr + len + 1;
if (strcmp(ptr2, "user_low_time") == 0) {
o_limits[i].user_low_time = val;
} else if (strcmp(ptr2, "user_low_time_limit") == 0) {
o_limits[i].user_low_time_limit = val;
} else if (strcmp(ptr2, "user_hi_time") == 0) {
o_limits[i].user_hi_time = val;
} else if (strcmp(ptr2, "user_hi_time_limit") == 0) {
o_limits[i].user_hi_time_limit = val;
} else if (strcmp(ptr2, "ip_low_time") == 0) {
o_limits[i].ip_low_time = val;
} else if (strcmp(ptr2, "ip_low_time_limit") == 0) {
o_limits[i].ip_low_time_limit = val;
} else if (strcmp(ptr2, "ip_hi_time") == 0) {
o_limits[i].ip_hi_time = val;
} else if (strcmp(ptr2, "ip_hi_time_limit") == 0) {
o_limits[i].ip_hi_time_limit = val;
} else if (strcmp(ptr2, "lifetime") == 0) {
if (val != OVENT_OK) {
o_limits[i].lifetime = val;
lifetime_changed = true;
} else {
LOGERR("%s(%s): ERR: lifetime can't be"
" %d in '%s'",
from, __func__, OVENT_OK,
oc->optionname);
}
} else {
LOGERR("%s(%s): ERR: Unknown %s item '%s' "
"in '%s'",
from, __func__, OC_OLIMITS, ptr2,
oc->optionname);
}
processed = true;
break;
}
}
if (!processed) {
if (strcmp(ptr, "ipc_factor") == 0) {
ovent_limits_ipc_factor = atof(oc->optionvalue);
processed = true;
}
}
if (lifetime_changed) {
o_limits_max_lifetime = -1;
i = -1;
while (o_limits[++i].name) {
if (o_limits_max_lifetime < o_limits[i].lifetime)
o_limits_max_lifetime = o_limits[i].lifetime;
}
}
K_WUNLOCK(event_limits_free);
if (!processed) {
LOGERR("%s(%s): ERR: Unknown %s name '%s'",
from, __func__, OC_OLIMITS, oc->optionname);
}
}
/* IPS for IPS_GROUP_OK/BAN look like: /* IPS for IPS_GROUP_OK/BAN look like:
* optionname: (OC_IPS_OK or OC_IPS_BAN) + description * optionname: (OC_IPS_OK or OC_IPS_BAN) + description
* optionvalue: is the IP address * optionvalue: is the IP address
@ -2538,6 +2632,7 @@ OC_TRIGGER oc_trigger[] = {
{ SWITCH_STATE_NAME, true, oc_switch_state }, { SWITCH_STATE_NAME, true, oc_switch_state },
{ DIFF_PERCENT_NAME, true, oc_diff_percent }, { DIFF_PERCENT_NAME, true, oc_diff_percent },
{ OC_LIMITS, false, oc_event_limits }, { OC_LIMITS, false, oc_event_limits },
{ OC_OLIMITS, false, oc_ovent_limits },
{ OC_IPS, false, oc_ips }, { OC_IPS, false, oc_ips },
{ NULL, 0, NULL } { NULL, 0, NULL }
}; };
@ -2769,8 +2864,10 @@ bool optioncontrol_fill(PGconn *conn)
PGresult *res; PGresult *res;
K_ITEM *item; K_ITEM *item;
OPTIONCONTROL *row; OPTIONCONTROL *row;
K_TREE_CTX ctx[1];
IPS *ips;
char *params[1]; char *params[1];
int n, i, par = 0; int n, i, par = 0, ban_count, ok_count;
char *field; char *field;
char *sel; char *sel;
int fields = 4; int fields = 4;
@ -2859,6 +2956,25 @@ bool optioncontrol_fill(PGconn *conn)
LOGWARNING("%s(): loaded %d optioncontrol records", __func__, n); LOGWARNING("%s(): loaded %d optioncontrol records", __func__, n);
LOGWARNING("%s() switch_state initially %d", LOGWARNING("%s() switch_state initially %d",
__func__, switch_state); __func__, switch_state);
ok_count = ban_count = 0;
K_RLOCK(ips_free);
item = first_in_ktree(ips_root, ctx);
while (item) {
DATA_IPS(ips, item);
if (CURRENT(&(ips->expirydate))) {
if (strcmp(ips->group, IPS_GROUP_OK) == 0)
ok_count++;
else if (strcmp(ips->group, IPS_GROUP_BAN) == 0)
ban_count++;
}
item = next_in_ktree(ctx);
}
K_RUNLOCK(ips_free);
LOGWARNING("%s() IPS: %s:%d %s:%d",
__func__, IPS_GROUP_OK, ok_count,
IPS_GROUP_BAN, ban_count);
} }
return ok; return ok;
@ -6274,63 +6390,6 @@ bool payouts_fill(PGconn *conn)
return ok; return ok;
} }
void ips_add(char *group, char *ip, char *des, bool log, bool cclass, int life,
bool locked)
{
K_ITEM *i_item, *i2_item;
IPS *ips, *ips2;
char *dot;
tv_t now;
bool ok;
setnow(&now);
if (!locked)
K_WLOCK(ips_free);
i_item = k_unlink_head(ips_free);
DATA_IPS(ips, i_item);
STRNCPY(ips->group, group);
STRNCPY(ips->ip, ip);
ips->lifetime = life;
if (des) {
ips->description = strdup(des);
if (!ips->description)
quithere(1, "strdup OOM");
LIST_MEM_ADD(ips_free, ips->description);
}
ips->log = log;
HISTORYDATEDEFAULT(ips, &now);
add_to_ktree(ips_root, i_item);
k_add_head(ips_store, i_item);
if (cclass) {
i2_item = k_unlink_head(ips_free);
DATA_IPS(ips2, i2_item);
memcpy(ips2, ips, sizeof(*ips2));
ok = false;
dot = strchr(ips->ip, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
*dot = '\0';
ok = true;
}
}
}
if (ok) {
if (des) {
ips2->description = strdup(des);
LIST_MEM_ADD(ips_free, ips2->description);
}
add_to_ktree(ips_root, i2_item);
k_add_head(ips_store, i2_item);
} else
k_add_head(ips_free, i2_item);
}
if (!locked)
K_WUNLOCK(ips_free);
}
// trf_root overrides by,inet,cd fields // trf_root overrides by,inet,cd fields
int _events_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root) int _events_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root)
{ {
@ -6392,7 +6451,7 @@ int _events_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root)
if (dot) { if (dot) {
dot = strchr(dot+1, '.'); dot = strchr(dot+1, '.');
if (dot) if (dot)
*dot = '\0'; *(dot+1) = '\0';
} }
} }
} }
@ -6444,6 +6503,202 @@ int _events_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root)
return check_events(&events); return check_events(&events);
} }
// trf_root overrides by,inet,cd fields
int _ovents_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root)
{
K_ITEM *o_item, *i_webtime, *i_username;
OVENTS ovents, *d_ovents;
char reply[1024] = "";
size_t siz = sizeof(reply);
char u_key[TXT_SML+1], i_key[TXT_SML+1], c_key[TXT_SML+1];
int hour, min;
bool gotc;
LOGDEBUG("%s(): add", __func__);
bzero(&ovents, sizeof(ovents));
// Default to now if not specified
setnow(&(ovents.createdate));
if (by)
STRNCPY(ovents.createby, by);
if (inet)
STRNCPY(ovents.createinet, inet);
if (cd)
copy_tv(&(ovents.createdate), cd);
// trf_root values overrides parameters
HISTORYDATETRANSFER(trf_root, &ovents);
// username overrides createby
i_username = optional_name(trf_root, "username", 1, NULL,
reply, siz);
if (i_username)
STRNCPY(ovents.createby, transfer_data(i_username));
// webtime overrides
i_webtime = optional_name(trf_root, "webtime", 1, NULL,
reply, siz);
if (i_webtime) {
TXT_TO_CTV("webtime", transfer_data(i_webtime),
ovents.createdate);
}
STRNCPY(u_key, ovents.createby);
STRNCPY(i_key, ovents.createinet);
gotc = false;
if (i_key[0]) {
char *dot;
STRNCPY(c_key, i_key);
dot = strchr(c_key, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
*(dot+1) = '\0';
gotc = true;
}
}
}
}
if (!gotc)
c_key[0] = '\0';
if (!u_key[0] && !i_key[0] && !c_key[0])
return OVENT_OK;
hour = TV_TO_HOUR(&(ovents.createdate));
min = TV_TO_MIN(&(ovents.createdate));
K_WLOCK(ovents_free);
if (u_key[0]) {
o_item = find_ovents(u_key, hour, NULL);
if (o_item) {
DATA_OVENTS(d_ovents, o_item);
d_ovents->count[IDMIN(id, min)]++;
} else {
o_item = k_unlink_head(ovents_free);
DATA_OVENTS(d_ovents, o_item);
bzero(d_ovents, sizeof(*d_ovents));
STRNCPY(d_ovents->key, u_key);
d_ovents->hour = hour;
d_ovents->count[IDMIN(id, min)]++;
copy_tv(&(d_ovents->createdate), &(ovents.createdate));
STRNCPY(d_ovents->createby, ovents.createby);
STRNCPY(d_ovents->createinet, ovents.createinet);
copy_tv(&(d_ovents->expirydate), &default_expiry);
k_add_head(ovents_store, o_item);
add_to_ktree(ovents_root, o_item);
}
}
if (i_key[0]) {
o_item = find_ovents(i_key, hour, NULL);
if (o_item) {
DATA_OVENTS(d_ovents, o_item);
d_ovents->count[IDMIN(id, min)]++;
} else {
o_item = k_unlink_head(ovents_free);
DATA_OVENTS(d_ovents, o_item);
bzero(d_ovents, sizeof(*d_ovents));
STRNCPY(d_ovents->key, i_key);
d_ovents->hour = hour;
d_ovents->count[IDMIN(id, min)]++;
copy_tv(&(d_ovents->createdate), &(ovents.createdate));
STRNCPY(d_ovents->createby, ovents.createby);
STRNCPY(d_ovents->createinet, ovents.createinet);
copy_tv(&(d_ovents->expirydate), &default_expiry);
k_add_head(ovents_store, o_item);
add_to_ktree(ovents_root, o_item);
}
}
if (c_key[0]) {
o_item = find_ovents(c_key, hour, NULL);
if (o_item) {
DATA_OVENTS(d_ovents, o_item);
d_ovents->count[IDMIN(id, min)]++;
} else {
o_item = k_unlink_head(ovents_free);
DATA_OVENTS(d_ovents, o_item);
bzero(d_ovents, sizeof(*d_ovents));
STRNCPY(d_ovents->key, c_key);
d_ovents->hour = hour;
d_ovents->count[IDMIN(id, min)]++;
copy_tv(&(d_ovents->createdate), &(ovents.createdate));
STRNCPY(d_ovents->createby, ovents.createby);
STRNCPY(d_ovents->createinet, ovents.createinet);
copy_tv(&(d_ovents->expirydate), &default_expiry);
k_add_head(ovents_store, o_item);
add_to_ktree(ovents_root, o_item);
}
}
K_WUNLOCK(ovents_free);
return check_ovents(id, u_key, i_key, c_key, &(ovents.createdate));
}
void ips_add(char *group, char *ip, char *des, bool log, bool cclass, int life,
bool locked)
{
K_ITEM *i_item, *i2_item;
IPS *ips, *ips2;
char *dot;
tv_t now;
bool ok;
setnow(&now);
if (!locked)
K_WLOCK(ips_free);
i_item = k_unlink_head(ips_free);
DATA_IPS(ips, i_item);
STRNCPY(ips->group, group);
STRNCPY(ips->ip, ip);
ips->lifetime = life;
if (des) {
ips->description = strdup(des);
if (!ips->description)
quithere(1, "strdup OOM");
LIST_MEM_ADD(ips_free, ips->description);
}
ips->log = log;
HISTORYDATEDEFAULT(ips, &now);
add_to_ktree(ips_root, i_item);
k_add_head(ips_store, i_item);
if (cclass) {
i2_item = k_unlink_head(ips_free);
DATA_IPS(ips2, i2_item);
memcpy(ips2, ips, sizeof(*ips2));
ok = false;
dot = strchr(ips->ip, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
*(dot+1) = '\0';
ok = true;
}
}
}
if (ok) {
if (des) {
ips2->description = strdup(des);
LIST_MEM_ADD(ips_free, ips2->description);
}
add_to_ktree(ips_root, i2_item);
k_add_head(ips_store, i2_item);
} else
k_add_head(ips_free, i2_item);
}
if (!locked)
K_WUNLOCK(ips_free);
}
// TODO: discard them from RAM // TODO: discard them from RAM
bool auths_add(PGconn *conn, char *poolinstance, char *username, bool auths_add(PGconn *conn, char *poolinstance, char *username,
char *workername, char *clientid, char *enonce1, char *workername, char *clientid, char *enonce1,

Loading…
Cancel
Save