/*
 * The instrumentation of the COVALENT-INTRUSION-DETECTION-MIB module.
 * $Id: intrusion-mib.c 8676 2008-01-17 23:11:17Z ispringer $
 */

#include <stdio.h>
#include <sys/types.h>

/* Apache includes */
#include <httpd.h>
#include <http_config.h>

/* UCD-SNMP includes */
#include <ucd-snmp-config.h>
#include <asn1.h>
#include <snmp.h>
#include <snmp_api.h>
#include <snmp_impl.h>
#include <snmp_client.h>
#include <snmp_vars.h>
#include <var_struct.h>
#include <agent_trap.h>

#include <ietf-mibs/snmp-generic.h>
#include <covalent-snmp-config.h>
#include <covalent-snmp-logging.h>
#include <covalent-snmp-sconfig.h>

#include <ietf-mibs/snmp-generic.h>
#include <ietf-mibs/snmpv2-tc.h>
#include <covalent-mibs/intrusion-mib.h>

#include "log_config.h"

#if (PACKAGE == 1)
#define MODULE_NAME tripwire_module
#else
#define MODULE_NAME intrusion_module
#endif

/* We need to have our own main server_rec and their amount.  */
server_rec *intrusion_www_services;
int intrusion_www_service_count;
intrusion_entry_t *intrusion_stats;

static unsigned long local_long;
static unsigned char local_buf[256];

/* The SNMP logger function knows whereto the logging must go */
/* and is available in the server_config record of covalent-snmp. */
snmp_logger *intrusion2snmp_logger;
notification *send_notification;

#define CTINTRUSIONADMINSTATUS           1
#define CTINTRUSIONLASTTIME              2
#define CTINTRUSIONLASTDOC               3
#define CTINTRUSIONLASTCOUNTER           4
#define CTINTRUSIONNEWTIME               5
#define CTINTRUSIONNEWDOC                6
#define CTINTRUSIONNEWCOUNTER            7

static int
write_ctIntrusionAdminStatus(int action,
    u_char *var_val, u_char var_val_type, size_t var_val_len,
    u_char *statP, oid *name, size_t name_len)
{
    return SNMP_ERR_NOTWRITABLE;
}

int
getIntrusionWwwIndex_1(struct variable *vp,
        oid *name, size_t *namelength,
        oid *newname, size_t *newlength,
        int exact, server_rec *www_service)
{
oid *ptr;
int result;
intrusion_sconfig *sconf;

    *newlength = vp->namelen;
    memcpy((char *)newname, (char *)vp->name, *newlength * sizeof(oid));
    ptr = &(newname[ (*newlength)++ ]);
    *ptr = 1;
    while (www_service) {
        result = snmp_oid_compare(name, *namelength, newname, *newlength);
        if ((exact && (result == 0)) || (!exact && (result < 0))) {
            sconf = ap_get_module_config(www_service->module_config, &MODULE_NAME);
            return(sconf->dbhandle ? 1 : 2);
        }
        (*ptr)++;
        www_service = www_service->next;
    }
    return(0);
}

int
getIntrusionWwwIndex_2(struct variable *vp,
        oid *name, size_t *namelength,
        oid *newname, size_t *newlength,
        int exact)
{
oid *ptr;
int result;

    *newlength = vp->namelen;
    memcpy((char *)newname, (char *)vp->name, *newlength * sizeof(oid));
    ptr = &(newname[ (*newlength)++ ]);
    *ptr = 1;
    while (*ptr <= intrusion_www_service_count) {
        result = snmp_oid_compare(name, *namelength, newname, *newlength);
        if ((exact && (result == 0)) || (!exact && (result < 0))) {
            return(*ptr);
        }
        (*ptr)++;
    }
    return(0);
}

