mirror of https://github.com/BOINC/boinc.git
- add ops script to check for users to large numbers of hosts.
Could be evidence of botnets, like the recent one. - scheduler: update comments in authenticate_user()
This commit is contained in:
parent
b3e64f761e
commit
81f9ab467a
|
@ -0,0 +1,59 @@
|
|||
<?php
|
||||
|
||||
// identify possible botnet accounts:
|
||||
// those with > MAX_HOSTS hosts
|
||||
//
|
||||
// could:
|
||||
// add other criteria like # of countries (based on IP addr)
|
||||
// consider only hosts with recent last RPC time
|
||||
|
||||
define ('MAX_HOSTS', 100);
|
||||
|
||||
require_once('../inc/boinc_db.inc');
|
||||
|
||||
// when hosts are merged, userid is set to zero
|
||||
// and the rpc_seqno field is used to link to the non-zombie host.
|
||||
// Follow links and return the latter.
|
||||
//
|
||||
function follow_links($id) {
|
||||
$orig_id = $id;
|
||||
$host = BoincHost::lookup_id($id);
|
||||
while ($host->userid == 0) {
|
||||
$host = BoincHost::lookup_id($host->rpc_seqno);
|
||||
if (!$host) {
|
||||
echo "(broken link in chain from host $orig_id)\n";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return $host;
|
||||
}
|
||||
|
||||
function main() {
|
||||
$hosts = BoincHost::enum_fields('id, userid, last_ip_addr', '');
|
||||
$users = [];
|
||||
foreach ($hosts as $host) {
|
||||
if ($host->userid == 0) {
|
||||
$host = follow_links($host->id);
|
||||
if (!$host) continue;
|
||||
}
|
||||
if (array_key_exists($host->userid, $users)) {
|
||||
$users[$host->userid]++;
|
||||
} else {
|
||||
$users[$host->userid] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($users as $id => $nhosts) {
|
||||
if ($nhosts < MAX_HOSTS) continue;
|
||||
$user = BoincUser::lookup_id($id);
|
||||
if ($user) {
|
||||
echo "$user->name has $nhosts hosts\n";
|
||||
} else {
|
||||
echo "unknown user $id has $nhosts hosts\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
?>
|
|
@ -231,20 +231,52 @@ static void mark_results_over(DB_HOST& host) {
|
|||
}
|
||||
|
||||
// Based on the info in the request message,
|
||||
// look up the host and its user, and make sure the authenticator matches.
|
||||
// Some special cases:
|
||||
// 1) If no host ID is supplied, or if RPC seqno mismatch,
|
||||
// create a new host record
|
||||
// 2) If the host record specified by g_request->hostid is a "zombie"
|
||||
// (i.e. it was merged with another host via the web site)
|
||||
// then follow links to find the proper host
|
||||
// look up the appropriate user and host records; create host record if needed.
|
||||
// Return error if couldn't authenticate user.
|
||||
//
|
||||
// The request message may contain any or all of:
|
||||
// user info:
|
||||
// authenticator (can be weak auth)
|
||||
// cross_project_id
|
||||
// host info:
|
||||
// hostid (DB ID of host)
|
||||
// rpc_seqno
|
||||
// host_cpid
|
||||
// host info like name/IP/processor/RAM
|
||||
//
|
||||
// general logic (see comments for details)
|
||||
// if hostid given
|
||||
// lookup host record
|
||||
// lookup user based on host.userid
|
||||
// if host.authenticator doesn't match request
|
||||
// lookup user based on request auth
|
||||
// check that user.id == host.userid
|
||||
// else
|
||||
// lookup user based on request auth
|
||||
// look for a host return matching host_cpid,
|
||||
// or host info; probably means detach/reattach.
|
||||
// if none, create host record
|
||||
//
|
||||
// Special cases:
|
||||
// - If no host ID is supplied, this is first RPC from host;
|
||||
// create a new host record
|
||||
// - if RPC seqno mismatch, user probably copied client_state.xml
|
||||
// to a new host; create a new host record
|
||||
// - If the host record specified by hostid is a "zombie"
|
||||
// (i.e. it was merged with another host via the web)
|
||||
// follow links to find the master host record
|
||||
// We use the rpc_seqno field for these links.
|
||||
//
|
||||
// POSTCONDITION:
|
||||
// If this function returns zero, then:
|
||||
// - reply.host contains a valid host record (possibly new)
|
||||
// - reply.user contains a valid user record
|
||||
// - reply.user contains a valid user record,
|
||||
// and host.userid == user.id
|
||||
// - if user belongs to a team, reply.team contains team record
|
||||
//
|
||||
// Also sets reply->email_hash,
|
||||
// and updates user CPID if needed to match request
|
||||
//
|
||||
int authenticate_user() {
|
||||
int retval;
|
||||
char buf[1024];
|
||||
|
@ -256,6 +288,7 @@ int authenticate_user() {
|
|||
retval = host.lookup_id(g_request->hostid);
|
||||
while (!retval && host.userid==0) {
|
||||
// if host record is zombie, follow link to new host
|
||||
// TODO: check for infinite loop
|
||||
//
|
||||
retval = host.lookup_id(host.rpc_seqno);
|
||||
if (!retval) {
|
||||
|
@ -267,6 +300,9 @@ int authenticate_user() {
|
|||
}
|
||||
}
|
||||
if (retval) {
|
||||
// bad host ID, or broken zombie chain.
|
||||
// Make new host record.
|
||||
//
|
||||
g_reply->insert_message("Can't find host record", "low");
|
||||
log_messages.printf(MSG_NORMAL,
|
||||
"[HOST#%lu?] can't find host\n",
|
||||
|
@ -278,8 +314,9 @@ int authenticate_user() {
|
|||
|
||||
g_reply->host = host;
|
||||
|
||||
// look up user based on the ID in host record,
|
||||
// and see if the authenticator matches (regular or weak)
|
||||
// We have a host record based on ID.
|
||||
// Look up user based on host.userid,
|
||||
// and see if the authenticator matches request (regular or weak)
|
||||
//
|
||||
g_request->using_weak_auth = false;
|
||||
sprintf(buf, "where id=%lu", host.userid);
|
||||
|
@ -324,8 +361,14 @@ int authenticate_user() {
|
|||
}
|
||||
}
|
||||
|
||||
// At this point we have a host record,
|
||||
// and a user record that's authenticated with the request
|
||||
|
||||
g_reply->user = user;
|
||||
|
||||
// Check that the host and user records are consistent
|
||||
// If not, make a new host record
|
||||
|
||||
if (host.userid != user.id) {
|
||||
// If the request's host ID isn't consistent with the authenticator,
|
||||
// create a new host record.
|
||||
|
@ -334,10 +377,9 @@ int authenticate_user() {
|
|||
"[HOST#%lu] [USER#%lu] inconsistent host ID; creating new host\n",
|
||||
host.id, user.id
|
||||
);
|
||||
goto make_new_host;
|
||||
goto didnt_find_host;
|
||||
}
|
||||
|
||||
|
||||
// If the seqno from the host is less than what we expect,
|
||||
// the user must have copied the state file to a different host.
|
||||
// Make a new host record.
|
||||
|
@ -348,7 +390,7 @@ int authenticate_user() {
|
|||
"[HOST#%lu] [USER#%lu] RPC seqno %d less than expected %d; creating new host\n",
|
||||
g_reply->host.id, user.id, g_request->rpc_seqno, g_reply->host.rpc_seqno
|
||||
);
|
||||
goto make_new_host;
|
||||
goto didnt_find_host;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -359,6 +401,7 @@ lookup_user_and_make_new_host:
|
|||
// if authenticator contains _, it's a weak auth
|
||||
//
|
||||
if (strchr(g_request->authenticator, '_')) {
|
||||
// weak auths start with 'userid_'
|
||||
int userid = atoi(g_request->authenticator);
|
||||
retval = user.lookup_id(userid);
|
||||
if (!retval) {
|
||||
|
@ -388,12 +431,16 @@ lookup_user_and_make_new_host:
|
|||
);
|
||||
return ERR_AUTHENTICATOR;
|
||||
}
|
||||
|
||||
// at this point we have a user record, authenticated by the request
|
||||
|
||||
g_reply->user = user;
|
||||
|
||||
// If host CPID is present,
|
||||
// scan backwards through this user's hosts,
|
||||
// looking for one with the same host CPID.
|
||||
// If we find one, it means the user detached and reattached.
|
||||
// find most recent host with the same host CPID
|
||||
// belonging to this user.
|
||||
// If we find one, and it has similar properties,
|
||||
// it probably means the user detached and reattached.
|
||||
// Use the existing host record,
|
||||
// and mark in-progress results as over.
|
||||
//
|
||||
|
@ -419,15 +466,23 @@ lookup_user_and_make_new_host:
|
|||
}
|
||||
}
|
||||
|
||||
make_new_host:
|
||||
// One final attempt to locate an existing host record:
|
||||
didnt_find_host:
|
||||
// we didn't find a usable host record; either
|
||||
// - host ID was given but didn't match user ID,
|
||||
// or RPC seqno was too low.
|
||||
// - host ID wasn't given
|
||||
//
|
||||
// Before we create a new host record,
|
||||
// a final attempt to locate an existing host record:
|
||||
// scan backwards through this user's hosts,
|
||||
// looking for one with the same host name,
|
||||
// IP address, processor and amount of RAM.
|
||||
// If found, use the existing host record,
|
||||
// If found, it probably means the client detached and reattached.
|
||||
// Use the existing host record,
|
||||
// and mark in-progress results as over.
|
||||
//
|
||||
// NOTE: If the client was run with --allow_multiple_clients, skip this.
|
||||
// NOTE: If the client was run with --allow_multiple_clients,
|
||||
// skip this; each client instance needs its own host record.
|
||||
//
|
||||
if ((g_request->allow_multiple_clients != 1)
|
||||
&& find_host_by_other(user, g_request->host, host)
|
||||
|
@ -444,11 +499,8 @@ make_new_host:
|
|||
}
|
||||
goto got_host;
|
||||
}
|
||||
// either of the above cases,
|
||||
// or host ID didn't match user ID,
|
||||
// or RPC seqno was too low.
|
||||
//
|
||||
// Create a new host.
|
||||
|
||||
// Create a new host record.
|
||||
// g_reply->user is filled in and valid at this point
|
||||
//
|
||||
host = g_request->host;
|
||||
|
@ -471,6 +523,8 @@ make_new_host:
|
|||
host.id = boinc_db.insert_id();
|
||||
|
||||
got_host:
|
||||
// at this point we have a (possibly new) host record.
|
||||
//
|
||||
g_reply->host = host;
|
||||
g_reply->hostid = g_reply->host.id;
|
||||
// this tells client to updates its host ID
|
||||
|
@ -480,7 +534,8 @@ got_host:
|
|||
// This kludge forces this.
|
||||
}
|
||||
|
||||
// have user record in g_reply->user at this point
|
||||
// at this point we have user record in g_reply->user,
|
||||
// and host record in g_reply->host
|
||||
//
|
||||
|
||||
if (g_reply->user.teamid) {
|
||||
|
|
Loading…
Reference in New Issue