/* This file is part of Kismet Kismet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kismet is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Kismet; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include "alertracker.h" #include "configfile.h" const char *ALERT_fields_text[] = { "sec", "usec", "header", "bssid", "source", "dest", "other", "channel", "text", NULL }; // alert. data = ALERT_data int Protocol_ALERT(PROTO_PARMS) { kis_alert_info *info = (kis_alert_info *) data; ostringstream osstr; cache->Filled(field_vec->size()); for (unsigned int x = 0; x < field_vec->size(); x++) { unsigned int fnum = (*field_vec)[x]; if (fnum >= ALERT_maxfield) { out_string = "Unknown field requested."; return -1; } osstr.str(""); // Shortcut test the cache once and print/bail immediately if (cache->Filled(fnum)) { out_string += cache->GetCache(fnum) + " "; continue; } switch(fnum) { case ALERT_header: cache->Cache(fnum, info->header); break; case ALERT_sec: osstr << (int) info->tm.tv_sec; cache->Cache(fnum, osstr.str()); break; case ALERT_usec: osstr << (int) info->tm.tv_usec; cache->Cache(fnum, osstr.str()); break; case ALERT_bssid: cache->Cache(fnum, info->bssid.Mac2String()); break; case ALERT_source: cache->Cache(fnum, info->source.Mac2String()); break; case ALERT_dest: cache->Cache(fnum, info->dest.Mac2String()); break; case ALERT_other: cache->Cache(fnum, info->other.Mac2String()); break; case ALERT_channel: osstr << info->channel; cache->Cache(fnum, osstr.str()); break; case ALERT_text: cache->Cache(fnum, "\001" + info->text + "\001"); break; default: out_string = "Unknown field requested."; return -1; break; } // print the newly filled in cache out_string += cache->GetCache(fnum) + " "; } return 1; } void Protocol_ALERT_enable(PROTO_ENABLE_PARMS) { globalreg->alertracker->BlitBacklogged(in_fd); } Alertracker::Alertracker() { fprintf(stderr, "*** Alertracker::Alertracker() called with no global registry. Bad.\n"); } Alertracker::Alertracker(GlobalRegistry *in_globalreg) { globalreg = in_globalreg; next_alert_id = 0; if (globalreg->kismet_config == NULL) { fprintf(stderr, "FATAL OOPS: Alertracker called with null config\n"); exit(1); } if (globalreg->packetchain == NULL) { fprintf(stderr, "FATAL OOPS: Alertracker called with null packetchain\n"); exit(1); } if (globalreg->kisnetserver == NULL) { fprintf(stderr, "FATAL OOPS: Alertracker called with null kisnetserver\n"); exit(1); } if (globalreg->kismet_config->FetchOpt("alertbacklog") != "") { int scantmp; if (sscanf(globalreg->kismet_config->FetchOpt("alertbacklog").c_str(), "%d", &scantmp) != 1 || scantmp < 0) { globalreg->messagebus->InjectMessage("Illegal value for 'alertbacklog' " "in config file", MSGFLAG_FATAL); globalreg->fatal_condition = 1; return; } num_backlog = scantmp; } // Register the alert component _PCM(PACK_COMP_ALERT) = globalreg->packetchain->RegisterPacketComponent("alert"); // Register the alert protocol _NPM(PROTO_REF_ALERT) = globalreg->kisnetserver->RegisterProtocol("ALERT", 0, 1, ALERT_fields_text, &Protocol_ALERT, &Protocol_ALERT_enable, this); // Register a KISMET alert type with no rate restrictions _ARM(ALERT_REF_KISMET) = RegisterAlert("KISMET", sat_day, 0, sat_day, 0); // Parse config file vector of all alerts if (ParseAlertConfig(globalreg->kismet_config) < 0) { _MSG("Failed to parse alert values from Kismet config file", MSGFLAG_FATAL); globalreg->fatal_condition = 1; return; } _MSG("Created alert tracker...", MSGFLAG_INFO); } Alertracker::~Alertracker() { for (map::iterator x = alert_ref_map.begin(); x != alert_ref_map.end(); ++x) delete x->second; } int Alertracker::RegisterAlert(const char *in_header, alert_time_unit in_unit, int in_rate, alert_time_unit in_burstunit, int in_burst) { char err[1024]; // Bail if this header is registered if (alert_name_map.find(in_header) != alert_name_map.end()) { snprintf(err, 1024, "RegisterAlert() header already registered '%s'", in_header); globalreg->messagebus->InjectMessage(err, MSGFLAG_ERROR); return -1; } // Bail if the rates are impossible if (in_burstunit > in_unit) { snprintf(err, 1024, "Registering alert '%s' failed, time unit for " "burst rate must be <= time unit for max rate", in_header); globalreg->messagebus->InjectMessage(err, MSGFLAG_ERROR); return -1; } alert_rec *arec = new alert_rec; arec->ref_index = next_alert_id++; arec->header = StrUpper(in_header); arec->limit_unit = in_unit; arec->burst_unit = in_burstunit; arec->limit_rate = in_rate; arec->limit_burst = in_burst; arec->burst_sent = 0; arec->time_last = 0; alert_name_map[arec->header] = arec->ref_index; alert_ref_map[arec->ref_index] = arec; return arec->ref_index; } int Alertracker::FetchAlertRef(string in_header) { if (alert_name_map.find(in_header) != alert_name_map.end()) return alert_name_map[in_header]; return -1; } int Alertracker::CheckTimes(alert_rec *arec) { // Is this alert rate-limited? If not, shortcut out and send it if (arec->limit_rate == 0) { return 1; } struct timeval now; gettimeofday(&now, NULL); // If the last time we sent anything was longer than the main rate limit, // then we reset back to empty if (arec->time_last < (now.tv_sec - alert_time_unit_conv[arec->limit_unit])) { arec->total_sent = 0; arec->burst_sent = 0; return 1; } // If the last time we sent anything was longer than the burst rate, we can // reset the burst to 0 if (arec->time_last < (now.tv_sec - alert_time_unit_conv[arec->burst_unit])) { arec->burst_sent = 0; } // If we're under the limit on both, we're good to go if (arec->burst_sent < arec->limit_burst && arec->total_sent < arec->limit_rate) return 1; return 0; } int Alertracker::PotentialAlert(int in_ref) { map::iterator aritr = alert_ref_map.find(in_ref); if (aritr == alert_ref_map.end()) return -1; alert_rec *arec = aritr->second; return CheckTimes(arec); } int Alertracker::RaiseAlert(int in_ref, kis_packet *in_pack, mac_addr bssid, mac_addr source, mac_addr dest, mac_addr other, int in_channel, string in_text) { map::iterator aritr = alert_ref_map.find(in_ref); if (aritr == alert_ref_map.end()) return -1; alert_rec *arec = aritr->second; if (CheckTimes(arec) != 1) return 0; kis_alert_info *info = new kis_alert_info; info->header = arec->header; gettimeofday(&(info->tm), NULL); info->bssid = bssid; info->source = source; info->dest = dest; info->other = other; info->channel = in_channel; info->text = in_text; // Increment and set the timers arec->burst_sent++; arec->total_sent++; arec->time_last = time(0); alert_backlog.push_back(info); if ((int) alert_backlog.size() > num_backlog) { delete alert_backlog[0]; alert_backlog.erase(alert_backlog.begin()); } // Try to get the existing alert info kis_alert_component *acomp = (kis_alert_component *) in_pack->fetch(_PCM(PACK_COMP_ALERT)); // if we don't have an alert container, make one on this packet if (acomp == NULL) { acomp = new kis_alert_component; in_pack->insert(_PCM(PACK_COMP_ALERT), acomp); } // Attach it to the packet acomp->alert_vec.push_back(info); // Send it to the network as an alert globalreg->kisnetserver->SendToAll(_NPM(PROTO_REF_ALERT), (void *) info); // Send the text info globalreg->messagebus->InjectMessage((info->header + " " + info->text), MSGFLAG_ALERT); return 1; } void Alertracker::BlitBacklogged(int in_fd) { for (unsigned int x = 0; x < alert_backlog.size(); x++) { kis_protocol_cache cache; globalreg->kisnetserver->SendToClient(in_fd, _NPM(PROTO_REF_ALERT), (void *) alert_backlog[x], &cache); } } int Alertracker::ParseAlertStr(string alert_str, string *ret_name, alert_time_unit *ret_limit_unit, int *ret_limit_rate, alert_time_unit *ret_limit_burst, int *ret_burst_rate) { char err[1024]; vector tokens = StrTokenize(alert_str, ","); if (tokens.size() != 3) { snprintf(err, 1024, "Malformed limits for alert '%s'", alert_str.c_str()); globalreg->messagebus->InjectMessage(err, MSGFLAG_ERROR); return -1; } (*ret_name) = StrLower(tokens[0]); if (ParseRateUnit(StrLower(tokens[1]), ret_limit_unit, ret_limit_rate) != 1 || ParseRateUnit(StrLower(tokens[2]), ret_limit_burst, ret_burst_rate) != 1) { snprintf(err, 1024, "Malformed limits for alert '%s'", alert_str.c_str()); globalreg->messagebus->InjectMessage(err, MSGFLAG_ERROR); return -1; } return 1; } // Split up a rate/unit string into real values int Alertracker::ParseRateUnit(string in_ru, alert_time_unit *ret_unit, int *ret_rate) { char err[1024]; vector units = StrTokenize(in_ru, "/"); if (units.size() == 1) { // Unit is per minute if not specified (*ret_unit) = sat_minute; } else { // Parse the string unit if (units[1] == "sec" || units[1] == "second") { (*ret_unit) = sat_second; } else if (units[1] == "min" || units[1] == "minute") { (*ret_unit) = sat_minute; } else if (units[1] == "hr" || units[1] == "hour") { (*ret_unit) = sat_hour; } else if (units[1] == "day") { (*ret_unit) = sat_day; } else { snprintf(err, 1024, "Alertracker - Invalid time unit for alert rate '%s'", units[1].c_str()); globalreg->messagebus->InjectMessage(err, MSGFLAG_ERROR); return -1; } } // Get the number if (sscanf(units[0].c_str(), "%d", ret_rate) != 1) { snprintf(err, 1024, "Alertracker - Invalid rate '%s' for alert", units[0].c_str()); globalreg->messagebus->InjectMessage(err, MSGFLAG_ERROR); return -1; } return 1; } int Alertracker::ParseAlertConfig(ConfigFile *in_conf) { vector clines = in_conf->FetchOptVec("alert"); for (unsigned int x = 0; x < clines.size(); x++) { alert_conf_rec *rec = new alert_conf_rec; if (ParseAlertStr(clines[x], &(rec->header), &(rec->limit_unit), &(rec->limit_rate), &(rec->burst_unit), &(rec->limit_burst)) < 0) { _MSG("Invalid alert line in config file: " + clines[x], MSGFLAG_FATAL); globalreg->fatal_condition = 1; return -1; } alert_conf_map[StrLower(rec->header)] = rec; } return 1; } int Alertracker::ActivateConfiguredAlert(const char *in_header) { string hdr = StrLower(in_header); if (alert_conf_map.find(hdr) == alert_conf_map.end()) { _MSG("Alert type " + string(in_header) + " not found in list of activated " "alerts.", MSGFLAG_INFO); return -1; } alert_conf_rec *rec = alert_conf_map[hdr]; return RegisterAlert(rec->header.c_str(), rec->limit_unit, rec->limit_rate, rec->burst_unit, rec->limit_burst); } const vector *Alertracker::FetchBacklog() { return (const vector *) &alert_backlog; }