static unsigned char *
read_ctIntrusionServiceEntry(struct variable *vp,
    oid *name, size_t *length, int exact,
    size_t *var_len, WriteMethod **write_method)
{
oid newname[ MAX_OID_LEN ];
size_t newlength;
int index;
intrusion_entry_t *stats_rec;

    if (vp->magic == CTINTRUSIONADMINSTATUS) {
        local_long = getIntrusionWwwIndex_1(vp, name, length, newname, &newlength, exact,
                                                                        intrusion_www_services);
        if (!local_long) {
            return(NULL);
        }
    } else {
        index = getIntrusionWwwIndex_2(vp, name, length, newname, &newlength, exact);
        stats_rec = &(intrusion_stats[ (index - 1) ]);
        if ((index == 0) || (intrusion_stats == NULL)) {
            return(NULL);
        }
    }
    *length = newlength;
    memcpy( (char *)name,(char *)newname, *length * sizeof(oid));
    *write_method = 0;
    switch (vp->magic) {
    case CTINTRUSIONADMINSTATUS:
        *write_method = write_ctIntrusionAdminStatus;
        *var_len = sizeof(long);
        return (unsigned char *) &local_long;
    case CTINTRUSIONLASTTIME:
        *var_len = snmp_time2DateAndTime(stats_rec->lastTime, local_buf);
        return (unsigned char *) local_buf;
    case CTINTRUSIONLASTDOC:
        *var_len = strlen(stats_rec->lastDoc);
        return (unsigned char *) stats_rec->lastDoc;
    case CTINTRUSIONLASTCOUNTER:
        *var_len = sizeof(stats_rec->lastCounter);
        return (unsigned char *) &(stats_rec->lastCounter);
    case CTINTRUSIONNEWTIME:
        *var_len = snmp_time2DateAndTime(stats_rec->newTime, local_buf);
        return (unsigned char *) local_buf;
    case CTINTRUSIONNEWDOC:
        *var_len = strlen(stats_rec->newDoc);
        return (unsigned char *) stats_rec->newDoc;
    case CTINTRUSIONNEWCOUNTER:
        *var_len = sizeof(stats_rec->newCounter);
        return (unsigned char *) &(stats_rec->newCounter);
    }
    return NULL;
}

static oid ctIntrusionServiceEntry_oid[] = {1, 3, 6, 1, 4, 1, 6100, 8, 1, 1, 1};
static struct variable2 ctIntrusionServiceEntry_variables[] = {
    { CTINTRUSIONADMINSTATUS, ASN_INTEGER, RWRITE, read_ctIntrusionServiceEntry, 1, {1} },
    { CTINTRUSIONLASTTIME, ASN_OCTET_STR, RONLY, read_ctIntrusionServiceEntry, 1, {2} },
    { CTINTRUSIONLASTDOC, ASN_OCTET_STR, RONLY, read_ctIntrusionServiceEntry, 1, {3} },
    { CTINTRUSIONLASTCOUNTER, ASN_COUNTER, RONLY, read_ctIntrusionServiceEntry, 1, {4} },
    { CTINTRUSIONNEWTIME, ASN_OCTET_STR, RONLY, read_ctIntrusionServiceEntry, 1, {5} },
    { CTINTRUSIONNEWDOC, ASN_OCTET_STR, RONLY, read_ctIntrusionServiceEntry, 1, {6} },
    { CTINTRUSIONNEWCOUNTER, ASN_COUNTER, RONLY, read_ctIntrusionServiceEntry, 1, {7} },
};

int
count_services(server_rec *s)
{
int result = 0;
     while(s) {
        result++;
        s = s->next;
     }
     return(result);
}

int
init_intrusion_stats(server_rec *s, pool *p)
{
    if (intrusion2snmp_logger) {
        intrusion_www_service_count = count_services(s);
        intrusion_stats = (intrusion_entry_t *)
                         ap_pcalloc(p, (sizeof(intrusion_sconfig) *
                                 intrusion_www_service_count));
    }
    return(0);
}

int
get_wwwServiceIndex(server_rec *s)
{
module *mod_ptr;
covalent_snmp_sconfig *sconf_ptr;

    mod_ptr = ap_find_linked_module(CONDUCTOR_MODULE_NAME);
    if (mod_ptr == NULL) {
        /* The Covalent SNMP Conductor module is not there */
        return(-1);
    }
    sconf_ptr = (covalent_snmp_sconfig *)
                ap_get_module_config(s->module_config, mod_ptr);
    if (sconf_ptr == NULL) {
        /* server config record is not there */
        return(-1);
    }
    return(sconf_ptr->wwwServiceIndex);
}

void
invoke_intrusion_mib_update(request_rec *r, int last_or_new)
{
snmp_generic_log_record log_record;

    if (intrusion2snmp_logger) {
        log_record.type = CONDUCTOR_ANTIVIRUS_UPDATE_STATS;
        log_record.data.intrusion.wwwServiceIndex = get_wwwServiceIndex(r->server);
        log_record.data.intrusion.last_or_new = last_or_new;
        log_record.data.intrusion.time = time(NULL);
        strncpy(log_record.data.intrusion.doc, r->uri, MAX_SNMPADMINSTRING);
        intrusion2snmp_logger(&log_record);
    }
}


/* Oid definition of the Notification */
static oid ctIntrusionNotifications_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 8, 2, 0 };
/* Oid definition of the Notification Objects */
static oid ctIntrusionLastDocNotify_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 8, 1, 1, 1, 3, 0 };
static oid ctIntrusionNewDocNotify_oid[] = { 1, 3, 6, 1, 4, 1, 6100, 8, 1, 1, 1, 6, 0 };

