Browse Source

ckdb - allow blocking and script alerting on ckdb events

master
kanoi 9 years ago
parent
commit
3aff67018f
  1. 3
      pool/base.php
  2. 1
      pool/db.php
  3. 26
      pool/page.php
  4. 1
      pool/page_api.php
  5. 1
      pool/page_reg.php
  6. 1
      pool/page_reset.php
  7. 20
      pool/socket.php
  8. 120
      src/ckdb.c
  9. 164
      src/ckdb.h
  10. 108
      src/ckdb_cmd.c
  11. 536
      src/ckdb_data.c
  12. 180
      src/ckdb_dbio.c

3
pool/base.php

@ -6,6 +6,9 @@ global $dbg, $dbgstr;
$dbg = false;
$dbgstr = '';
#
global $alrts;
$alrts = array();
#
function adddbg($str)
{
global $dbg, $dbgstr;

1
pool/db.php

@ -1,7 +1,6 @@
<?php
#
include_once('socket.php');
include_once('base.php');
#
# List of db functions to call and get the results back from ckdb
# From homeInfo() and the rest after that

26
pool/page.php

@ -180,7 +180,7 @@ function pghead($css_marker, $script_marker, $name)
return $head;
}
#
function pgtop($info, $dotop, $user, $douser)
function pgtop($alert_marker, $info, $dotop, $user, $douser)
{
global $site_title, $loginfailed;
@ -342,6 +342,7 @@ function pgtop($info, $dotop, $user, $douser)
$top .= " You need to enable javascript to use";
$top .= " the $site_title web site.</div></noscript>";
$top .= $alert_marker;
if ($loginfailed === true)
$top .= '<div class=accwarn>Login Failed</div>';
if (isset($info['u_nopayaddr']))
@ -507,7 +508,7 @@ function pgmenu($menus)
return $ret;
}
#
function pgbody($info, $page, $menu, $dotop, $user, $douser)
function pgbody($alert_marker, $info, $page, $menu, $dotop, $user, $douser)
{
$body = '<body';
if ($page == 'index')
@ -519,7 +520,7 @@ function pgbody($info, $page, $menu, $dotop, $user, $douser)
$body .= '<table border=0 cellpadding=0 cellspacing=0 width=94%>';
$body .= '<tr><td>';
$body .= pgtop($info, $dotop, $user, $douser);
$body .= pgtop($alert_marker, $info, $dotop, $user, $douser);
$body .= '</td></tr>';
$body .= '<tr><td>';
@ -531,7 +532,7 @@ function pgbody($info, $page, $menu, $dotop, $user, $douser)
return $body;
}
#
function pgfoot($info)
function pgfoot($elapsed_marker, $info)
{
global $stt;
$foot = '</div></td></tr>';
@ -555,7 +556,8 @@ function pgfoot($info)
$now = date('Y');
if ($now != '2014')
$foot .= "-$now";
$foot .= '&nbsp;<span class=ft>Z/s</span></span><span class=ftr id=ftr>&nbsp;</span></div>';
$foot .= '&nbsp;<span class=ft>' . $elapsed_marker;
$foot .= '</span></span><span class=ftr id=ftr>&nbsp;</span></div>';
$foot .= "<script type='text/javascript'>jst();tim();mini();</script></body></html>\n";
return $foot;
@ -565,10 +567,13 @@ function gopage($info, $data, $pagefun, $page, $menu, $name, $user, $ispage = tr
{
global $dbg, $stt;
global $page_css, $page_scripts;
global $alrts;
$dbg_marker = '[@dbg@]';
$css_marker = '[@css@]';
$script_marker = '[@scripts@]';
$alert_marker = '[@alert@]';
$elapsed_marker = '[@elapsed@]';
if ($dbg === true)
$pg = $dbg_marker.'<br>';
@ -590,8 +595,8 @@ function gopage($info, $data, $pagefun, $page, $menu, $name, $user, $ispage = tr
// unset($_SESSION['logkey']);
$head = pghead($css_marker, $script_marker, $name);
$body = pgbody($info, $page, $menu, $dotop, $user, $douser);
$foot = pgfoot($info);
$body = pgbody($alert_marker, $info, $page, $menu, $dotop, $user, $douser);
$foot = pgfoot($elapsed_marker, $info);
if ($dbg === true)
$pg = str_replace($dbg_marker, cvtdbg(), $pg);
@ -603,6 +608,11 @@ function gopage($info, $data, $pagefun, $page, $menu, $name, $user, $ispage = tr
$head = str_replace($script_marker, $page_scripts, $head);
$alertstr = '';
foreach ($alrts as $str => $num)
$alertstr .= "<div class=accwarn>$str</div>";
$body = str_replace($alert_marker, $alertstr, $body);
$all = $head;
$all .= trm_force($body);
$all .= trm($pg);
@ -612,7 +622,7 @@ function gopage($info, $data, $pagefun, $page, $menu, $name, $user, $ispage = tr
else
$elapsed = microtime(true) - $stt;
$foot = trm_force(str_replace('Z/', number_format($elapsed, 4), $foot));
$foot = trm_force(str_replace($elapsed_marker, number_format($elapsed, 4).'s', $foot));
usleep(100000);

1
pool/page_api.php

@ -23,6 +23,7 @@ function show_api($info, $page, $menu, $name, $user)
$ans = getAtts($u, 'KAPIKey.str');
if ($ans['STATUS'] != 'ok')
no_api($jfu);
# TODO: pass $api to ckdb to produce an invalid Key event
if (!isset($ans['KAPIKey.str']))
no_api($jfu);
if ($ans['KAPIKey.str'] != $api)

1
pool/page_reg.php

@ -1,6 +1,5 @@
<?php
#
include_once('socket.php');
include_once('email.php');
#
function doregres($data, $u)

1
pool/page_reset.php

@ -1,6 +1,5 @@
<?php
#
include_once('socket.php');
include_once('email.php');
#
function allow_reset($error)

20
pool/socket.php

@ -193,8 +193,13 @@ function sendsock($fun, $msg, $tmo = false)
# and the data $msg to send to ckdb
# and it returns $ret = false on error or $ret = the string reply
#
# Alerts are always tagged on the end as: $fld_sep alert $val_sep text
# There's allowed to be more than one. They are removed
#
function sendsockreply($fun, $msg, $tmo = false)
{
global $fld_sep, $val_sep, $alrts;
$ret = false;
$socket = getsock($fun, $tmo);
if ($socket !== false)
@ -205,6 +210,21 @@ function sendsockreply($fun, $msg, $tmo = false)
socket_close($socket);
}
$al = $fld_sep . 'alert' . $val_sep;
if ($ret !== false and strpos($ret, $al) !== false)
{
$all = explode($al, $ret);
$ret = $all[0];
$skip = true;
foreach ($all as $lrt)
{
if ($skip)
$skip = false;
else
// Discard duplicates
$alrts[preg_replace("/[\n\r]*$/",'',$lrt)] = 1;
}
}
return $ret;
}
#

120
src/ckdb.c

@ -286,6 +286,9 @@ static cklock_t fpm_lock;
static char *first_pool_message;
static sem_t socketer_sem;
// command called for any ckdb alerts
char *ckdb_alert_cmd = NULL;
char *btc_server = "http://127.0.0.1:8330";
char *btc_auth;
int btc_timeout = 5;
@ -477,12 +480,46 @@ K_STORE *payouts_store;
// Emulate a list for lock checking
K_LIST *process_pplns_free;
/*
// EVENTLOG
K_TREE *eventlog_root;
K_LIST *eventlog_free;
K_STORE *eventlog_store;
*/
// IPS
K_TREE *ips_root;
K_LIST *ips_free;
K_STORE *ips_store;
// EVENTS
K_TREE *events_user_root;
K_TREE *events_ip_root;
K_TREE *events_ipc_root;
K_TREE *events_hash_root;
K_LIST *events_free;
K_STORE *events_store;
EVENT_LIMITS e_limits[] = {
{ EVENTID_PASSFAIL, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// It's only possible to create an address account once, so user_lo/hi can never trigger
{ EVENTID_CREADDR, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// It's only possible to create an account once, so user_lo/hi can never trigger
{ EVENTID_CREACC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// page_api.php with an invalid username
{ EVENTID_UNKATTS, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// 2fa missing/invalid format
{ EVENTID_INV2FA, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Wrong 2fa value
{ EVENTID_WRONG2FA, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Invalid address according to btcd
{ EVENTID_INVBTC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Incorrect format/length address
{ EVENTID_INCBTC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Address belongs to some other account
{ EVENTID_BTCUSED, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// It's only possible to create an account once, so user_lo/hi can never trigger
{ EVENTID_AUTOACC, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Invalid user on auth, CKPool will throttle these
{ EVENTID_INVAUTH, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 },
// Invalid user on chkpass
{ EVENTID_INVUSER, 60, 1, 2*60, 2, 60, 1, 2*60, 2, 24*60*60 }
};
int event_limits_hash_lifetime = 24*60*60;
// AUTHS authorise.id.json={...}
K_TREE *auths_root;
@ -1169,6 +1206,19 @@ static void alloc_storage()
payouts_wid_root = new_ktree("PayoutsWId", cmp_payouts_wid,
payouts_free);
ips_free = k_new_list("IPs", sizeof(IPS), ALLOC_IPS, LIMIT_IPS, true);
ips_store = k_new_store(ips_free);
ips_root = new_ktree(NULL, cmp_ips, ips_free);
ips_add(IPS_GROUP_OK, "127.0.0.1", "local", false, true, 0);
events_free = k_new_list("Events", sizeof(EVENTS),
ALLOC_EVENTS, LIMIT_EVENTS, true);
events_store = k_new_store(events_free);
events_user_root = new_ktree(NULL, cmp_events_user, events_free);
events_ip_root = new_ktree(NULL, cmp_events_ip, events_free);
events_ipc_root = new_ktree(NULL, cmp_events_ipc, events_free);
events_hash_root = new_ktree(NULL, cmp_events_hash, events_free);
auths_free = k_new_list("Auths", sizeof(AUTHS),
ALLOC_AUTHS, LIMIT_AUTHS, true);
auths_store = k_new_store(auths_free);
@ -1250,6 +1300,9 @@ static void alloc_storage()
DLPRIO(userinfo, 50);
// Needs to check users and ips
DLPRIO(events, 47);
DLPRIO(auths, 44);
DLPRIO(users, 43);
DLPRIO(useratts, 42);
@ -1272,6 +1325,7 @@ static void alloc_storage()
DLPRIO(idcontrol, PRIO_TERMINAL);
DLPRIO(optioncontrol, PRIO_TERMINAL);
DLPRIO(paymentaddresses, PRIO_TERMINAL);
DLPRIO(ips, PRIO_TERMINAL);
DLPCHECK();
@ -1459,6 +1513,10 @@ static void dealloc_storage()
LOGWARNING("%s() poolstats ...", __func__);
FREE_ALL(poolstats);
FREE_TREE(events_user);
FREE_TREE(events_ip);
FREE_LISTS(events);
FREE_ALL(ips);
FREE_ALL(auths);
FREE_TREE(payouts_wid);
@ -2675,9 +2733,10 @@ static enum cmd_values breakdown(K_ITEM **ml_item, char *buf, tv_t *now,
TRANSFER *transfer;
K_TREE_CTX ctx[1];
MSGLINE *msgline;
K_ITEM *t_item = NULL, *cd_item = NULL, *seqall;
K_ITEM *t_item = NULL, *cd_item = NULL, *seqall, *i_item;
IPS *ips;
char *cmdptr, *idptr, *next, *eq, *end, *was;
char *data = NULL, *st = NULL, *st2 = NULL;
char *data = NULL, *st = NULL, *st2 = NULL, *ip = NULL;
bool noid = false;
size_t siz;
@ -2939,6 +2998,8 @@ static enum cmd_values breakdown(K_ITEM **ml_item, char *buf, tv_t *now,
k_add_head(transfer_free, t_item);
K_WUNLOCK(transfer_free);
} else {
if (strcmp(data, INETTRF) == 0)
ip = transfer->mvalue;
add_to_ktree_nolock(msgline->trf_root, t_item);
k_add_head_nolock(msgline->trf_store, t_item);
}
@ -2991,6 +3052,30 @@ static enum cmd_values breakdown(K_ITEM **ml_item, char *buf, tv_t *now,
}
}
free(cmdptr);
if (!seqall && ip) {
bool alert = false;
K_WLOCK(ips_free);
i_item = find_ips(IPS_GROUP_BAN, ip);
if (i_item) {
DATA_IPS(ips, i_item);
// Has the ban expired?
if ((int)tvdiff(now, &(ips->createdate)) > ips->lifetime) {
remove_from_ktree(ips_root, i_item);
k_unlink_item(ips_store, i_item);
if (ips->description) {
LIST_MEM_SUB(ips_free, ips->description);
FREENULL(ips->description);
}
k_add_head(ips_free, i_item);
} else
alert = true;
}
K_WUNLOCK(ips_free);
if (alert)
return CMD_ALERT;
}
return ckdb_cmds[msgline->which_cmds].cmd_val;
nogood:
if (t_item) {
@ -4113,7 +4198,7 @@ static void *socketer(__maybe_unused void *arg)
proc_instance_t *pi = (proc_instance_t *)arg;
pthread_t clis_pt, blis_pt;
unixsock_t *us = &pi->us;
char *end, *ans = NULL, *rep = NULL, *buf = NULL;
char *end, *ans = NULL, *rep = NULL, *buf = NULL, *tmp;
enum cmd_values cmdnum;
int sockd;
K_ITEM *wq_item = NULL, *ml_item = NULL;
@ -4184,6 +4269,15 @@ static void *socketer(__maybe_unused void *arg)
now.tv_sec);
send_unix_msg(sockd, reply);
break;
case CMD_ALERT:
snprintf(reply, sizeof(reply),
"%s.%ld.failed.ERR",
msgline->id,
now.tv_sec);
tmp = reply_event(EVENTID_MAX, reply);
send_unix_msg(sockd, tmp);
FREENULL(tmp);
break;
case CMD_TERMINATE:
LOGWARNING("Listener received"
" terminate message,"
@ -4506,6 +4600,7 @@ static void reload_line(PGconn *conn, char *filename, uint64_t count, char *buf)
switch (cmdnum) {
// Ignore
case CMD_REPLY:
case CMD_ALERT:
break;
// Shouldn't be there
case CMD_TERMINATE:
@ -5659,6 +5754,8 @@ static void check_restore_dir(char *name)
}
static struct option long_options[] = {
// script to call when alerts happen
{ "alert", required_argument, 0, 'c' },
{ "config", required_argument, 0, 'c' },
{ "dbname", required_argument, 0, 'd' },
{ "free", required_argument, 0, 'f' },
@ -5715,8 +5812,11 @@ int main(int argc, char **argv)
memset(&ckp, 0, sizeof(ckp));
ckp.loglevel = LOG_NOTICE;
while ((c = getopt_long(argc, argv, "c:d:ghi:kl:mM:n:p:P:r:R:s:S:t:u:U:vw:yY:", long_options, &i)) != -1) {
while ((c = getopt_long(argc, argv, "a:c:d:ghi:kl:mM:n:p:P:r:R:s:S:t:u:U:vw:yY:", long_options, &i)) != -1) {
switch(c) {
case 'a':
ckdb_alert_cmd = strdup(optarg);
break;
case 'c':
ckp.config = strdup(optarg);
break;

164
src/ckdb.h

@ -51,7 +51,7 @@
#define DB_VLOCK "1"
#define DB_VERSION "1.0.4"
#define CKDB_VERSION DB_VERSION"-1.923"
#define CKDB_VERSION DB_VERSION"-1.930"
#define WHERE_FFL " - from %s %s() line %d"
#define WHERE_FFL_HERE __FILE__, __func__, __LINE__
@ -353,6 +353,8 @@ extern cklock_t last_lock;
#define STR_SHAREERRORS "shareerror"
#define STR_AGEWORKINFO "ageworkinfo"
extern char *ckdb_alert_cmd;
extern char *btc_server;
extern char *btc_auth;
extern int btc_timeout;
@ -627,6 +629,7 @@ enum cmd_values {
CMD_UNSET,
CMD_DUPSEQ, // Ignore, we've already got it
CMD_REPLY, // Means something was wrong - send back reply
CMD_ALERT, // Means reply with the buf passed
CMD_TERMINATE,
CMD_PING,
CMD_VERSION,
@ -804,6 +807,16 @@ enum cmd_values {
_row->expirydate.tv_usec = default_expiry.tv_usec; \
} while (0)
#define HISTORYDATEDEFAULT(_row, _cd) do { \
_row->createdate.tv_sec = (_cd)->tv_sec; \
_row->createdate.tv_usec = (_cd)->tv_usec; \
STRNCPY(_row->createby, by_default); \
STRNCPY(_row->createcode, (char *)__func__); \
STRNCPY(_row->createinet, inet_default); \
_row->expirydate.tv_sec = default_expiry.tv_sec; \
_row->expirydate.tv_usec = default_expiry.tv_usec; \
} while (0)
/* Override _row defaults if transfer fields are present
* We don't care about the reply so it can be small */
#define HISTORYDATETRANSFER(_root, _row) do { \
@ -815,17 +828,17 @@ enum cmd_values {
__item = optional_name(_root, BYTRF, 1, NULL, __reply, __siz); \
if (__item) { \
DATA_TRANSFER(__transfer, __item); \
STRNCPY(_row->createby, __transfer->mvalue); \
STRNCPY((_row)->createby, __transfer->mvalue); \
} \
__item = optional_name(_root, CODETRF, 1, NULL, __reply, __siz); \
if (__item) { \
DATA_TRANSFER(__transfer, __item); \
STRNCPY(_row->createcode, __transfer->mvalue); \
STRNCPY((_row)->createcode, __transfer->mvalue); \
} \
__item = optional_name(_root, INETTRF, 1, NULL, __reply, __siz); \
if (__item) { \
DATA_TRANSFER(__transfer, __item); \
STRNCPY(_row->createinet, __transfer->mvalue); \
STRNCPY((_row)->createinet, __transfer->mvalue); \
} \
} \
} while (0)
@ -1935,25 +1948,109 @@ extern K_LIST *process_pplns_free;
* UserAtts can also at the user level */
#define SHIFTS_SETTING_NAME "ShiftsPageSize"
/*
// EVENTLOG
typedef struct eventlog {
int64_t eventlogid;
char poolinstance[TXT_BIG+1];
char eventlogcode[TXT_SML+1];
char *eventlogdescription;
// IPS
typedef struct ips {
char group[TXT_SML+1];
char ip[TXT_MED+1];
int lifetime;
bool log;
char *description;
HISTORYDATECONTROLFIELDS;
} EVENTLOG;
#define ALLOC_EVENTLOG 100
#define LIMIT_EVENTLOG 0
#define INIT_EVENTLOG(_item) INIT_GENERIC(_item, eventlog)
#define DATA_EVENTLOG(_var, _item) DATA_GENERIC(_var, _item, eventlog, true)
extern K_TREE *eventlog_root;
extern K_LIST *eventlog_free;
extern K_STORE *eventlog_store;
*/
} IPS;
#define ALLOC_IPS 16
#define LIMIT_IPS 0
#define INIT_IPS(_item) INIT_GENERIC(_item, ips)
#define DATA_IPS(_var, _item) DATA_GENERIC(_var, _item, ips, true)
#define DATA_IPS_NULL(_var, _item) DATA_GENERIC(_var, _item, ips, false)
extern K_TREE *ips_root;
extern K_LIST *ips_free;
extern K_STORE *ips_store;
#define IPS_GROUP_OK "OK"
#define IPS_GROUP_BAD "BAD"
#define IPS_GROUP_BAN "BAN"
// EVENTS RAM only
typedef struct events {
int id;
// class C truncated version of createinet or full IP for IPv6
char ipc[TXT_MED+1];
// check for repeated use of the same hash
char hash[TXT_BIG+1];
// How many trees the item is still in
int trees;
// who: createby, createinet, createdate
HISTORYDATECONTROLFIELDS;
} EVENTS;
#define ALLOC_EVENTS 1000
#define LIMIT_EVENTS 0
#define INIT_EVENTS(_item) INIT_GENERIC(_item, events)
#define DATA_EVENTS(_var, _item) DATA_GENERIC(_var, _item, events, true)
#define DATA_EVENTS_NULL(_var, _item) DATA_GENERIC(_var, _item, events, false)
extern K_TREE *events_user_root;
extern K_TREE *events_ip_root;
extern K_TREE *events_ipc_root;
extern K_TREE *events_hash_root;
extern K_LIST *events_free;
extern K_STORE *events_store;
// Any password failure
#define EVENTID_PASSFAIL 0
// Add/Change address
#define EVENTID_CREADDR 1
// Create an account
#define EVENTID_CREACC 2
// API unkatts
#define EVENTID_UNKATTS 3
// 2FA rubbish
#define EVENTID_INV2FA 4
// 2FA incorrect
#define EVENTID_WRONG2FA 5
// Attempt change to an invalid BTC address (that required btcd checking)
#define EVENTID_INVBTC 6
// Attempt change to an incorrect BTC address (that failed the ckdb test)
#define EVENTID_INCBTC 7
// Attempt change to another used BTC address
#define EVENTID_BTCUSED 8
// Auto create account
#define EVENTID_AUTOACC 9
// Unknown auth username
#define EVENTID_INVAUTH 10
// Unknown chkpass username
#define EVENTID_INVUSER 11
#define EVENTID_MAX 11
#define EVENT_OK -1
/* user limits are checked for matching id+user
* ip limits are checked for matching id+ip,
* however, it requires more than one user found with the given ip
* i.e. a single user will not fail the test result for ip */
typedef struct event_limits {
int id;
int user_low_time;
// how many in above limit = ok (+1 = alert)
int user_low_time_limit;
int user_hi_time;
// how many in above limit = ok (+1 = alert)
int user_hi_time_limit;
int ip_low_time;
// how many in above limit = ok (+1 = alert)
int ip_low_time_limit;
int ip_hi_time;
// how many in above limit = ok (+1 = alert)
int ip_hi_time_limit;
// expire events
int lifetime;
} EVENT_LIMITS;
extern EVENT_LIMITS e_limits[];
// Has a fixed limit of 1 event allowed
extern int event_limits_hash_lifetime;
// AUTHS authorise.id.json={...}
typedef struct auths {
@ -2650,6 +2747,20 @@ extern K_ITEM *find_payoutid(int64_t payoutid);
extern K_ITEM *find_payouts_wid(int64_t workinfoidend, K_TREE_CTX *ctx);
extern double payout_stats(PAYOUTS *payouts, char *statname);
extern bool process_pplns(int32_t height, char *blockhash, tv_t *now);
extern cmp_t cmp_ips(K_ITEM *a, K_ITEM *b);
extern K_ITEM *find_ips(char *group, char *ip);
extern cmp_t cmp_events_user(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_events_ip(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_events_ipc(K_ITEM *a, K_ITEM *b);
extern cmp_t cmp_events_hash(K_ITEM *a, K_ITEM *b);
extern K_ITEM *last_events_user(int id, char *user, K_TREE_CTX *ctx);
extern K_ITEM *last_events_ip(int id, char *ip, K_TREE_CTX *ctx);
extern K_ITEM *last_events_ipc(int id, char *ipc, K_TREE_CTX *ctx);
extern K_ITEM *last_events_hash(int id, char *hash, K_TREE_CTX *ctx);
extern int check_events(EVENTS *events);
extern char *_reply_event(int event, char *buf, bool fre);
#define reply_event(_event, _buf) _reply_event(_event, _buf, false)
#define reply_event_free(_event, _buf) _reply_event(_event, _buf, true)
extern cmp_t cmp_auths(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);
@ -2755,7 +2866,8 @@ extern int64_t nextid(PGconn *conn, char *idname, int64_t increment,
tv_t *cd, char *by, char *code, char *inet);
extern bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
char *newhash, char *email, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root, char *status);
char *inet, tv_t *cd, K_TREE *trf_root, char *status,
int *event);
extern K_ITEM *users_add(PGconn *conn, char *username, char *emailaddress,
char *passwordhash, int64_t userbits, char *by,
char *code, char *inet, tv_t *cd, K_TREE *trf_root);
@ -2857,11 +2969,15 @@ extern bool payouts_add(PGconn *conn, bool add, K_ITEM *p_item,
extern K_ITEM *payouts_full_expire(PGconn *conn, int64_t payoutid, tv_t *now,
bool lock);
extern bool payouts_fill(PGconn *conn);
extern void ips_add(char *group, char *ip, char *des, bool log, bool cclass, int life);
extern int _events_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root);
#define events_add(_id, _trf_root) _events_add(_id, NULL, NULL, NULL, _trf_root)
extern bool auths_add(PGconn *conn, char *poolinstance, char *username,
char *workername, char *clientid, char *enonce1,
char *useragent, char *preauth, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root,
bool addressuser, USERS **users, WORKERS **workers);
bool addressuser, USERS **users, WORKERS **workers,
int *event);
extern bool poolstats_add(PGconn *conn, bool store, char *poolinstance,
char *elapsed, char *users, char *workers,
char *hashrate, char *hashrate5m,

108
src/ckdb_cmd.c

@ -39,6 +39,7 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by,
char reply[1024] = "";
size_t siz = sizeof(reply);
K_ITEM *i_username, *i_emailaddress, *i_passwordhash, *u_item = NULL;
int event = EVENT_OK;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
@ -66,15 +67,18 @@ static char *cmd_adduser(PGconn *conn, char *cmd, char *id, tv_t *now, char *by,
if (!i_passwordhash)
return strdup(reply);
event = events_add(EVENTID_CREACC, trf_root);
if (event == EVENT_OK) {
u_item = users_add(conn, transfer_data(i_username),
transfer_data(i_emailaddress),
transfer_data(i_passwordhash), 0,
by, code, inet, now, trf_root);
}
}
if (!u_item) {
LOGERR("%s() %s.failed.DBE", __func__, id);
return strdup("failed.DBE");
return reply_event(event, "failed.DBE");
}
LOGDEBUG("%s.ok.added %s", id, transfer_data(i_username));
snprintf(reply, siz, "ok.added %s", transfer_data(i_username));
@ -88,6 +92,7 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
K_ITEM *i_username, *i_oldhash, *i_newhash, *i_2fa, *u_item;
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
bool ok = true;
char *oldhash;
int32_t value;
@ -112,8 +117,10 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
}
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt, reply, siz);
if (!i_2fa)
return strdup(reply);
if (!i_2fa) {
event = events_add(EVENTID_INV2FA, trf_root);
return reply_event(event, reply);
}
if (ok) {
i_newhash = require_name(trf_root, "newhash",
@ -131,6 +138,8 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
if (USER_TOTP_ENA(users)) {
value = (int32_t)atoi(transfer_data(i_2fa));
ok = check_2fa(users, value);
if (!ok)
event = events_add(EVENTID_WRONG2FA, trf_root);
}
if (ok) {
ok = users_update(NULL,
@ -140,7 +149,7 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
NULL,
by, code, inet, now,
trf_root,
NULL);
NULL, &event);
}
} else
ok = false;
@ -148,7 +157,7 @@ static char *cmd_newpass(__maybe_unused PGconn *conn, char *cmd, char *id,
if (!ok) {
LOGERR("%s.failed.%s", id, transfer_data(i_username));
return strdup("failed.");
return reply_event(event, "failed.");
}
LOGDEBUG("%s.ok.%s", id, transfer_data(i_username));
return strdup("ok.");
@ -162,6 +171,7 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
K_ITEM *i_username, *i_passwordhash, *i_2fa, *u_item;
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
USERS *users;
bool ok;
@ -177,27 +187,34 @@ static char *cmd_chkpass(__maybe_unused PGconn *conn, char *cmd, char *id,
return strdup(reply);
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt, reply, siz);
if (!i_2fa)
return strdup(reply);
if (!i_2fa) {
event = events_add(EVENTID_INV2FA, trf_root);
return reply_event(event, reply);
}
K_RLOCK(users_free);
u_item = find_users(transfer_data(i_username));
K_RUNLOCK(users_free);
if (!u_item)
if (!u_item) {
event = events_add(EVENTID_INVUSER, trf_root);
ok = false;
else {
} else {
DATA_USERS(users, u_item);
ok = check_hash(users, transfer_data(i_passwordhash));
if (!ok)
event = events_add(EVENTID_PASSFAIL, trf_root);
if (ok && USER_TOTP_ENA(users)) {
uint32_t value = (int32_t)atoi(transfer_data(i_2fa));
ok = check_2fa(users, value);
if (!ok)
event = events_add(EVENTID_WRONG2FA, trf_root);
}
}
if (!ok) {
LOGERR("%s.failed.%s", id, transfer_data(i_username));
return strdup("failed.");
return reply_event(event, "failed.");
}
LOGDEBUG("%s.ok.%s", id, transfer_data(i_username));
return strdup("ok.");
@ -210,6 +227,7 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
K_ITEM *i_username, *i_action, *i_entropy, *i_value, *u_item, *u_new;
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
size_t len, off;
char tmp[1024];
int32_t entropy, value;
@ -241,8 +259,10 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
// Field always expected, use 0 if not required
i_value = require_name(trf_root, "value", 1, (char *)intpatt,
reply, siz);
if (!i_value)
return strdup(reply);
if (!i_value) {
event = events_add(EVENTID_INV2FA, trf_root);
return reply_event(event, reply);
}
K_RLOCK(users_free);
u_item = find_users(transfer_data(i_username));
@ -328,6 +348,7 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
goto dame;
value = (int32_t)atoi(transfer_data(i_value));
if (!check_2fa(users, value)) {
event = events_add(EVENTID_WRONG2FA, trf_root);
sfa_error = "Invalid code";
// Report sfa_error to web
ok = true;
@ -350,6 +371,7 @@ static char *cmd_2fa(__maybe_unused PGconn *conn, char *cmd, char *id,
// remove requires value
value = (int32_t)atoi(transfer_data(i_value));
if (!check_2fa(users, value)) {
event = events_add(EVENTID_WRONG2FA, trf_root);
sfa_error = "Invalid code";
// Report sfa_error to web
ok = true;
@ -428,7 +450,7 @@ dame:
// Only db/php/code errors should get here
LOGERR("%s.failed.%s-%s", id, transfer_data(i_username), action);
FREENULL(buf);
return strdup("failed.");
return reply_event(event, "failed.");
}
snprintf(tmp, sizeof(tmp), "2fa_status=%s%c2fa_error=%s%c2fa_msg=%s",
@ -436,7 +458,7 @@ dame:
sfa_msg);
APPEND_REALLOC(buf, off, len, tmp);
LOGDEBUG("%s.%s-%s.%s", id, transfer_data(i_username), action, buf);
return buf;
return reply_event_free(event, buf);
}
static char *cmd_userset(PGconn *conn, char *cmd, char *id,
@ -451,6 +473,7 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
char *email, *address, *payname;
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
char tmp[1024];
PAYMENTADDRESSES *row, *pa;
K_STORE *pa_store = NULL;
@ -479,6 +502,7 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
K_RUNLOCK(users_free);
if (!u_item) {
event = events_add(EVENTID_UNKATTS, trf_root);
reason = "Unknown user";
goto struckout;
} else {
@ -548,11 +572,13 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
i_2fa = require_name(trf_root, "2fa", 1, (char *)intpatt,
reply, siz);
if (!i_2fa) {
event = events_add(EVENTID_INV2FA, trf_root);
reason = "Invalid data";
goto struckout;
}
if (!check_hash(users, transfer_data(i_passwordhash))) {
event = events_add(EVENTID_PASSFAIL, trf_root);
reason = "Incorrect password";
goto struckout;
}
@ -560,6 +586,7 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
if (USER_TOTP_ENA(users)) {
uint32_t value = (int32_t)atoi(transfer_data(i_2fa));
if (!check_2fa(users, value)) {
event = events_add(EVENTID_WRONG2FA, trf_root);
reason = "Invalid data";
goto struckout;
}
@ -624,6 +651,8 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
reply, siz);
if (!i_address) {
K_WUNLOCK(paymentaddresses_free);
event = events_add(EVENTID_INCBTC,
trf_root);
reason = "Invalid address";
goto struckout;
}
@ -679,10 +708,14 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
* payout address */
DATA_PAYMENTADDRESSES(pa, old_pa_item);
if (pa->userid != users->userid) {
event = events_add(EVENTID_BTCUSED,
trf_root);
reason = "Unavailable BTC address";
goto struckout;
}
} else if (!btc_valid_address(row->payaddress)) {
event = events_add(EVENTID_INVBTC,
trf_root);
reason = "Invalid BTC address";
goto struckout;
}
@ -696,7 +729,7 @@ static char *cmd_userset(PGconn *conn, char *cmd, char *id,
email,
by, code, inet, now,
trf_root,
NULL);
NULL, &event);
if (!ok) {
reason = "email error";
goto struckout;
@ -728,16 +761,22 @@ struckout:
pa_store = NULL;
}
if (reason) {
char *user, *st = NULL;
snprintf(reply, siz, "ERR.%s", reason);
LOGERR("%s.%s.%s", cmd, id, reply);
return strdup(reply);
if (i_username)
user = st = safe_text(transfer_data(i_username));
else
user = EMPTY;
LOGERR("%s.%s.%s (%s)", cmd, id, reply, user);
FREENULL(st);
return reply_event(event, reply);
}
APPEND_REALLOC_INIT(ret, off, len);
APPEND_REALLOC(ret, off, len, "ok.");
APPEND_REALLOC(ret, off, len, answer);
free(answer);
LOGDEBUG("%s.%s", id, ret);
return ret;
return reply_event_free(event, ret);
}
static char *cmd_workerset(PGconn *conn, char *cmd, char *id, tv_t *now,
@ -2894,6 +2933,7 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by,
K_TREE_CTX ctx[1];
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
K_ITEM *i_poolinstance, *i_username, *i_workername, *i_clientid;
K_ITEM *i_enonce1, *i_useragent, *i_preauth, *u_item, *oc_item, *w_item;
USERS *users = NULL;
@ -2902,7 +2942,7 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by,
OPTIONCONTROL *optioncontrol;
size_t len, off;
char *buf;
bool ok, first;
bool ok = true, first;
LOGDEBUG("%s(): cmd '%s'", __func__, cmd);
@ -2958,13 +2998,19 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by,
u_item = find_users(username);
K_RUNLOCK(users_free);
if (!u_item) {
event = events_add(EVENTID_AUTOACC, trf_root);
if (event == EVENT_OK) {
DATA_OPTIONCONTROL(optioncontrol, oc_item);
u_item = users_add(conn, username, EMPTY,
optioncontrol->optionvalue, 0,
by, code, inet, cd, trf_root);
optioncontrol->optionvalue,
0, by, code, inet, cd,
trf_root);
} else
ok = false;
}
}
if (ok) {
ok = auths_add(conn, transfer_data(i_poolinstance),
username,
transfer_data(i_workername),
@ -2973,11 +3019,12 @@ static char *cmd_auth_do(PGconn *conn, char *cmd, char *id, char *by,
transfer_data(i_useragent),
transfer_data(i_preauth),
by, code, inet, cd, trf_root, false,
&users, &workers);
&users, &workers, &event);
}
if (!ok) {
LOGDEBUG("%s() %s.failed.DBE", __func__, id);
return strdup("failed.DBE");
return reply_event(event, "failed.DBE");
}
// Only flag a successful auth
@ -3042,6 +3089,7 @@ static char *cmd_addrauth_do(PGconn *conn, char *cmd, char *id, char *by,
K_TREE_CTX ctx[1];
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
K_ITEM *i_poolinstance, *i_username, *i_workername, *i_clientid;
K_ITEM *i_enonce1, *i_useragent, *i_preauth, *w_item;
USERS *users = NULL;
@ -3105,11 +3153,11 @@ static char *cmd_addrauth_do(PGconn *conn, char *cmd, char *id, char *by,
transfer_data(i_useragent),
transfer_data(i_preauth),
by, code, inet, cd, trf_root, true,
&users, &workers);
&users, &workers, &event);
if (!ok) {
LOGDEBUG("%s() %s.failed.DBE", __func__, id);
return strdup("failed.DBE");
return reply_event(event, "failed.DBE");
}
// Only flag a successful auth
@ -3352,7 +3400,7 @@ static char *cmd_homepage(__maybe_unused PGconn *conn, char *cmd, char *id,
pool.shareinv, FLDSEP);
APPEND_REALLOC(buf, off, len, tmp);
// TODO: assumes only one poolinstance (for now)
// TODO: DB only has one poolinstance with -i
K_RLOCK(poolstats_free);
p_item = last_in_ktree(poolstats_root, ctx);
K_RUNLOCK(poolstats_free);
@ -3524,6 +3572,7 @@ static char *cmd_getatts(__maybe_unused PGconn *conn, char *cmd, char *id,
K_ITEM *i_username, *i_attlist, *u_item, *ua_item;
char reply[1024] = "";
size_t siz = sizeof(reply);
int event = EVENT_OK;
char tmp[1024];
USERATTS *useratts;
USERS *users;
@ -3538,6 +3587,7 @@ static char *cmd_getatts(__maybe_unused PGconn *conn, char *cmd, char *id,
i_username = require_name(trf_root, "username", MIN_USERNAME,
(char *)userpatt, reply, siz);
if (!i_username) {
// Shouldn't happen except with a code problem no event required
reason = "Missing username";
goto nuts;
}
@ -3547,6 +3597,8 @@ static char *cmd_getatts(__maybe_unused PGconn *conn, char *cmd, char *id,
K_RUNLOCK(users_free);
if (!u_item) {
// page_api.php without a valid username
event = events_add(EVENTID_UNKATTS, trf_root);
reason = "Unknown user";
goto nuts;
} else {
@ -3639,7 +3691,7 @@ nuts:
free(answer);
snprintf(reply, siz, "ERR.%s", reason);
LOGERR("%s.%s.%s", cmd, id, reply);
return strdup(reply);
return reply_event(event, reply);
}
snprintf(reply, siz, "ok.%s", answer);
LOGDEBUG("%s.%s", id, answer);
@ -5871,7 +5923,7 @@ static char *cmd_userstatus(PGconn *conn, char *cmd, char *id, tv_t *now, char *
NULL,
by, code, inet, now,
trf_root,
status);
status, NULL);
}
if (!ok) {

536
src/ckdb_data.c

@ -425,7 +425,7 @@ void _txt_to_tv(char *nam, char *fld, tv_t *data, size_t siz, WHERE_FFL_ARGS)
_txt_to_data(TYPE_TV, nam, fld, (void *)data, siz, WHERE_FFL_PASS);
}
// Convert msg S,nS to tv_t
// Convert msg S[,nS] to tv_t
void _txt_to_ctv(char *nam, char *fld, tv_t *data, size_t siz, WHERE_FFL_ARGS)
{
_txt_to_data(TYPE_CTV, nam, fld, (void *)data, siz, WHERE_FFL_PASS);
@ -4273,6 +4273,540 @@ oku:
return ok;
}
// order by group asc,ip asc,expirydate desc
cmp_t cmp_ips(K_ITEM *a, K_ITEM *b)
{
IPS *ia, *ib;
DATA_IPS(ia, a);
DATA_IPS(ib, b);
cmp_t c = CMP_STR(ia->group, ib->group);
if (c == 0) {
c = CMP_STR(ia->ip, ib->ip);
if (c == 0)
c = CMP_TV(ib->expirydate, ia->expirydate);
}
return c;
}
// Must be R or W locked before call
K_ITEM *find_ips(char *group, char *ip)
{
K_TREE_CTX ctx[1];
K_ITEM look;
IPS ips;
STRNCPY(ips.group, group);
STRNCPY(ips.ip, ip);
ips.expirydate.tv_sec = default_expiry.tv_sec;
ips.expirydate.tv_usec = default_expiry.tv_usec;
INIT_IPS(&look);
look.data = (void *)(&ips);
return find_in_ktree(ips_root, &look, ctx);
}
// order by createby asc,id asc,expirydate desc, createdate asc
cmp_t cmp_events_user(K_ITEM *a, K_ITEM *b)
{
EVENTS *ea, *eb;
DATA_EVENTS(ea, a);
DATA_EVENTS(eb, b);
cmp_t c = CMP_STR(ea->createby, eb->createby);
if (c == 0) {
c = CMP_INT(ea->id, eb->id);
if (c == 0) {
c = CMP_TV(eb->expirydate, ea->expirydate);
if (c == 0)
c = CMP_TV(ea->createdate, eb->createdate);
}
}
return c;
}
// order by createinet asc,id asc,expirydate desc, createdate asc
cmp_t cmp_events_ip(K_ITEM *a, K_ITEM *b)
{
EVENTS *ea, *eb;
DATA_EVENTS(ea, a);
DATA_EVENTS(eb, b);
cmp_t c = CMP_STR(ea->createinet, eb->createinet);
if (c == 0) {
c = CMP_INT(ea->id, eb->id);
if (c == 0) {
c = CMP_TV(eb->expirydate, ea->expirydate);
if (c == 0)
c = CMP_TV(ea->createdate, eb->createdate);
}
}
return c;
}
// order by ipc asc,id asc,expirydate desc, createdate asc
cmp_t cmp_events_ipc(K_ITEM *a, K_ITEM *b)
{
EVENTS *ea, *eb;
DATA_EVENTS(ea, a);
DATA_EVENTS(eb, b);
cmp_t c = CMP_STR(ea->ipc, eb->ipc);
if (c == 0) {
c = CMP_INT(ea->id, eb->id);
if (c == 0) {
c = CMP_TV(eb->expirydate, ea->expirydate);
if (c == 0)
c = CMP_TV(ea->createdate, eb->createdate);
}
}
return c;
}
// order by hash asc,id asc,expirydate desc, createdate asc
cmp_t cmp_events_hash(K_ITEM *a, K_ITEM *b)
{
EVENTS *ea, *eb;
DATA_EVENTS(ea, a);
DATA_EVENTS(eb, b);
cmp_t c = CMP_STR(ea->hash, eb->hash);
if (c == 0) {
c = CMP_INT(ea->id, eb->id);
if (c == 0) {
c = CMP_TV(eb->expirydate, ea->expirydate);
if (c == 0)
c = CMP_TV(ea->createdate, eb->createdate);
}
}
return c;
}
K_ITEM *last_events_user(int id, char *user, K_TREE_CTX *ctx)
{
K_TREE_CTX ctx0[1];
EVENTS events;
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
events.id = id;
STRNCPY(events.createby, user);
copy_tv(&(events.expirydate), &default_expiry);
copy_tv(&(events.createdate), &date_eot);
INIT_EVENTS(&look);
look.data = (void *)(&events);
return find_before_in_ktree(events_user_root, &look, ctx);
}
K_ITEM *last_events_ip(int id, char *ip, K_TREE_CTX *ctx)
{
K_TREE_CTX ctx0[1];
EVENTS events;
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
events.id = id;
STRNCPY(events.createinet, ip);
copy_tv(&(events.expirydate), &default_expiry);
copy_tv(&(events.createdate), &date_eot);
INIT_EVENTS(&look);
look.data = (void *)(&events);
return find_before_in_ktree(events_ip_root, &look, ctx);
}
K_ITEM *last_events_ipc(int id, char *ipc, K_TREE_CTX *ctx)
{
K_TREE_CTX ctx0[1];
EVENTS events;
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
events.id = id;
STRNCPY(events.ipc, ipc);
copy_tv(&(events.expirydate), &default_expiry);
copy_tv(&(events.createdate), &date_eot);
INIT_EVENTS(&look);
look.data = (void *)(&events);
return find_before_in_ktree(events_ipc_root, &look, ctx);
}
K_ITEM *last_events_hash(int id, char *hash, K_TREE_CTX *ctx)
{
K_TREE_CTX ctx0[1];
EVENTS events;
K_ITEM look;
if (ctx == NULL)
ctx = ctx0;
events.id = id;
STRNCPY(events.hash, hash);
copy_tv(&(events.expirydate), &default_expiry);
copy_tv(&(events.createdate), &date_eot);
INIT_EVENTS(&look);
look.data = (void *)(&events);
return find_before_in_ktree(events_hash_root, &look, ctx);
}
enum event_cause {
CAUSE_NONE,
CAUSE_USER_LO,
CAUSE_USER_HI,
CAUSE_IP_LO,
CAUSE_IP_HI,
CAUSE_IPC_LO,
CAUSE_IPC_HI,
CAUSE_HASH
};
static const char *cause_none = "None";
static const char *cause_user_lo = "User Low";
static const char *cause_user_hi = "User High";
static const char *cause_ip_lo = "IP Low";
static const char *cause_ip_hi = "IP High";
static const char *cause_ipc_lo = "IP C-Class Low";
static const char *cause_ipc_hi = "IP C-Class Hi";
static const char *cause_hash = "Hash";
static const char *cause_unknown = "Unknown?";
static const char *cause_str(enum event_cause cause)
{
switch (cause) {
case CAUSE_NONE:
return cause_none;
case CAUSE_USER_LO:
return cause_user_lo;
case CAUSE_USER_HI:
return cause_user_hi;
case CAUSE_IP_LO:
return cause_ip_lo;
case CAUSE_IP_HI:
return cause_ip_hi;
case CAUSE_IPC_LO:
return cause_ipc_lo;
case CAUSE_IPC_HI:
return cause_ipc_hi;
case CAUSE_HASH:
return cause_hash;
default:
return cause_unknown;
}
}
// return EVENT_OK or timeout seconds
int check_events(EVENTS *events)
{
bool alert = false, user1, user2;
char createby[TXT_SML+1], *st = NULL;
enum event_cause cause = CAUSE_NONE;
K_ITEM *i_item, *e_item = NULL, *tmp_item, *u_item;
K_TREE_CTX ctx[1];
EVENTS *e = NULL;
int count, secs;
int tyme, limit, lifetime;
pid_t pid;
tv_t now;
// No way to send an alert, so don't test
if (!ckdb_alert_cmd)
return EVENT_OK;
K_RLOCK(ips_free);
i_item = find_ips(IPS_GROUP_OK, events->createinet);
if (!i_item)
i_item = find_ips(IPS_GROUP_OK, events->ipc);
K_RUNLOCK(ips_free);
if (i_item)
return EVENT_OK;
// All tests below always run all full checks to clean up old events
setnow(&now);
K_WLOCK(events_free);
// Check hash - same hash passfail on more than one valid User
if (events->id == EVENTID_PASSFAIL && *(events->hash)) {
e_item = last_events_hash(events->id, events->hash, ctx);
if (e_item) {
DATA_EVENTS(e, e_item);
user1 = false;
while (e_item && e->id == events->id &&
strcmp(e->hash, events->hash) == 0 &&
CURRENT(&(e->expirydate))) {
// rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old?
if (secs >= event_limits_hash_lifetime) {
tmp_item = e_item;
e_item = prev_in_ktree(ctx);
// Discard the old event - e is still tmp_item
remove_from_ktree(events_hash_root, tmp_item);
if (--(e->trees) < 1) {
// No longer in any of the event trees
k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item);
}
continue;
}
if (!alert) {
if (!user1) {
K_RLOCK(users_free);
u_item = find_users(e->createby);
K_RUNLOCK(users_free);
if (u_item) {
// Remember the username
STRNCPY(createby, e->createby);
user1 = true;
}
} else {
// Don't check username case mistyping errors
if (strcasecmp(createby, e->createby) != 0) {
K_RLOCK(users_free);
u_item = find_users(e->createby);
K_RUNLOCK(users_free);
if (u_item) {
alert = true;
cause = CAUSE_HASH;
tyme = 0;
limit = 1;
lifetime = event_limits_hash_lifetime;
}
}
}
}
e_item = prev_in_ktree(ctx);
}
}
}
// Check User
e_item = last_events_user(events->id, events->createby, ctx);
if (e_item) {
DATA_EVENTS(e, e_item);
count = 0;
while (e_item && e->id == events->id &&
strcmp(e->createby, events->createby) == 0 &&
CURRENT(&(e->expirydate))) {
count++;
// rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old?
if (secs >= e_limits[events->id].lifetime) {
tmp_item = e_item;
e_item = prev_in_ktree(ctx);
// Discard the old event - e is still tmp_item
remove_from_ktree(events_user_root, tmp_item);
if (--e->trees < 1) {
// No longer in any of the event trees
k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item);
}
continue;
}
if (alert == false &&
secs <= e_limits[events->id].user_low_time &&
count > e_limits[events->id].user_low_time_limit) {
alert = true;
cause = CAUSE_USER_LO;
tyme = e_limits[events->id].user_low_time;
limit = e_limits[events->id].user_low_time_limit;
lifetime = e_limits[events->id].lifetime;
}
if (alert == false &&
secs <= e_limits[events->id].user_hi_time &&
count > e_limits[events->id].user_hi_time_limit) {
alert = true;
cause = CAUSE_USER_HI;
tyme = e_limits[events->id].user_hi_time;
limit = e_limits[events->id].user_hi_time_limit;
lifetime = e_limits[events->id].lifetime;
}
e_item = prev_in_ktree(ctx);
}
}
// Check IP
e_item = last_events_ip(events->id, events->createinet, ctx);
if (e_item) {
DATA_EVENTS(e, e_item);
count = 0;
// Remember the first username
STRNCPY(createby, e->createby);
user2 = false;
while (e_item && e->id == events->id &&
strcmp(e->createinet, events->createinet) == 0 &&
CURRENT(&(e->expirydate))) {
count++;
// rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old?
if (secs >= e_limits[events->id].lifetime) {
tmp_item = e_item;
e_item = prev_in_ktree(ctx);
// Discard the old event - e is still tmp_item
remove_from_ktree(events_ip_root, tmp_item);
if (--e->trees < 1) {
// No longer in any of the event trees
k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item);
}
continue;
}
// Allow username case typing errors
if (strcasecmp(createby, e->createby) != 0)
user2 = true;
if (alert == false &&
secs <= e_limits[events->id].ip_low_time &&
count > e_limits[events->id].ip_low_time_limit) {
alert = true;
cause = CAUSE_IP_LO;
tyme = e_limits[events->id].ip_low_time;
limit = e_limits[events->id].ip_low_time_limit;
lifetime = e_limits[events->id].lifetime;
}
if (alert == false &&
secs <= e_limits[events->id].ip_hi_time &&
count > e_limits[events->id].ip_hi_time_limit) {
alert = true;
cause = CAUSE_IP_HI;
tyme = e_limits[events->id].ip_hi_time;
limit = e_limits[events->id].ip_hi_time_limit;
lifetime = e_limits[events->id].lifetime;
}
e_item = prev_in_ktree(ctx);
}
/* If the ip alert was a single user then it's not an ip failure
* since the User check already covers that */
if (alert && (cause == CAUSE_IP_LO || cause == CAUSE_IP_HI) &&
user2 == false)
alert = false;
}
// Check IPC (Class C IP) use same rules as for IP
e_item = last_events_ipc(events->id, events->ipc, ctx);
if (e_item) {
DATA_EVENTS(e, e_item);
count = 0;
// Remember the first username
STRNCPY(createby, e->createby);
user2 = false;
while (e_item && e->id == events->id &&
strcmp(e->ipc, events->ipc) == 0 &&
CURRENT(&(e->expirydate))) {
count++;
// rounded down seconds
secs = (int)tvdiff(&now, &(e->createdate));
// Is this event too old?
if (secs >= e_limits[events->id].lifetime) {
tmp_item = e_item;
e_item = prev_in_ktree(ctx);
// Discard the old event - e is still tmp_item
remove_from_ktree(events_ipc_root, tmp_item);
if (--e->trees < 1) {
k_unlink_item(events_store, tmp_item);
k_add_head(events_free, tmp_item);
}
continue;
}
// Allow username case typing errors
if (strcasecmp(createby, e->createby) != 0)
user2 = true;
if (alert == false &&
secs <= e_limits[events->id].ip_low_time &&
count > e_limits[events->id].ip_low_time_limit) {
alert = true;
cause = CAUSE_IPC_LO;
tyme = e_limits[events->id].ip_low_time;
limit = e_limits[events->id].ip_low_time_limit;
lifetime = e_limits[events->id].lifetime;
}
if (alert == false &&
secs <= e_limits[events->id].ip_hi_time &&
count > e_limits[events->id].ip_hi_time_limit) {
alert = true;
cause = CAUSE_IPC_HI;
tyme = e_limits[events->id].ip_hi_time;
limit = e_limits[events->id].ip_hi_time_limit;
lifetime = e_limits[events->id].lifetime;
}
e_item = prev_in_ktree(ctx);
}
/* If the ipc alert was a single user then it's not an ipc failure
* since the User check already covers that */
if (alert && (cause == CAUSE_IPC_LO || cause == CAUSE_IPC_HI) &&
user2 == false)
alert = false;
}
K_WUNLOCK(events_free);
if (alert) {
LOGERR("%s() ALERT ID:%d Lim:%d Time:%d Life:%d %s '%s' '%s'",
__func__,
events->id, limit, tyme, lifetime,
events->createinet,
st = safe_text_nonull(events->createby),
cause_str(cause));
FREENULL(st);
ips_add(IPS_GROUP_BAN, events->createinet,
(char *)cause_str(cause), true, false, lifetime);
pid = fork();
if (pid < 0) {
LOGERR("%s() ALERT failed to fork (%d)",
__func__, errno);
} else {
if (pid == 0) {
char buf1[16], buf2[16], buf3[16], buf4[16];
snprintf(buf1, sizeof(buf1), "%d", events->id);
snprintf(buf2, sizeof(buf2), "%d", limit);
snprintf(buf3, sizeof(buf3), "%d", tyme);
snprintf(buf4, sizeof(buf4), "%d", lifetime);
st = safe_text_nonull(events->createby);
execl(ckdb_alert_cmd, ckdb_alert_cmd, buf1,
buf2, buf3, buf4, events->createinet,
st, cause_str(cause), NULL);
LOGERR("%s() ALERT fork failed to execute (%d)",
__func__, errno);
FREENULL(st);
exit(0);
}
}
return lifetime;
}
return EVENT_OK;
}
static char lurt[] = "alert=";
static size_t lurtsiz = sizeof(lurt);
static char toomany[] = "Too many failures, come back later";
static size_t toomanysiz = sizeof(toomany);
char *_reply_event(int event, char *buf, bool fre)
{
size_t len;
char *reply;
if (event == EVENT_OK) {
if (fre)
return buf;
else {
reply = strdup(buf);
if (!reply)
quithere(1, "strdup OOM");
return reply;
}
}
len = strlen(buf);
len += 1 + lurtsiz;
len += toomanysiz + 1;
reply = malloc(len);
if (!reply)
quithere(1, "malloc (%d) OOM", (int)len);
snprintf(reply, len, "%s%c%s%s", buf, FLDSEP, lurt, toomany);
if (fre)
free(buf);
return reply;
}
// order by userid asc,createdate asc,authid asc,expirydate desc
cmp_t cmp_auths(K_ITEM *a, K_ITEM *b)
{

180
src/ckdb_dbio.c

@ -389,10 +389,10 @@ cleanup:
return lastid;
}
// status was added to the end so type checking intercepts new mistakes
bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
char *newhash, char *email, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root, char *status)
char *inet, tv_t *cd, K_TREE *trf_root, char *status,
int *event)
{
ExecStatusType rescode;
bool conned = false;
@ -414,8 +414,11 @@ bool users_update(PGconn *conn, K_ITEM *u_item, char *oldhash,
DATA_USERS(users, u_item);
// i.e. if oldhash == EMPTY, just overwrite with new
if (hash && oldhash != EMPTY && !check_hash(users, oldhash))
if (hash && oldhash != EMPTY && !check_hash(users, oldhash)) {
if (event)
*event = events_add(EVENTID_PASSFAIL, trf_root);
return false;
}
K_WLOCK(users_free);
item = k_unlink_head(users_free);
@ -6125,12 +6128,180 @@ bool payouts_fill(PGconn *conn)
return ok;
}
void ips_add(char *group, char *ip, char *des, bool log, bool cclass, int life)
{
K_ITEM *i_item, *i2_item;
IPS *ips, *ips2;
char *dot;
tv_t now;
bool ok;
setnow(&now);
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);
}
K_WUNLOCK(ips_free);
}
// trf_root overrides by,inet,cd fields
int _events_add(int id, char *by, char *inet, tv_t *cd, K_TREE *trf_root)
{
K_ITEM look, *e_item, *i_passwordhash, *i_webtime, *i_username;
K_TREE_CTX ctx[1];
EVENTS events, *d_events;
char reply[1024] = "";
size_t siz = sizeof(reply);
LOGDEBUG("%s(): add", __func__);
bzero(&events, sizeof(events));
events.id = id;
events.expirydate.tv_sec = default_expiry.tv_sec;
events.expirydate.tv_usec = default_expiry.tv_usec;
// Default to now if not specified
setnow(&(events.createdate));
if (by)
STRNCPY(events.createby, by);
if (inet)
STRNCPY(events.createinet, inet);
if (cd)
copy_tv(&(events.createdate), cd);
// trf_root values overrides parameters
HISTORYDATETRANSFER(trf_root, &events);
// username overrides createby
i_username = optional_name(trf_root, "username", 1, NULL,
reply, siz);
if (i_username)
STRNCPY(events.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),
events.createdate);
}
/* We don't care if it's valid or not, though php should have
* already ensured it's valid */
i_passwordhash = optional_name(trf_root, "passwordhash", 1, NULL,
reply, siz);
if (i_passwordhash)
STRNCPY(events.hash, transfer_data(i_passwordhash));
if (events.createinet[0]) {
char *dot;
STRNCPY(events.ipc, events.createinet);
dot = strchr(events.ipc, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot) {
dot = strchr(dot+1, '.');
if (dot)
*dot = '\0';
}
}
}
// Only store an event that had actual usable data
if (!events.createby[0] && !events.createinet[0] &&
!events.ipc[0] && !events.hash[0])
return EVENT_OK;
INIT_EVENTS(&look);
look.data = (void *)(&events);
// All processing under lock - since we must be able to delete events
K_WLOCK(events_free);
// keep looping incrementing the usec time until it's not a duplicate
while (find_in_ktree(events_user_root, &look, ctx) ||
find_in_ktree(events_ip_root, &look, ctx) ||
find_in_ktree(events_ipc_root, &look, ctx) ||
find_in_ktree(events_hash_root, &look, ctx)) {
if (++events.createdate.tv_usec >= 1000000) {
events.createdate.tv_usec -= 1000000;
events.createdate.tv_sec++;
}
}
e_item = k_unlink_head(events_free);
DATA_EVENTS(d_events, e_item);
COPY_DATA(d_events, &events);
k_add_head(events_store, e_item);
if (d_events->createby[0]) {
add_to_ktree(events_user_root, e_item);
d_events->trees++;
}
if (d_events->createinet[0]) {
add_to_ktree(events_ip_root, e_item);
d_events->trees++;
}
// Don't bother if it's the same as IP
if (d_events->ipc[0] &&
strcmp(d_events->ipc, d_events->createinet) != 0) {
add_to_ktree(events_ipc_root, e_item);
d_events->trees++;
}
if (d_events->hash[0]) {
add_to_ktree(events_hash_root, e_item);
d_events->trees++;
}
K_WUNLOCK(events_free);
return check_events(&events);
}
// TODO: discard them from RAM
bool auths_add(PGconn *conn, char *poolinstance, char *username,
char *workername, char *clientid, char *enonce1,
char *useragent, char *preauth, char *by, char *code,
char *inet, tv_t *cd, K_TREE *trf_root,
bool addressuser, USERS **users, WORKERS **workers)
bool addressuser, USERS **users, WORKERS **workers,
int *event)
{
K_TREE_CTX ctx[1];
K_ITEM *a_item, *u_item, *w_item;
@ -6161,6 +6332,7 @@ bool auths_add(PGconn *conn, char *poolinstance, char *username,
__func__,
st = safe_text_nonull(username));
FREENULL(st);
*event = events_add(EVENTID_INVAUTH, trf_root);
}
if (!u_item)
goto unitem;

Loading…
Cancel
Save