void
generate_ctIntrusionNotification(int type, int index,
                                    char *str1, oid *objid1, int objidlen1)
{
/* varbind variables of the Notification Objects */
struct variable_list ctIntrusionNotification_var1;

    PRINT_INTRUSIONMIB("generate_ctIntrusionNotification");
    if (send_notification) {
        ctIntrusionNotifications_oid[9] = type;
        objid1[ 12 ] = index;
        if (str1 == NULL) {
            memset(&ctIntrusionNotification_var1, 0, sizeof(ctIntrusionNotification_var1));
            snmp_set_var_objid(&ctIntrusionNotification_var1, objid1, objidlen1);
            snmp_set_var_value(&ctIntrusionNotification_var1, str1, strlen(str1));
            ctIntrusionNotification_var1.type = ASN_OCTET_STR;
            ctIntrusionNotification_var1.next_variable = NULL;
        }
        send_notification(6, type, ctIntrusionNotifications_oid,
                                sizeof(ctIntrusionNotifications_oid)/sizeof(oid),
                                                &ctIntrusionNotification_var1);
    }
}

int
log_intrusion_info(snmp_generic_log_record *data)
{
intrusion_entry_t *entry;

    if (data->type == CONDUCTOR_INTRUSION_DOCUPDATE) {
        if (intrusion_stats) {
	    if (data->data.intrusion.last_or_new == 0) { /* last document */
                entry = &(intrusion_stats[ data->data.intrusion.wwwServiceIndex ]);
                if (strcmp(data->data.intrusion.doc, entry->lastDoc)) {
                    entry->lastTime = data->data.intrusion.time;
                    strncpy(entry->lastDoc, data->data.intrusion.doc, MAX_SNMPADMINSTRING);
                    entry->lastCounter++;
                    generate_ctIntrusionNotification(3,
                                        (data->data.intrusion.wwwServiceIndex + 1),
                                        entry->lastDoc, ctIntrusionLastDocNotify_oid,
                                        sizeof(ctIntrusionLastDocNotify_oid)/sizeof(oid));
                }
            } else { /* new document */
                entry = &(intrusion_stats[ data->data.intrusion.wwwServiceIndex ]);
                if (strcmp(data->data.intrusion.doc, entry->newDoc)) {
                    entry->newTime = data->data.intrusion.time;
                    strncpy(entry->newDoc, data->data.intrusion.doc, MAX_SNMPADMINSTRING);
                    entry->newCounter++;
                    generate_ctIntrusionNotification(4,
                                        (data->data.intrusion.wwwServiceIndex + 1),
                                        entry->lastDoc, ctIntrusionNewDocNotify_oid,
                                        sizeof(ctIntrusionNewDocNotify_oid)/sizeof(oid));
                }
            }
        }
        return(1);
    }
    return(0);
}

void
init_covalent_intrusion_detection_mib(server_rec *s, pool *p)
{
module *covalent_snmp_module;
covalent_snmp_sconfig *covalent_snmp_sconf;
external_mib_subtree *subtree;

    covalent_snmp_module = ap_find_linked_module(CONDUCTOR_MODULE_NAME);
    if (covalent_snmp_module == NULL) {
        /* The Covalent SNMP Conductor module is not there */
        return;
    }
    intrusion_www_services = s;
    covalent_snmp_sconf = ap_get_module_config(intrusion_www_services->module_config,
                                                covalent_snmp_module);
    if (covalent_snmp_sconf == NULL) {
        /* The Covalent SNMP Conductor sconfig is not there */
        return;
    }
    intrusion2snmp_logger = covalent_snmp_sconf->snmp_logger;
    send_notification = covalent_snmp_sconf->invoke_notification;
    subtree = (external_mib_subtree *)
                ap_push_array(covalent_snmp_sconf->external_mib_subtrees);
    if (subtree) {
        subtree->base_oid = ctIntrusionServiceEntry_oid;
        subtree->base_oid_length = sizeof(ctIntrusionServiceEntry_oid) /
                                sizeof(ctIntrusionServiceEntry_oid[0]);
        subtree->variables = (struct variables *)ctIntrusionServiceEntry_variables;
        subtree->variable_size = sizeof(ctIntrusionServiceEntry_variables[0]);
        subtree->variables_amount = sizeof(ctIntrusionServiceEntry_variables) /
                                sizeof(ctIntrusionServiceEntry_variables[0]);
        subtree->init = init_intrusion_stats;
        subtree->logging_sink = log_intrusion_info;
    }
}
