static int analyzer_docsis_reg_status_update(struct analyzer_docsis_priv *priv, struct analyzer_docsis_cm *cm, enum docsis_mmt_rng_status new_status, ptime ts, struct proto_process_stack *stack, unsigned int stack_index) { if (cm->ranging_status == new_status) return POM_OK; if (event_has_listener(priv->evt_cm_reg_status)) { struct event *evt = event_alloc(priv->evt_cm_reg_status); if (!evt) { pom_mutex_unlock(&priv->lock); return POM_ERR; } struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[analyzer_docsis_cm_reg_status_old].value, cm->ranging_status); data_set(evt_data[analyzer_docsis_cm_reg_status_old]); PTYPE_UINT8_SETVAL(evt_data[analyzer_docsis_cm_reg_status_new].value, new_status); data_set(evt_data[analyzer_docsis_cm_reg_status_new]); PTYPE_MAC_SETADDR(evt_data[analyzer_docsis_cm_reg_status_mac].value, cm->mac); data_set(evt_data[analyzer_docsis_cm_reg_status_mac]); PTYPE_UINT8_SETVAL(evt_data[analyzer_docsis_cm_reg_status_timeout].value, T4_TIMEOUT * cm->t4_multiplier); data_set(evt_data[analyzer_docsis_cm_reg_status_timeout]); if (event_process(evt, stack, stack_index, ts) != POM_OK) { pom_mutex_unlock(&priv->lock); return POM_ERR; } } cm->ranging_status = new_status; return POM_OK; }
/* * machine_id records are used to get the selinux context associated to a * guest. */ int process_machine_id_event(auparse_state_t *au) { uid_t uid; time_t time; const char *seclevel, *uuid, *name; struct event *event; int success; seclevel = get_seclevel(auparse_find_field(au, "vm-ctx")); if (seclevel == NULL) { if (debug) fprintf(stderr, "Security context not found for " "MACHINE_ID event.\n"); } if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) return 0; event = event_alloc(); if (event == NULL) return 1; event->type = ET_MACHINE_ID; event->uuid = copy_str(uuid); event->name = copy_str(name); event->success = success; event->seclevel = copy_str(seclevel); event->uid = uid; event->start = time; add_proof(event, au); if (list_append(events, event) == NULL) { event_free(event); return 1; } return 0; }
ACL_EVENT *event_new_select(void) { ACL_EVENT *eventp; EVENT_SELECT *ev; eventp = event_alloc(sizeof(EVENT_SELECT)); snprintf(eventp->name, sizeof(eventp->name), "events - select"); eventp->event_mode = ACL_EVENT_SELECT; eventp->use_thread = 0; eventp->loop_fn = event_loop; eventp->free_fn = event_free; eventp->enable_read_fn = event_enable_read; eventp->enable_write_fn = event_enable_write; eventp->enable_listen_fn = event_enable_listen; eventp->disable_read_fn = event_disable_read; eventp->disable_write_fn = event_disable_write; eventp->disable_readwrite_fn = event_disable_readwrite; eventp->isrset_fn = event_isrset; eventp->iswset_fn = event_iswset; eventp->isxset_fn = event_isxset; eventp->timer_request = event_timer_request; eventp->timer_cancel = event_timer_cancel; eventp->timer_keep = event_timer_keep; eventp->timer_ifkeep = event_timer_ifkeep; ev = (EVENT_SELECT*) eventp; FD_ZERO(&ev->rmask); FD_ZERO(&ev->wmask); FD_ZERO(&ev->xmask); return eventp; }
ACL_EVENT *event_new_select_thr(void) { EVENT_SELECT_THR *event_thr; event_thr = (EVENT_SELECT_THR*) event_alloc(sizeof(EVENT_SELECT_THR)); snprintf(event_thr->event.event.name, sizeof(event_thr->event.event.name), "thread events - select"); event_thr->event.event.event_mode = ACL_EVENT_SELECT; event_thr->event.event.use_thread = 1; event_thr->event.event.loop_fn = event_loop; event_thr->event.event.free_fn = event_free; event_thr->event.event.add_dog_fn = event_add_dog; event_thr->event.event.enable_read_fn = event_enable_read; event_thr->event.event.enable_write_fn = event_enable_write; event_thr->event.event.enable_listen_fn = event_enable_listen; event_thr->event.event.disable_readwrite_fn = event_disable_readwrite; event_thr->event.event.isrset_fn = event_isrset; event_thr->event.event.iswset_fn = event_iswset; event_thr->event.event.isxset_fn = event_isxset; event_thr->event.event.timer_request = event_timer_request_thr; event_thr->event.event.timer_cancel = event_timer_cancel_thr; event_thr->event.event.timer_keep = event_timer_keep_thr; event_thr->event.event.timer_ifkeep = event_timer_ifkeep_thr; FD_ZERO(&event_thr->rmask); FD_ZERO(&event_thr->wmask); FD_ZERO(&event_thr->xmask); LOCK_INIT(&event_thr->event.tm_mutex); LOCK_INIT(&event_thr->event.tb_mutex); return (ACL_EVENT *) event_thr; }
int add_start_guest_event(auparse_state_t *au) { struct event *start; uid_t uid; time_t time; const char *uuid, *name; int success; list_node_t *it; /* Just skip this record if it failed to get some of the fields */ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) return 0; /* On failure, loop backwards to update all the resources associated to * the last session of this guest. When a machine_id or a stop event is * found the loop can be broken because a machine_id is created at the * beginning of a session and a stop event indicates a previous * session. */ if (!success) { for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success && event->uuid && strcmp(uuid, event->uuid) == 0) { if (event->type == ET_STOP || event->type == ET_MACHINE_ID) { /* An old session found. */ break; } else if (event->type == ET_RES && event->end == 0) { event->end = time; add_proof(event, au); } } } } start = event_alloc(); if (start == NULL) return 1; start->type = ET_START; start->uuid = copy_str(uuid); start->name = copy_str(name); start->success = success; start->uid = uid; start->start = time; auparse_first_record(au); if (auparse_find_field(au, "vm-pid")) start->pid = auparse_get_field_int(au); add_proof(start, au); if (list_append(events, start) == NULL) { event_free(start); return 1; } return 0; }
ACL_EVENT *event_new_select_thr(void) { const char *myname = "event_new_select_thr"; EVENT_SELECT_THR *event_thr; int status; char ebuf[256]; event_thr = (EVENT_SELECT_THR*) event_alloc(sizeof(EVENT_SELECT_THR)); snprintf(event_thr->event.event.name, sizeof(event_thr->event.event.name), "thread events - select"); event_thr->event.event.event_mode = ACL_EVENT_SELECT; event_thr->event.event.use_thread = 1; event_thr->event.event.loop_fn = event_loop; event_thr->event.event.free_fn = event_free; event_thr->event.event.add_dog_fn = event_add_dog; event_thr->event.event.enable_read_fn = event_enable_read; event_thr->event.event.enable_write_fn = event_enable_write; event_thr->event.event.enable_listen_fn = event_enable_listen; event_thr->event.event.disable_readwrite_fn = event_disable_readwrite; event_thr->event.event.isrset_fn = event_isrset; event_thr->event.event.iswset_fn = event_iswset; event_thr->event.event.isxset_fn = event_isxset; event_thr->event.event.timer_request = event_timer_request_thr; event_thr->event.event.timer_cancel = event_timer_cancel_thr; event_thr->event.event.timer_keep = event_timer_keep_thr; event_thr->event.event.timer_ifkeep = event_timer_ifkeep_thr; FD_ZERO(&event_thr->rmask); FD_ZERO(&event_thr->wmask); FD_ZERO(&event_thr->xmask); status = acl_pthread_mutex_init(&event_thr->event.tm_mutex, NULL); if (status != 0) { acl_msg_fatal("%s(%d)->%s: pthread_mutex_init error=%s", __FILE__, __LINE__, myname, acl_last_strerror(ebuf, sizeof(ebuf))); } status = acl_pthread_mutex_init(&event_thr->event.tb_mutex, NULL); if (status != 0) { acl_msg_fatal("%s(%d)->%s: pthread_mutex_init error=%s", __FILE__, __LINE__, myname, acl_last_strerror(ebuf, sizeof(ebuf))); } return ((ACL_EVENT *) event_thr); }
int add_resource(auparse_state_t *au, const char *uuid, uid_t uid, time_t time, const char *name, int success, const char *reason, const char *res_type, const char *res) { if (!is_resource(res)) return 0; struct event *event = event_alloc(); if (event == NULL) return 1; event->type = ET_RES; event->uuid = copy_str(uuid); event->name = copy_str(name); event->success = success; event->reason = copy_str(reason); event->res_type = copy_str(res_type); event->res = copy_str(res); event->uid = uid; event->start = time; add_proof(event, au); /* Get cgroup specific fields. */ if (strcmp("cgroup", res_type) == 0) { event->cgroup_class = copy_str(auparse_find_field(au, "class")); if (event->cgroup_class) { const char *detail = NULL; if (strcmp("path", event->cgroup_class) == 0) { if (auparse_find_field(au, "path")) detail = auparse_interpret_field(au); } else if (strcmp("major", event->cgroup_class) == 0) { detail = auparse_find_field(au, "category"); } event->cgroup_detail = copy_str(detail); } event->cgroup_acl = copy_str(auparse_find_field(au, "acl")); } if (list_append(events, event) == NULL) { event_free(event); return 1; } return 0; }
int process_shutdown(auparse_state_t *au) { uid_t uid = -1; time_t time = 0; struct event *down; list_node_t *it; int success = 0; if (extract_virt_fields(au, NULL, &uid, &time, NULL, &success)) return 0; for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success) { if (event->type == ET_START || event->type == ET_RES) { if (event->end == 0) { event->end = time; add_proof(event, au); } } else if (event->type == ET_DOWN) { break; } } } down = event_alloc(); if (down == NULL) return 1; down->type = ET_DOWN; down->uid = uid; down->start = time; down->success = success; add_proof(down, au); if (list_append(events, down) == NULL) { event_free(down); return 1; } return 0; }
static int analyzer_smtp_event_process_begin(struct event *evt, void *obj, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_smtp_priv *apriv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; if (!s->ce) return POM_ERR; // Only process stuff if we have the DATA event or if we already have an event struct event_reg *evt_reg = event_get_reg(evt); struct data *evt_data = event_get_data(evt); struct analyzer_smtp_ce_priv *cpriv = conntrack_get_priv(s->ce, analyzer); // It's expected that an SMTP connection will always contain at least one message // So we always create the cpriv and event, no matter what if (!cpriv) { cpriv = malloc(sizeof(struct analyzer_smtp_ce_priv)); if (!cpriv) { pom_oom(sizeof(struct analyzer_smtp_ce_priv)); return POM_ERR; } memset(cpriv, 0, sizeof(struct analyzer_smtp_ce_priv)); if (conntrack_add_priv(s->ce, analyzer, cpriv, analyzer_smtp_ce_priv_cleanup) != POM_OK) { free(cpriv); return POM_ERR; } } if (!cpriv->evt_msg) { cpriv->evt_msg = event_alloc(apriv->evt_msg); if (!cpriv->evt_msg) return POM_ERR; } struct data *msg_data = event_get_data(cpriv->evt_msg); if (evt_reg == apriv->evt_cmd) { if (!cpriv->common_data_fetched) analyzer_smtp_event_fetch_common_data(cpriv, stack, stack_index, POM_DIR_REVERSE(s->direction)); // Process commands // A message was being transmitted and we recevied a new command if (event_is_started(cpriv->evt_msg)) { event_process_end(cpriv->evt_msg); cpriv->evt_msg = NULL; } char *cmd = PTYPE_STRING_GETVAL(evt_data[proto_smtp_cmd_name].value); if (!cmd) return POM_OK; char *arg = PTYPE_STRING_GETVAL(evt_data[proto_smtp_cmd_arg].value); if (arg) { while (*arg == ' ') arg++; } if (!strcasecmp(cmd, "MAIL")) { if (strncasecmp(arg, "FROM:", strlen("FROM:"))) { pomlog(POMLOG_DEBUG "Unparseable MAIL command"); return POM_OK; } arg += strlen("FROM:"); while (*arg == ' ') arg++; if (*arg == '<') arg++; size_t len; char *end = strchr(arg, '>'); if (end) len = end - arg; else len = strlen(arg); PTYPE_STRING_SETVAL_N(msg_data[analyzer_smtp_msg_from].value, arg, len); data_set(msg_data[analyzer_smtp_msg_from]); cpriv->last_cmd = analyzer_smtp_last_cmd_mail_from; } else if (!strcasecmp(cmd, "RCPT")) { if (strncasecmp(arg, "TO:", strlen("TO:"))) { pomlog(POMLOG_DEBUG "Unparseable RCPT command"); return POM_OK; } arg += strlen("TO:"); while (*arg == ' ') arg++; if (*arg == '<') arg++; size_t len; char *end = strchr(arg, '>'); if (end) len = end - arg; else len = strlen(arg); struct ptype *to = ptype_alloc("string"); if (!to) return POM_ERR; PTYPE_STRING_SETVAL_N(to, arg, len); if (data_item_add_ptype(msg_data, analyzer_smtp_msg_to, strdup("to"), to) != POM_OK) { ptype_cleanup(to); return POM_ERR; } cpriv->last_cmd = analyzer_smtp_last_cmd_rcpt_to; } else if (!strcasecmp(cmd, "DATA")) { cpriv->last_cmd = analyzer_smtp_last_cmd_data; if (!event_is_started(cpriv->evt_msg)) { analyzer_smtp_event_fill_common_data(cpriv, msg_data); event_process_begin(cpriv->evt_msg, stack, stack_index, event_get_timestamp(evt)); } else { pomlog(POMLOG_DEBUG "Message event already started !"); } } else if (!strcasecmp(cmd, "RSET")) { // Cleanup the event event_cleanup(cpriv->evt_msg); cpriv->evt_msg = NULL; cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else if (!strcasecmp(cmd, "HELO") || !strcasecmp(cmd, "EHLO")) { if (cpriv->client_hello) { pomlog(POMLOG_DEBUG "We already have a client hello !"); free(cpriv->client_hello); } cpriv->client_hello = strdup(arg); if (!cpriv->client_hello) { pom_oom(strlen(arg) + 1); return POM_ERR; } cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else if (!strcasecmp(cmd, "AUTH")) { if (!strncasecmp(arg, "PLAIN", strlen("PLAIN"))) { arg += strlen("PLAIN"); while (*arg == ' ') arg++; if (cpriv->evt_auth) { event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; } if (strlen(arg)) { if (analyzer_smtp_parse_auth_plain(apriv, cpriv, arg) == POM_OK) { event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); cpriv->last_cmd = analyzer_smtp_last_cmd_auth_plain_creds; } } else { cpriv->last_cmd = analyzer_smtp_last_cmd_auth_plain; } } else if (!strncasecmp(arg, "LOGIN", strlen("LOGIN"))) { arg += strlen("LOGIN"); while (*arg == ' ') arg++; if (cpriv->evt_auth) { event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; } cpriv->evt_auth = event_alloc(apriv->evt_auth); if (!cpriv->evt_auth) return POM_ERR; struct data *auth_data = event_get_data(cpriv->evt_auth); analyzer_smtp_event_fill_common_data(cpriv, auth_data); // Set the authentication type PTYPE_STRING_SETVAL(auth_data[analyzer_smtp_auth_type].value, "LOGIN"); data_set(auth_data[analyzer_smtp_auth_type]); if (strlen(arg)) { char *username = NULL; size_t out_len = 0; struct ptype *username_pt = NULL; if (decoder_decode_simple("base64", arg, strlen(arg), &username, &out_len) == POM_OK) { username_pt = ptype_alloc("string"); if (username_pt) { PTYPE_STRING_SETVAL_P(username_pt, username); if (data_item_add_ptype(auth_data, analyzer_smtp_auth_params, strdup("username"), username_pt) != POM_OK) { ptype_cleanup(username_pt); event_cleanup(cpriv->evt_auth); cpriv->evt_auth = NULL; username_pt = NULL; } } else { free(username); } } if (!username_pt) { cpriv->last_cmd = analyzer_smtp_last_cmd_other; event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); } } else { cpriv->last_cmd = analyzer_smtp_last_cmd_auth_login; } } } else if (cpriv->last_cmd == analyzer_smtp_last_cmd_auth_plain) { // We are expecting the credentials right now if (analyzer_smtp_parse_auth_plain(apriv, cpriv, cmd) == POM_OK) { event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); cpriv->last_cmd = analyzer_smtp_last_cmd_auth_plain_creds; } else { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } } else if (cpriv->last_cmd == analyzer_smtp_last_cmd_auth_login) { char *username = NULL; size_t out_len = 0; struct ptype *username_pt = NULL; if (decoder_decode_simple("base64", cmd, strlen(cmd), &username, &out_len) == POM_OK) { username_pt = ptype_alloc("string"); if (username_pt) { PTYPE_STRING_SETVAL_P(username_pt, username); struct data *auth_data = event_get_data(cpriv->evt_auth); if (data_item_add_ptype(auth_data, analyzer_smtp_auth_params, strdup("username"), username_pt) != POM_OK) { ptype_cleanup(username_pt); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; username_pt = NULL; } } else { free(username); } } if (!username_pt) { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else { event_process_begin(cpriv->evt_auth, stack, stack_index, event_get_timestamp(evt)); cpriv->last_cmd = analyzer_smtp_last_cmd_auth_login_user; } } else if (cpriv->last_cmd == analyzer_smtp_last_cmd_auth_login_user) { char *password = NULL; size_t out_len = 0; struct ptype *password_pt = NULL; if (decoder_decode_simple("base64", cmd, strlen(cmd), &password, &out_len) == POM_OK) { password_pt = ptype_alloc("string"); if (password_pt) { PTYPE_STRING_SETVAL_P(password_pt, password); struct data *auth_data = event_get_data(cpriv->evt_auth); if (data_item_add_ptype(auth_data, analyzer_smtp_auth_params, strdup("password"), password_pt) != POM_OK) { ptype_cleanup(password_pt); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; password_pt = NULL; } } else { free(password); } } if (!password_pt) { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } else { cpriv->last_cmd = analyzer_smtp_last_cmd_auth_login_pass; } } else { cpriv->last_cmd = analyzer_smtp_last_cmd_other; } } else if (evt_reg == apriv->evt_reply) { if (!cpriv->common_data_fetched) analyzer_smtp_event_fetch_common_data(cpriv, stack, stack_index, s->direction); // Process replies uint16_t code = *PTYPE_UINT16_GETVAL(evt_data[proto_smtp_reply_code].value); switch (cpriv->last_cmd) { default: case analyzer_smtp_last_cmd_other: if (code == 220 && evt_data[proto_smtp_reply_text].items && evt_data[proto_smtp_reply_text].items->value) { // STARTTLS returns 220 as well so ignore extra code 220 if (!cpriv->server_hello) { char *helo = PTYPE_STRING_GETVAL(evt_data[proto_smtp_reply_text].items->value); cpriv->server_hello = strdup(helo); if (!cpriv->server_hello) { pom_oom(strlen(helo) + 1); return POM_ERR; } } } break; case analyzer_smtp_last_cmd_mail_from: if (code != 250) { // FROM is invalid data_unset(msg_data[analyzer_smtp_msg_from]); } break; case analyzer_smtp_last_cmd_rcpt_to: // For now just don't do anything // It's best to keep a destination in there even if it's invalid or denied break; case analyzer_smtp_last_cmd_data: if (code == 354) { // The message is starting, keep last_cmd intact return POM_OK; } // Message is over (if ever transmited) if (event_is_started(cpriv->evt_msg)) { struct data *msg_data = event_get_data(cpriv->evt_msg); PTYPE_UINT16_SETVAL(msg_data[analyzer_smtp_msg_result].value, code); data_set(msg_data[analyzer_smtp_msg_result]); event_process_end(cpriv->evt_msg); cpriv->evt_msg = NULL; } break; case analyzer_smtp_last_cmd_auth_plain: case analyzer_smtp_last_cmd_auth_login: case analyzer_smtp_last_cmd_auth_login_user: // Check if authentication phase can continue if (code == 334) { // Don't reset cpriv->last_cmd return POM_OK; } else { struct data *evt_data = event_get_data(cpriv->evt_auth); PTYPE_BOOL_SETVAL(evt_data[analyzer_smtp_auth_success].value, 0); data_set(evt_data[analyzer_smtp_auth_success]); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; } break; case analyzer_smtp_last_cmd_auth_plain_creds: case analyzer_smtp_last_cmd_auth_login_pass: { // We just processed the credentials struct data *auth_data = event_get_data(cpriv->evt_auth); char success = 0; if (code == 235) success = 1; PTYPE_BOOL_SETVAL(auth_data[analyzer_smtp_auth_success].value, success); data_set(auth_data[analyzer_smtp_auth_success]); event_process_end(cpriv->evt_auth); cpriv->evt_auth = NULL; break; } } cpriv->last_cmd = analyzer_smtp_last_cmd_other; } return POM_OK; }
static int analyzer_smtp_parse_auth_plain(struct analyzer_smtp_priv *apriv, struct analyzer_smtp_ce_priv *cpriv, char *auth_plain) { // Parse SASL AUTH PLAIN as described in RFC 4616 // The decoded arg must be at least 3 bytes if (strlen(auth_plain) < 4 || memchr(auth_plain, '=', 4)) { pomlog(POMLOG_DEBUG "AUTH PLAIN argument too short"); return POM_OK; } // Allocate the event cpriv->evt_auth = event_alloc(apriv->evt_auth); if (!cpriv->evt_auth) return POM_ERR; struct data *evt_data = event_get_data(cpriv->evt_auth); analyzer_smtp_event_fill_common_data(cpriv, evt_data); // Set the authentication type PTYPE_STRING_SETVAL(evt_data[analyzer_smtp_auth_type].value, "PLAIN"); data_set(evt_data[analyzer_smtp_auth_type]); // Parse the authentication stuff char *creds_str = NULL; size_t out_len = 0; if (decoder_decode_simple("base64", auth_plain, strlen(auth_plain), &creds_str, &out_len) != POM_OK) { pomlog(POMLOG_DEBUG "Unable to decode AUTH PLAIN message"); return POM_OK; } if (out_len < 3) { pomlog(POMLOG_DEBUG "Invalid decoded AUTH PLAIN data"); return POM_OK; } char *tmp = creds_str; // Add the identity if (strlen(tmp)) { // SASL AUTH PLAIN specifies struct ptype *identity = ptype_alloc("string"); if (!identity) goto err; PTYPE_STRING_SETVAL(identity, tmp); if (data_item_add_ptype(evt_data, analyzer_smtp_auth_params, strdup("identity"), identity) != POM_OK) { ptype_cleanup(identity); goto err; } } tmp += strlen(tmp) + 1; // Add the username struct ptype *username = ptype_alloc("string"); if (!username) goto err; PTYPE_STRING_SETVAL(username, tmp); if (data_item_add_ptype(evt_data, analyzer_smtp_auth_params, strdup("username"), username) != POM_OK) { ptype_cleanup(username); goto err; } tmp += strlen(tmp) + 1; // Add the password struct ptype *password = ptype_alloc("string"); if (!password) goto err; PTYPE_STRING_SETVAL(password, tmp); if (data_item_add_ptype(evt_data, analyzer_smtp_auth_params, strdup("password"), password) != POM_OK) { ptype_cleanup(password); goto err; } free(creds_str); return POM_OK; err: event_cleanup(cpriv->evt_auth); cpriv->evt_auth = NULL; free(creds_str); return POM_ERR; }
int process_avc_apparmor_source(auparse_state_t *au) { uid_t uid = -1; time_t time = 0; struct event *avc; const char *target; /* Get the target object. */ if (auparse_find_field(au, "name") == NULL) { if (debug) { auparse_first_record(au); fprintf(stderr, "Couldn't get the resource name from " "the AVC record: %s\n", auparse_get_record_text(au)); } return 0; } target = auparse_interpret_field(au); /* Loop backwards to find a guest session with the target object * assigned to. */ struct list_node_t *it; struct event *res = NULL; for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success) { if (event->type == ET_DOWN) { /* It's just possible to find a matching guest * session in the current host session. */ break; } else if (event->type == ET_RES && event->end == 0 && event->res != NULL && strcmp(target, event->res) == 0) { res = event; break; } } } /* Check if a resource event was found. */ if (res == NULL) { if (debug) { fprintf(stderr, "Target object not found for AVC " "event.\n"); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; avc = event_alloc(); if (avc == NULL) return 1; avc->type = ET_AVC; /* Guest info */ avc->uuid = copy_str(res->uuid); avc->name = copy_str(res->name); memcpy(avc->proof, res->proof, sizeof(avc->proof)); /* AVC info */ avc->start = time; avc->uid = uid; auparse_first_record(au); if (auparse_find_field(au, "apparmor")) { int i; avc->avc_result = copy_str(auparse_interpret_field(au)); for (i = 0; avc->avc_result && avc->avc_result[i]; i++) { avc->avc_result[i] = tolower(avc->avc_result[i]); } } if (auparse_find_field(au, "operation")) avc->avc_operation = copy_str(auparse_interpret_field(au)); avc->target = copy_str(target); if (auparse_find_field(au, "comm")) avc->comm = copy_str(auparse_interpret_field(au)); add_proof(avc, au); if (list_append(events, avc) == NULL) { event_free(avc); return 1; } return 0; }
int process_avc_selinux_context(auparse_state_t *au, const char *context) { const char *seclevel; struct event *machine_id, *avc; uid_t uid; time_t time; seclevel = get_seclevel(auparse_find_field(au, context)); if (seclevel == NULL) { if (debug) { fprintf(stderr, "Security context not found " "for AVC event.\n"); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; machine_id = get_machine_id_by_seclevel(seclevel); if (machine_id == NULL) { if (debug) { fprintf(stderr, "Couldn't get the security " "level from the AVC event.\n"); } return 0; } avc = event_alloc(); if (avc == NULL) return 1; avc->type = ET_AVC; /* Guest info */ avc->uuid = copy_str(machine_id->uuid); avc->name = copy_str(machine_id->name); memcpy(avc->proof, machine_id->proof, sizeof(avc->proof)); /* AVC info */ avc->start = time; avc->uid = uid; avc->seclevel = copy_str(seclevel); auparse_first_record(au); avc->avc_result = copy_str(auparse_find_field(au, "seresult")); avc->avc_operation = copy_str(auparse_find_field(au, "seperms")); if (auparse_find_field(au, "comm")) avc->comm = copy_str(auparse_interpret_field(au)); if (auparse_find_field(au, "name")) avc->target = copy_str(auparse_interpret_field(au)); /* get the context related to the permission that was denied. */ if (avc->avc_operation) { const char *ctx = NULL; if (strcmp("relabelfrom", avc->avc_operation) == 0) { ctx = auparse_find_field(au, "scontext"); } else if (strcmp("relabelto", avc->avc_operation) == 0) { ctx = auparse_find_field(au, "tcontext"); } avc->context = copy_str(ctx); } add_proof(avc, au); if (list_append(events, avc) == NULL) { event_free(avc); return 1; } return 0; }
int add_stop_guest_event(auparse_state_t *au) { list_node_t *it; struct event *stop, *start = NULL, *event = NULL; uid_t uid; time_t time; const char *uuid, *name; int success; /* Just skip this record if it failed to get some of the fields */ if (extract_virt_fields(au, &uuid, &uid, &time, &name, &success)) return 0; /* Loop backwards to find the last start event for the uuid and * update all resource records related to that guest session. */ for (it = events->tail; it; it = it->prev) { event = it->data; if (event->success && event->uuid && strcmp(uuid, event->uuid) == 0) { if (event->type == ET_START) { /* If an old session is found it's no longer * necessary to update the resource records. */ if (event->end || start) break; /* This is the start event related to the * current session. */ start = event; } else if (event->type == ET_STOP || event->type == ET_MACHINE_ID) { /* Old session found. */ break; } else if (event->type == ET_RES && event->end == 0) { /* Update the resource assignments. */ event->end = time; add_proof(event, au); } } } if (start == NULL) { if (debug) { fprintf(stderr, "Couldn't find the correlated start " "record to the stop event.\n"); } return 0; } /* Create a new stop event */ stop = event_alloc(); if (stop == NULL) return 1; stop->type = ET_STOP; stop->uuid = copy_str(uuid); stop->name = copy_str(name); stop->success = success; stop->uid = uid; stop->start = time; auparse_first_record(au); if (auparse_find_field(au, "vm-pid")) stop->pid = auparse_get_field_int(au); add_proof(stop, au); if (list_append(events, stop) == NULL) { event_free(stop); return 1; } /* Update the correlated start event. */ if (success) { start->end = time; add_proof(start, au); } return 0; }
static int analyzer_tftp_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer_tftp_priv *priv = obj; struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_prev = &stack[stack_index - 1]; uint16_t opcode = *PTYPE_UINT16_GETVAL(s_prev->pkt_info->fields_value[proto_tftp_field_opcode]); // Get the session struct conntrack_session *session = conntrack_session_get(s_prev->ce); if (!session) return POM_ERR; struct analyzer_tftp_session_priv *spriv = conntrack_session_get_priv(session, obj); if (!spriv) { // Add session priv if it is not done yet spriv = malloc(sizeof(struct analyzer_tftp_session_priv)); if (!spriv) { pom_oom(sizeof(struct analyzer_tftp_session_priv)); goto err; } memset(spriv, 0, sizeof(struct analyzer_tftp_session_priv)); if (conntrack_session_add_priv(session, obj, spriv, analyzer_tftp_session_priv_cleanup) != POM_OK) { free(spriv); goto err; } } void *pload = s->pload; uint32_t plen = s->plen; switch (opcode) { case tftp_rrq: case tftp_wrq: { if (plen < 3) return POM_OK; // Invalid packet // Find the filename // The below should always be valid as proto_tftp already checked this char *filename = pload; char *mode = memchr(filename, 0, plen - 1) + 1; struct analyzer_tftp_file *fq = malloc(sizeof(struct analyzer_tftp_file)); if (!fq) { pom_oom(sizeof(struct analyzer_tftp_file)); goto err; } memset(fq, 0, sizeof(struct analyzer_tftp_file)); // Get the port on which we expect this file // No need to check the IP as we got the session biding struct proto_process_stack *s_l4 = &stack[stack_index - 2]; unsigned int i; for (i = 0; !fq->port ; i++) { struct proto_reg_info *pinfo = proto_get_info(s_l4->proto); char *name = pinfo->pkt_fields[i].name; if (!name) { pomlog(POMLOG_ERR "Source port not found in RRQ/WRQ packets"); goto err; } if (!strcmp(name, "sport")) { fq->port = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]); break; } } fq->evt = event_alloc(priv->evt_file); if (!fq->evt) { free(fq); goto err; } struct data *evt_data = event_get_data(fq->evt); PTYPE_STRING_SETVAL(evt_data[analyzer_tftp_file_filename].value, filename); data_set(evt_data[analyzer_tftp_file_filename]); PTYPE_STRING_SETVAL(evt_data[analyzer_tftp_file_mode].value, mode); data_set(evt_data[analyzer_tftp_file_mode]); PTYPE_BOOL_SETVAL(evt_data[analyzer_tftp_file_write].value, opcode == tftp_wrq); data_set(evt_data[analyzer_tftp_file_write]); fq->next = spriv->files; if (fq->next) fq->next->prev = fq; spriv->files = fq; conntrack_session_unlock(session); event_process_begin(fq->evt, stack, stack_index, p->ts); break; } case tftp_data: { if (plen < sizeof(uint16_t)) return POM_OK; // Invalid packet struct analyzer_tftp_file *f = conntrack_get_priv(s_prev->ce, obj); struct data *evt_data = NULL; if (!f) { // The file is not yet associated to this connection // Find it in the queue struct proto_process_stack *s_l4 = &stack[stack_index - 2]; unsigned int i; uint16_t sport = 0, dport = 0; for (i = 0; !sport || !dport ; i++) { struct proto_reg_info *pinfo = proto_get_info(s_l4->proto); char *name = pinfo->pkt_fields[i].name; if (!name) { pomlog(POMLOG_ERR "Source port not found in data packets"); goto err; } if (!strcmp(name, "sport")) sport = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]); if (!strcmp(name, "dport")) dport = *PTYPE_UINT16_GETVAL(s_l4->pkt_info->fields_value[i]); } // Find the file in the session list for (f = spriv->files; ; f = f->next) { evt_data = event_get_data(f->evt); if (*PTYPE_BOOL_GETVAL(evt_data[analyzer_tftp_file_write].value)) { if (f->port == sport) break; } else { if (f->port == dport) break; } } if (!f) { pomlog(POMLOG_DEBUG "File not found in queued file request."); conntrack_session_unlock(session); return POM_OK; } // Remove the file from the queue and assign it to the conntrack if (f->prev) f->prev->next = f->next; else spriv->files = f->next; if (f->next) f->next->prev = f->prev; f->prev = NULL; f->next = NULL; // Create the payload buffer f->pload = pload_alloc(f->evt, PLOAD_FLAG_NEED_MAGIC); if (!f->pload) goto err; conntrack_add_priv(s_prev->ce, obj, f, analyzer_tftp_conntrack_priv_cleanup); } else { evt_data = event_get_data(f->evt); } conntrack_session_unlock(session); if (!f->pload) { pomlog(POMLOG_DEBUG "Ignoring extra packet"); return POM_OK; } // Discard the block ID pload += sizeof(uint16_t); plen -= sizeof(uint16_t); if (pload_append(f->pload, pload, plen) != POM_OK) goto err; uint32_t *size = PTYPE_UINT32_GETVAL(evt_data[analyzer_tftp_file_size].value); *size += plen; if (plen < ANALYZER_TFTP_BLK_SIZE) { // Got last packet ! data_set(evt_data[analyzer_tftp_file_size]); int res = pload_end(f->pload); res += event_process_end(f->evt); f->evt = NULL; f->pload = NULL; if (res) goto err; } break; } case tftp_error: { conntrack_session_unlock(session); struct analyzer_tftp_file *f = conntrack_get_priv(s_prev->ce, obj); if (f && f->pload) { int res = pload_end(f->pload); res += event_process_end(f->evt); f->pload = NULL; f->evt = NULL; if (res) goto err; } break; } default: conntrack_session_unlock(session); break; } return POM_OK; err: conntrack_session_unlock(session); return POM_ERR; }
static int analyzer_docsis_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_docsis_priv *priv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; uint8_t *type = PTYPE_UINT8_GETVAL(s->pkt_info->fields_value[proto_docsis_mgmt_field_type]); char *mac_dst = PTYPE_MAC_GETADDR(s->pkt_info->fields_value[proto_docsis_mgmt_field_dst]); // FIXME : improve this filtering at the source // Filter some useless messages we don't care about if (*type == MMT_UCD2 || *type == MMT_UCD3 || *type == MMT_MDD) return POM_OK; if (*type != MMT_RNG_RSP) { pomlog(POMLOG_DEBUG "Unhandled DOCSIS MGMT message type %u for destination mac %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX", *type, mac_dst[0], mac_dst[1], mac_dst[2], mac_dst[3], mac_dst[4], mac_dst[5]); return POM_OK; } // Use the last bits for the modem ID uint16_t id = ntohs(*(uint16_t*) (mac_dst + 4)) & ANALYZER_DOCSIS_CM_MASK; pom_mutex_lock(&priv->lock); struct analyzer_docsis_cm *cm; for (cm = priv->cms[id]; cm; cm = cm->next) { if (!memcmp(cm->mac, mac_dst, sizeof(cm->mac))) break; } if (!cm) { // Cable modem not found ! cm = malloc(sizeof(struct analyzer_docsis_cm)); if (!cm) { pom_mutex_unlock(&priv->lock); pom_oom(sizeof(struct analyzer_docsis_cm)); return POM_ERR; } memset(cm, 0, sizeof(struct analyzer_docsis_cm)); cm->t = timer_alloc(cm, analyzer_docsis_cm_timeout); if (!cm->t) { pom_mutex_unlock(&priv->lock); free(cm); return POM_ERR; } cm->analyzer = analyzer; memcpy(cm->mac, mac_dst, sizeof(cm->mac)); cm->t4_multiplier = 1; cm->next = priv->cms[id]; if (cm->next) cm->next->prev = cm; priv->cms[id] = cm; // Announce the new CM if (event_has_listener(priv->evt_cm_new)) { struct event *evt = event_alloc(priv->evt_cm_new); if (!evt) { pom_mutex_unlock(&priv->lock); return POM_ERR; } struct data *evt_data = event_get_data(evt); PTYPE_MAC_SETADDR(evt_data[analyzer_docsis_cm_new_mac].value, cm->mac); data_set(evt_data[analyzer_docsis_cm_new_mac]); PTYPE_STRING_SETVAL(evt_data[analyzer_docsis_cm_new_input].value, p->input->name); data_set(evt_data[analyzer_docsis_cm_new_input]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) { pom_mutex_unlock(&priv->lock); return POM_ERR; } } } switch (*type) { case MMT_RNG_RSP: analyzer_docsis_pkt_parse_rng_rsp(priv, cm, p, stack, stack_index); break; // FIXME If ranging_status is 0 and we receive another msg, probably it's actually registered // and we need to call analyzer_docsis_reg_status_update(); } timer_queue_now(cm->t, T4_TIMEOUT * cm->t4_multiplier, p->ts); pom_mutex_unlock(&priv->lock); return POM_OK; }
int process_avc_apparmor_target(auparse_state_t *au) { uid_t uid; time_t time; const char *profile; struct event *avc; /* Get profile associated with the AVC record */ if (auparse_find_field(au, "profile") == NULL) { if (debug) { auparse_first_record(au); fprintf(stderr, "AppArmor profile not found for AVC " "record: %s\n", auparse_get_record_text(au)); } return 0; } profile = auparse_interpret_field(au); /* Break path to get just the basename */ const char *basename = profile + strlen(profile); while (basename != profile && *basename != '/') basename--; if (*basename == '/') basename++; /* Check if it is an apparmor profile generated by libvirt and get the * guest UUID from it */ const char *prefix = "libvirt-"; if (strncmp(prefix, basename, strlen(prefix)) != 0) { if (debug) { fprintf(stderr, "Found a profile which is not " "generated by libvirt: %s\n", profile); } return 0; } /* Try to find a valid guest session */ const char *uuid = basename + strlen(prefix); struct list_node_t *it; struct event *machine_id = NULL; for (it = events->tail; it; it = it->prev) { struct event *event = it->data; if (event->success) { if (event->uuid != NULL && strcmp(event->uuid, uuid) == 0) { /* machine_id is used here instead of the start * event because it is generated before any * other event when a guest is started. So, * it's possible to correlate AVC events that * occurs during a guest start. */ if (event->type == ET_MACHINE_ID) { machine_id = event; break; } else if (event->type == ET_STOP) { break; } } else if (event->type == ET_DOWN) { break; } } } if (machine_id == NULL) { if (debug) { fprintf(stderr, "Found an AVC record for an unknown " "guest.\n"); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; avc = event_alloc(); if (avc == NULL) return 1; avc->type = ET_AVC; /* Guest info */ avc->uuid = copy_str(machine_id->uuid); avc->name = copy_str(machine_id->name); memcpy(avc->proof, machine_id->proof, sizeof(avc->proof)); /* AVC info */ avc->start = time; avc->uid = uid; auparse_first_record(au); if (auparse_find_field(au, "apparmor")) { int i; avc->avc_result = copy_str(auparse_interpret_field(au)); for (i = 0; avc->avc_result && avc->avc_result[i]; i++) { avc->avc_result[i] = tolower(avc->avc_result[i]); } } if (auparse_find_field(au, "operation")) avc->avc_operation = copy_str(auparse_interpret_field(au)); if (auparse_find_field(au, "name")) avc->target = copy_str(auparse_interpret_field(au)); if (auparse_find_field(au, "comm")) avc->comm = copy_str(auparse_interpret_field(au)); add_proof(avc, au); if (list_append(events, avc) == NULL) { event_free(avc); return 1; } return 0; }
int analyzer_eap_finalize(struct analyzer_eap_priv *apriv, struct analyzer_eap_ce_priv *cpriv) { if (!cpriv->evt_request || !cpriv->evt_response) return POM_OK; struct event *evt = NULL; struct data *evt_data = NULL; struct data *evt_req_data = event_get_data(cpriv->evt_request); struct data *evt_rsp_data = event_get_data(cpriv->evt_response); if (!data_is_set(evt_rsp_data[evt_eap_md5_challenge_value])) return POM_OK; if (!data_is_set(evt_req_data[evt_eap_md5_challenge_value])) return POM_OK; evt = event_alloc(apriv->evt_md5_auth); if (!evt) return POM_ERR; evt_data = event_get_data(evt); if (ptype_copy(evt_data[analyzer_eap_md5_challenge].value, evt_req_data[evt_eap_md5_challenge_value].value) != POM_OK) return POM_ERR; data_set(evt_data[analyzer_eap_md5_challenge]); if (ptype_copy(evt_data[analyzer_eap_md5_response].value, evt_rsp_data[evt_eap_md5_challenge_value].value) != POM_OK) return POM_ERR; data_set(evt_data[analyzer_eap_md5_response]); if (cpriv->client) { evt_data[analyzer_eap_common_client].value = cpriv->client; data_set(evt_data[analyzer_eap_common_client]); data_do_clean(evt_data[analyzer_eap_common_client]); cpriv->client = NULL; } if (cpriv->server) { evt_data[analyzer_eap_common_server].value = cpriv->server; data_set(evt_data[analyzer_eap_common_server]); data_do_clean(evt_data[analyzer_eap_common_server]); cpriv->server = NULL; } if (cpriv->vlan) { evt_data[analyzer_eap_common_vlan].value = cpriv->vlan; data_set(evt_data[analyzer_eap_common_vlan]); data_do_clean(evt_data[analyzer_eap_common_vlan]); cpriv->vlan = NULL; } if (cpriv->top_proto) { PTYPE_STRING_SETVAL(evt_data[analyzer_eap_common_top_proto].value, cpriv->top_proto); data_set(evt_data[analyzer_eap_common_top_proto]); } if (ptype_copy(evt_data[analyzer_eap_common_identifier].value, evt_req_data[evt_eap_common_identifier].value) != POM_OK) return POM_ERR; data_set(evt_data[analyzer_eap_common_identifier]); if (!data_is_set(evt_rsp_data[evt_eap_md5_challenge_name])) return POM_OK; if (ptype_copy(evt_data[analyzer_eap_common_username].value, evt_rsp_data[evt_eap_md5_challenge_name].value) != POM_OK) return POM_ERR; data_set(evt_data[analyzer_eap_common_username]); if (cpriv->evt_result) { struct data *evt_res_data = event_get_data(cpriv->evt_result); ptype_copy(evt_data[analyzer_eap_common_success].value, evt_res_data[evt_eap_success_failure_success].value); data_set(evt_data[analyzer_eap_common_success]); event_refcount_dec(cpriv->evt_result); cpriv->evt_result = NULL; } ptime ts = event_get_timestamp(cpriv->evt_response); event_refcount_dec(cpriv->evt_request); cpriv->evt_request = NULL; event_refcount_dec(cpriv->evt_response); cpriv->evt_response = NULL; return event_process(evt, NULL, 0, ts); }
int analyzer_ppp_pap_finalize(struct analyzer_ppp_pap_priv *apriv, struct analyzer_ppp_pap_ce_priv *cpriv) { if (!cpriv->evt_request) return POM_OK; struct event *evt = NULL; struct data *evt_data = NULL; struct data *evt_req_data = event_get_data(cpriv->evt_request); evt = event_alloc(apriv->evt_auth); if (!evt) return POM_ERR; evt_data = event_get_data(evt); if (ptype_copy(evt_data[analyzer_ppp_pap_auth_peer_id].value, evt_req_data[evt_ppp_pap_request_peer_id].value) != POM_OK) { event_cleanup(evt); return POM_ERR; } data_set(evt_data[analyzer_ppp_pap_auth_peer_id]); if (ptype_copy(evt_data[analyzer_ppp_pap_auth_password].value, evt_req_data[evt_ppp_pap_request_password].value) != POM_OK) { event_cleanup(evt); return POM_ERR; } data_set(evt_data[analyzer_ppp_pap_auth_password]); if (cpriv->client) { evt_data[analyzer_ppp_pap_auth_client].value = cpriv->client; data_set(evt_data[analyzer_ppp_pap_auth_client]); data_do_clean(evt_data[analyzer_ppp_pap_auth_client]); cpriv->client = NULL; } if (cpriv->server) { evt_data[analyzer_ppp_pap_auth_server].value = cpriv->server; data_set(evt_data[analyzer_ppp_pap_auth_server]); data_do_clean(evt_data[analyzer_ppp_pap_auth_server]); cpriv->server = NULL; } if (cpriv->vlan) { evt_data[analyzer_ppp_pap_auth_vlan].value = cpriv->vlan; data_set(evt_data[analyzer_ppp_pap_auth_vlan]); data_do_clean(evt_data[analyzer_ppp_pap_auth_vlan]); cpriv->vlan = NULL; } if (cpriv->top_proto) { PTYPE_STRING_SETVAL(evt_data[analyzer_ppp_pap_auth_top_proto].value, cpriv->top_proto); data_set(evt_data[analyzer_ppp_pap_auth_top_proto]); } if (ptype_copy(evt_data[analyzer_ppp_pap_auth_identifier].value, evt_req_data[evt_ppp_pap_request_identifier].value) != POM_OK) { event_cleanup(evt); return POM_ERR; } data_set(evt_data[analyzer_ppp_pap_auth_identifier]); if (cpriv->evt_ack_nack) { struct data *evt_ack_data = event_get_data(cpriv->evt_ack_nack); uint8_t code = *PTYPE_UINT8_GETVAL(evt_ack_data[evt_ppp_pap_ack_nack_code].value); if (code == 2) { PTYPE_BOOL_SETVAL(evt_data[analyzer_ppp_pap_auth_success].value, 1); } else { PTYPE_BOOL_SETVAL(evt_data[analyzer_ppp_pap_auth_success].value, 0); } data_set(evt_data[analyzer_ppp_pap_auth_success]); event_refcount_dec(cpriv->evt_ack_nack); cpriv->evt_ack_nack = NULL; } ptime ts = event_get_timestamp(cpriv->evt_request); event_refcount_dec(cpriv->evt_request); cpriv->evt_request = NULL; return event_process(evt, NULL, 0, ts); }
static int analyzer_rtp_pload_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_rtp_priv *priv = analyzer->priv; struct proto_process_stack *pload_stack = &stack[stack_index]; struct proto_process_stack *s = &stack[stack_index - 1]; if (!s->ce) return POM_ERR; struct analyzer_rtp_ce_priv *cp = conntrack_get_priv(s->ce, obj); if (!cp) { cp = malloc(sizeof(struct analyzer_rtp_ce_priv)); if (!cp) { pom_oom(sizeof(struct analyzer_rtp_ce_priv)); return POM_ERR; } memset(cp, 0, sizeof(struct analyzer_rtp_ce_priv)); if (conntrack_add_priv(s->ce, obj, cp, analyzer_rtp_ce_cleanup) != POM_OK) return POM_ERR; } int dir = s->direction; if (!cp->evt[dir]) { cp->evt[dir] = event_alloc(priv->evt_rtp_stream); if (!cp->evt[dir]) return POM_ERR; struct data *evt_data = event_get_data(cp->evt[dir]); ptype_copy(evt_data[analyzer_rtp_stream_ssrc].value, s->pkt_info->fields_value[proto_rtp_field_ssrc]); data_set(evt_data[analyzer_rtp_stream_ssrc]); // For now we always assume RTP is over UDP or TCP if (stack_index > 2) { struct proto_process_stack *l4_stack = &stack[stack_index - 2]; unsigned int i; for (i = 0; !data_is_set(evt_data[analyzer_rtp_stream_src_port]) || !data_is_set(evt_data[analyzer_rtp_stream_dst_port]); i++) { struct proto_reg_info *l4_info = proto_get_info(l4_stack->proto); char *name = l4_info->pkt_fields[i].name; if (!name) break; if (!data_is_set(evt_data[analyzer_rtp_stream_src_port]) && !strcmp(name, "sport")) { ptype_copy(evt_data[analyzer_rtp_stream_src_port].value, l4_stack->pkt_info->fields_value[i]); data_set(evt_data[analyzer_rtp_stream_src_port]); } else if (!data_is_set(evt_data[analyzer_rtp_stream_dst_port]) && !strcmp(name, "dport")) { ptype_copy(evt_data[analyzer_rtp_stream_dst_port].value, l4_stack->pkt_info->fields_value[i]); data_set(evt_data[analyzer_rtp_stream_dst_port]); } } } if (stack_index > 3) { struct proto_process_stack *l3_stack = &stack[stack_index - 3]; unsigned int i; for (i = 0; !data_is_set(evt_data[analyzer_rtp_stream_src_addr]) || !data_is_set(evt_data[analyzer_rtp_stream_dst_addr]); i++) { struct proto_reg_info *l3_info = proto_get_info(l3_stack->proto); char *name = l3_info->pkt_fields[i].name; if (!name) break; if (!data_is_set(evt_data[analyzer_rtp_stream_src_addr]) && !strcmp(name, "src")) { evt_data[analyzer_rtp_stream_src_addr].value = ptype_alloc_from(l3_stack->pkt_info->fields_value[i]); if (evt_data[analyzer_rtp_stream_src_addr].value) data_set(evt_data[analyzer_rtp_stream_src_addr]); } else if (!data_is_set(evt_data[analyzer_rtp_stream_dst_addr]) && !strcmp(name, "dst")) { evt_data[analyzer_rtp_stream_dst_addr].value = ptype_alloc_from(l3_stack->pkt_info->fields_value[i]); if (evt_data[analyzer_rtp_stream_dst_addr].value) data_set(evt_data[analyzer_rtp_stream_dst_addr]); } } } struct proto *sess_proto = telephony_stream_info_get_sess_proto(s->ce); if (sess_proto) { struct proto_reg_info *proto_reg = proto_get_info(sess_proto); PTYPE_STRING_SETVAL(evt_data[analyzer_rtp_stream_sess_proto].value, proto_reg->name); data_set(evt_data[analyzer_rtp_stream_sess_proto]); } char *call_id = telephony_stream_info_get_call_id(s->ce); if (call_id) { PTYPE_STRING_SETVAL_P(evt_data[analyzer_rtp_stream_call_id].value, call_id); data_set(evt_data[analyzer_rtp_stream_call_id]); } if (event_process_begin(cp->evt[dir], stack, stack_index, p->ts) != POM_OK) return POM_ERR; } if (!cp->pload[dir]) { cp->pload[dir] = pload_alloc(cp->evt[dir], 0); if (!cp->pload[dir]) return POM_ERR; struct telephony_codec_info info = { 0 }; if (telephony_stream_info_get_codec(&info, stack, stack_index - 1) == POM_OK) { char *pload_type = telephony_codec_info_get_pload_type(&info); if (pload_type) pload_set_type(cp->pload[dir], pload_type); } } if (pload_append(cp->pload[dir], pload_stack->pload, pload_stack->plen) != POM_OK) return POM_ERR; return POM_OK; }
static int proto_eap_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; if (sizeof(struct eap_header) > s->plen) return PROTO_INVALID; struct proto_eap_priv *priv = proto_priv; struct eap_header *hdr = s->pload; PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_eap_field_code], hdr->code); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_eap_field_identifier], hdr->identifier); if (hdr->code < 1 || hdr->code > 4) return PROTO_INVALID; uint16_t len = ntohs(hdr->length); if (len > s->plen) return PROTO_INVALID; // Keep only the payload lenght len -= sizeof(struct eap_header); if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; if (conntrack_delayed_cleanup(s->ce, *PTYPE_UINT32_GETVAL(priv->p_timeout), p->ts) != POM_OK) { conntrack_unlock(s->ce); return PROTO_ERR; } conntrack_unlock(s->ce); if (hdr->code == 3 || hdr->code == 4) { // Content length is 0 for success and failure if (len != 4) return PROTO_INVALID; len = 0; if (!event_has_listener(priv->evt_success_failure)) return PROTO_OK; struct event *evt = event_alloc(priv->evt_success_failure); if (!evt) return PROTO_ERR; struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_identifier].value, hdr->identifier); data_set(evt_data[evt_eap_common_identifier]); PTYPE_BOOL_SETVAL(evt_data[evt_eap_success_failure_success].value, (hdr->code == 3 ? 1 : 0)); data_set(evt_data[evt_eap_success_failure_success]); return event_process(evt, stack, stack_index, p->ts); } // At this point, code is either 1 or 2 (request/response) void *pload = s->pload + sizeof(struct eap_header); uint8_t type = 0; // There is at least 1 byte of data for request/response if (len < 1) return PROTO_INVALID; len--; type = *(uint8_t*)pload; pload++; struct event *evt = NULL; struct data *evt_data = NULL; switch (type) { case 1: // Identity if (!event_has_listener(priv->evt_identity)) break; evt = event_alloc(priv->evt_identity); if (!evt) return PROTO_ERR; if (len) { evt_data = event_get_data(evt); PTYPE_STRING_SETVAL_N(evt_data[evt_eap_identity_identity].value, pload, len); data_set(evt_data[evt_eap_identity_identity]); } break; case 4: // MD5-Challenge if (!event_has_listener(priv->evt_md5_challenge)) break; if (len < 17) return PROTO_INVALID; uint8_t value_size = *(uint8_t*)pload; if (value_size != 16) return PROTO_INVALID; pload++; len--; evt = event_alloc(priv->evt_md5_challenge); if (!evt) return PROTO_ERR; evt_data = event_get_data(evt); PTYPE_BYTES_SETLEN(evt_data[evt_eap_md5_challenge_value].value, 16); PTYPE_BYTES_SETVAL(evt_data[evt_eap_md5_challenge_value].value, pload); data_set(evt_data[evt_eap_md5_challenge_value]); if (len > 16) { PTYPE_STRING_SETVAL_N(evt_data[evt_eap_md5_challenge_name].value, pload + 16, len - 16); data_set(evt_data[evt_eap_md5_challenge_name]); } break; } if (evt) { if (!evt_data) evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_identifier].value, hdr->identifier); data_set(evt_data[evt_eap_common_identifier]); PTYPE_UINT8_SETVAL(evt_data[evt_eap_common_code].value, hdr->code); data_set(evt_data[evt_eap_common_code]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) return PROTO_ERR; } return PROTO_OK; }
static int proto_ppp_pap_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; if (sizeof(struct ppp_pap_header) > s->plen) return PROTO_INVALID; struct ppp_pap_header *pchdr = s->pload; size_t len = ntohs(pchdr->length); if (len > s->plen) return PROTO_INVALID; // Keep only the payload len len -= sizeof(struct ppp_pap_header); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ppp_pap_field_code], pchdr->code); PTYPE_UINT8_SETVAL(s->pkt_info->fields_value[proto_ppp_pap_field_identifier], pchdr->identifier); struct proto_ppp_pap_priv *priv = proto_priv; if (conntrack_get(stack, stack_index) != POM_OK) return PROTO_ERR; if (conntrack_delayed_cleanup(s->ce, *PTYPE_UINT32_GETVAL(priv->p_auth_timeout), p->ts) != POM_OK) { conntrack_unlock(s->ce); return PROTO_ERR; } conntrack_unlock(s->ce); if (pchdr->code == 1 && event_has_listener(priv->evt_request)) { if (len < 4) return PROTO_INVALID; uint8_t *peer_id_len = s->pload + sizeof(struct ppp_pap_header); if (*peer_id_len > len - 2) return PROTO_INVALID; len -= (*peer_id_len + 1); uint8_t *pwd_len = peer_id_len + *peer_id_len + 1; if (*pwd_len > len - 1) return PROTO_INVALID; // Process the challenge/response event struct event *evt = event_alloc(priv->evt_request); if (!evt) return PROTO_ERR; struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_request_code].value, pchdr->code); data_set(evt_data[evt_ppp_pap_request_code]); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_request_identifier].value, pchdr->identifier); data_set(evt_data[evt_ppp_pap_request_identifier]); PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_request_peer_id].value, (char *)peer_id_len + 1, *peer_id_len); data_set(evt_data[evt_ppp_pap_request_peer_id]); PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_request_password].value, (char *)pwd_len + 1, *pwd_len); data_set(evt_data[evt_ppp_pap_request_password]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) return PROTO_ERR; } if ((pchdr->code == 2 || pchdr->code == 3) && event_has_listener(priv->evt_ack_nack)) { struct event *evt = event_alloc(priv->evt_ack_nack); if (!evt) return PROTO_ERR; struct data *evt_data = event_get_data(evt); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_ack_nack_code].value, pchdr->code); data_set(evt_data[evt_ppp_pap_ack_nack_code]); PTYPE_UINT8_SETVAL(evt_data[evt_ppp_pap_ack_nack_identifier].value, pchdr->identifier); data_set(evt_data[evt_ppp_pap_ack_nack_identifier]); uint8_t *msg_len = s->pload + sizeof(struct ppp_pap_header); if (*msg_len > len - 1) return PROTO_INVALID; PTYPE_STRING_SETVAL_N(evt_data[evt_ppp_pap_ack_nack_message].value, (char *)msg_len + 1, *msg_len); data_set(evt_data[evt_ppp_pap_ack_nack_message]); if (event_process(evt, stack, stack_index, p->ts) != POM_OK) return PROTO_ERR; } return PROTO_OK; }
static int analyzer_arp_pkt_process(void *obj, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct analyzer *analyzer = obj; struct analyzer_arp_priv *priv = analyzer->priv; struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_prev = &stack[stack_index - 1]; struct in_addr arp_ip = PTYPE_IPV4_GETADDR(s->pkt_info->fields_value[proto_arp_field_sender_proto_addr]); // Discard bogon 0.0.0.0 if (!arp_ip.s_addr) return POM_OK; // Find that IP in the table uint32_t id = arp_ip.s_addr & ANALYZER_ARP_HOST_MASK; char *arp_mac = PTYPE_MAC_GETADDR(s->pkt_info->fields_value[proto_arp_field_sender_hw_addr]); uint16_t vlan = 0; if (s_prev->proto == priv->proto_vlan) vlan = *PTYPE_UINT16_GETVAL(s_prev->pkt_info->fields_value[proto_vlan_field_vid]); pom_mutex_lock(&priv->lock); struct analyzer_arp_host *host; for (host = priv->hosts[id]; host; host = host->next) { if (host->ip.s_addr == arp_ip.s_addr && host->vlan == vlan) break; } if (!host) { // Host not found ! host = malloc(sizeof(struct analyzer_arp_host)); if (!host) { pom_mutex_unlock(&priv->lock); pom_oom(sizeof(struct analyzer_arp_host)); return POM_ERR; } memset(host, 0, sizeof(struct analyzer_arp_host)); host->ip.s_addr = arp_ip.s_addr; memcpy(host->mac, arp_mac, sizeof(host->mac)); host->vlan = vlan; host->next = priv->hosts[id]; if (host->next) host->next->prev = host; priv->hosts[id] = host; pom_mutex_unlock(&priv->lock); // Announce the new station if (event_has_listener(priv->evt_new_sta)) { struct event *evt = event_alloc(priv->evt_new_sta); if (!evt) return POM_ERR; struct data *evt_data = evt->data; ptype_copy(evt_data[analyzer_arp_new_sta_mac_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_hw_addr]); data_set(evt_data[analyzer_arp_new_sta_mac_addr]); ptype_copy(evt_data[analyzer_arp_new_sta_ip_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_proto_addr]); data_set(evt_data[analyzer_arp_new_sta_ip_addr]); PTYPE_UINT16_SETVAL(evt_data[analyzer_arp_new_sta_vlan].value, vlan); data_set(evt_data[analyzer_arp_new_sta_vlan]); PTYPE_STRING_SETVAL(evt_data[analyzer_arp_new_sta_input].value, p->input->name); data_set(evt_data[analyzer_arp_new_sta_input]); if (event_process(evt, stack, stack_index) != POM_OK) return POM_ERR; } // Nothing else to do return POM_OK; } // Host was found, check mac if (memcmp(host->mac, arp_mac, sizeof(host->mac))) { if (event_has_listener(priv->evt_sta_changed)) { struct event *evt = event_alloc(priv->evt_sta_changed); if (!evt) { pom_mutex_unlock(&priv->lock); return POM_ERR; } struct data *evt_data = evt->data; PTYPE_MAC_SETADDR(evt_data[analyzer_arp_sta_changed_old_mac_addr].value, host->mac); data_set(evt_data[analyzer_arp_sta_changed_old_mac_addr]); ptype_copy(evt_data[analyzer_arp_sta_changed_new_mac_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_hw_addr]); data_set(evt_data[analyzer_arp_sta_changed_new_mac_addr]); ptype_copy(evt_data[analyzer_arp_sta_changed_ip_addr].value, s->pkt_info->fields_value[proto_arp_field_sender_proto_addr]); data_set(evt_data[analyzer_arp_sta_changed_ip_addr]); PTYPE_UINT16_SETVAL(evt_data[analyzer_arp_sta_changed_vlan].value, vlan); data_set(evt_data[analyzer_arp_sta_changed_vlan]); PTYPE_STRING_SETVAL(evt_data[analyzer_arp_sta_changed_input].value, p->input->name); data_set(evt_data[analyzer_arp_sta_changed_input]); if (event_process(evt, stack, stack_index) != POM_OK) { pom_mutex_unlock(&priv->lock); return POM_ERR; } } memcpy(host->mac, arp_mac, sizeof(host->mac)); } pom_mutex_unlock(&priv->lock); return POM_OK; }
/* This function tries to correlate an anomaly record to a guest using the qemu * pid or the selinux context. */ int process_anom(auparse_state_t *au) { uid_t uid; time_t time; pid_t pid = -1; list_node_t *it; struct event *anom, *start = NULL; /* An anomaly record is correlated to a guest by the process id */ if (auparse_find_field(au, "pid")) { pid = auparse_get_field_int(au); } else { if (debug) { fprintf(stderr, "Found an anomaly record " "without pid.\n"); } } /* Loop backwards to find a running guest with the same pid. */ if (pid >= 0) { for (it = events->tail; it; it = it->next) { struct event *event = it->data; if (event->pid == pid && event->success) { if (event->type == ET_STOP) { break; } else if (event->type == ET_START) { if (event->end == 0) start = event; break; } } } } /* Try to match using selinux context */ if (start == NULL) { const char *seclevel; struct event *machine_id; seclevel = get_seclevel(auparse_find_field(au, "subj")); if (seclevel == NULL) { if (debug) { auparse_first_record(au); const char *text = auparse_get_record_text(au); fprintf(stderr, "Security context not found " "for anomaly event: %s\n", text ? text : ""); } return 0; } machine_id = get_machine_id_by_seclevel(seclevel); if (machine_id == NULL) { if (debug) { fprintf(stderr, "Couldn't get the security " "level from the anomaly event.\n"); } return 0; } for (it = events->tail; it; it = it->next) { struct event *event = it->data; if (event->success && machine_id->uuid && event->uuid && strcmp(machine_id->uuid, event->uuid) == 0) { if (event->type == ET_STOP) { break; } else if (event->type == ET_START) { if (event->end == 0) start = event; break; } } } } if (start == NULL) { if (debug) { const char *text = auparse_get_record_text(au); fprintf(stderr, "Guest not found for " "anomaly record: %s.\n", text ? text : ""); } return 0; } if (extract_virt_fields(au, NULL, &uid, &time, NULL, NULL)) return 0; anom = event_alloc(); if (anom == NULL) return 1; anom->type = ET_ANOM; anom->uuid = copy_str(start->uuid); anom->name = copy_str(start->name); anom->uid = uid; anom->start = time; anom->pid = pid; memcpy(anom->proof, start->proof, sizeof(anom->proof)); add_proof(anom, au); if (list_append(events, anom) == NULL) { event_free(anom); return 1; } return 0; }
static int proto_smtp_process(void *proto_priv, struct packet *p, struct proto_process_stack *stack, unsigned int stack_index) { struct proto_process_stack *s = &stack[stack_index]; struct proto_process_stack *s_next = &stack[stack_index + 1]; if (conntrack_get_unique_from_parent(stack, stack_index) != POM_OK) { pomlog(POMLOG_ERR "Could not get conntrack entry"); return PROTO_ERR; } // There should no need to keep the lock here since we are in the packet_stream lock from proto_tcp conntrack_unlock(s->ce); struct proto_smtp_priv *ppriv = proto_priv; struct proto_smtp_conntrack_priv *priv = s->ce->priv; if (!priv) { priv = malloc(sizeof(struct proto_smtp_conntrack_priv)); if (!priv) { pom_oom(sizeof(struct proto_smtp_conntrack_priv)); return PROTO_ERR; } memset(priv, 0, sizeof(struct proto_smtp_conntrack_priv)); priv->parser[POM_DIR_FWD] = packet_stream_parser_alloc(SMTP_MAX_LINE, PACKET_STREAM_PARSER_FLAG_TRIM); if (!priv->parser[POM_DIR_FWD]) { free(priv); return PROTO_ERR; } priv->parser[POM_DIR_REV] = packet_stream_parser_alloc(SMTP_MAX_LINE, PACKET_STREAM_PARSER_FLAG_TRIM); if (!priv->parser[POM_DIR_REV]) { packet_stream_parser_cleanup(priv->parser[POM_DIR_FWD]); free(priv); return PROTO_ERR; } priv->server_direction = POM_DIR_UNK; s->ce->priv = priv; } if (priv->flags & PROTO_SMTP_FLAG_INVALID) return PROTO_OK; struct packet_stream_parser *parser = priv->parser[s->direction]; if (packet_stream_parser_add_payload(parser, s->pload, s->plen) != POM_OK) return PROTO_ERR; char *line = NULL; size_t len = 0; while (1) { // Some check to do prior to parse the payload if (s->direction == POM_DIR_REVERSE(priv->server_direction)) { if (priv->flags & PROTO_SMTP_FLAG_STARTTLS) { // Last command was a STARTTLS command, this is the TLS negociation // Since we can't parse this, mark it as invalid priv->flags |= PROTO_SMTP_FLAG_INVALID; return PROTO_OK; } else if (priv->flags & PROTO_SMTP_FLAG_CLIENT_DATA) { // We are receiving payload data, check where the end is void *pload; size_t plen; packet_stream_parser_get_remaining(parser, &pload, &plen); if (!plen) return PROTO_OK; // Look for the "<CR><LF>.<CR><LF>" sequence if (priv->data_end_pos > 0) { // The previous packet ended with something that might be the final sequence // Check if we have the rest int i, found = 1; for (i = 0; i < PROTO_SMTP_DATA_END_LEN - priv->data_end_pos && i <= plen; i++) { if (*(char*)(pload + i) != PROTO_SMTP_DATA_END[priv->data_end_pos + i]) { found = 0; break; } } if (found) { // If we have already processed the dot after <CR><LF> there is no way to remove it // Thus we mark this connection as invalid. Most MTA will send at worst the last // 3 bytes of the end sequence in a sequence packet if (i != plen || (priv->data_end_pos >= 2 && plen < 3)) { pomlog(POMLOG_DEBUG "The final line was not at the of a packet as expected !"); priv->flags |= PROTO_SMTP_FLAG_INVALID; event_process_end(priv->data_evt); priv->data_evt = NULL; return PROTO_OK; } s_next->pload = pload; s_next->plen = plen - PROTO_SMTP_DATA_END_LEN + 2; // The last line return is part of the payload priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA_END; priv->flags &= ~PROTO_SMTP_FLAG_CLIENT_DATA; priv->data_end_pos = 0; return PROTO_OK; } priv->data_end_pos = 0; } char *dotline = pom_strnstr(pload, PROTO_SMTP_DATA_END, plen); if (dotline) { if (pload + plen - PROTO_SMTP_DATA_END_LEN != dotline) { pomlog(POMLOG_DEBUG "The final line was not at the of a packet as expected !"); priv->flags |= PROTO_SMTP_FLAG_INVALID; event_process_end(priv->data_evt); priv->data_evt = NULL; return PROTO_OK; } s_next->pload = pload; s_next->plen = plen - PROTO_SMTP_DATA_END_LEN + 2; // The last line return is part of the payload priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA_END; priv->flags &= ~PROTO_SMTP_FLAG_CLIENT_DATA; } else { // Check if the end of the payload contains part of the "<CR><LF>.<CR><LF>" sequence int i, found = 0; for (i = 1 ; (i < PROTO_SMTP_DATA_END_LEN) && (i <= plen); i++) { if (!memcmp(pload + plen - i, PROTO_SMTP_DATA_END, i)) { found = 1; break; } } if (found) priv->data_end_pos = i; s_next->pload = pload; s_next->plen = plen; } return PROTO_OK; } } // Process commands if (packet_stream_parser_get_line(parser, &line, &len) != POM_OK) return PROTO_ERR; if (!line) return PROTO_OK; if (!len) // Probably a missed packet return PROTO_OK; // Try to find the server direction if (priv->server_direction == POM_DIR_UNK) { unsigned int code = atoi(line); if (code > 0) { priv->server_direction = s->direction; } else { priv->server_direction = POM_DIR_REVERSE(s->direction); } } if (s->direction == priv->server_direction) { // Parse the response code and generate the event if ((len < 5) || // Server response is 3 digit error code, a space or hyphen and then at least one letter of text (line[3] != ' ' && line[3] != '-')) { pomlog(POMLOG_DEBUG "Too short or invalid response from server"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } int code = atoi(line); if (code == 0) { pomlog(POMLOG_DEBUG "Invalid response from server"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } if (event_has_listener(ppriv->evt_reply)) { struct data *evt_data = NULL; if (priv->reply_evt) { evt_data = event_get_data(priv->reply_evt); uint16_t cur_code = *PTYPE_UINT16_GETVAL(evt_data[proto_smtp_reply_code].value); if (cur_code != code) { pomlog(POMLOG_WARN "Multiline code not the same as previous line : %hu -> %hu", cur_code, code); event_process_end(priv->reply_evt); priv->reply_evt = NULL; } } if (!priv->reply_evt) { priv->reply_evt = event_alloc(ppriv->evt_reply); if (!priv->reply_evt) return PROTO_ERR; evt_data = event_get_data(priv->reply_evt); PTYPE_UINT16_SETVAL(evt_data[proto_smtp_reply_code].value, code); data_set(evt_data[proto_smtp_reply_code]); } if (len > 4) { struct ptype *txt = ptype_alloc("string"); if (!txt) return PROTO_ERR; PTYPE_STRING_SETVAL_N(txt, line + 4, len - 4); if (data_item_add_ptype(evt_data, proto_smtp_reply_text, strdup("text"), txt) != POM_OK) return PROTO_ERR; } if (!event_is_started(priv->reply_evt)) event_process_begin(priv->reply_evt, stack, stack_index, p->ts); } if (line[3] != '-') { // Last line in the response if (priv->reply_evt) { event_process_end(priv->reply_evt); priv->reply_evt = NULL; } } if (priv->flags & PROTO_SMTP_FLAG_STARTTLS) { // The last command was STARTTLS priv->flags &= ~PROTO_SMTP_FLAG_STARTTLS; if (code == 220) { // TLS has the go, we can't parse from now so mark as invalid priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } } } else { // Client command if (len < 4) { // Client commands are at least 4 bytes long pomlog(POMLOG_DEBUG "Too short or invalid query from client"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } // Make sure it's a command by checking it's at least a four letter word int i; for (i = 0; i < 4; i++) { // In some case it can also be a base64 encoded word if (! ((line[i] >= 'A' && line[i] <= 'Z') || (line[i] >= 'a' && line[i] <= 'z') || (line[i] >= '0' && line [i] <= '9') || line[i] == '=')) break; } if ((i < 4)) { pomlog(POMLOG_DEBUG "Recieved invalid client command"); priv->flags |= PROTO_SMTP_FLAG_INVALID; return POM_OK; } if (!strncasecmp(line, "DATA", strlen("DATA")) && len == strlen("DATA")) { priv->flags |= PROTO_SMTP_FLAG_CLIENT_DATA; } else if (!strncasecmp(line, "STARTTLS", strlen("STARTTLS")) && len == strlen("STARTTLS")) { priv->flags |= PROTO_SMTP_FLAG_STARTTLS; } if (event_has_listener(ppriv->evt_cmd)) { struct event *evt = event_alloc(ppriv->evt_cmd); if (!evt) return PROTO_ERR; size_t cmdlen = len; char *space = memchr(line, ' ', len); if (space) cmdlen = space - line; struct data *evt_data = event_get_data(evt); PTYPE_STRING_SETVAL_N(evt_data[proto_smtp_cmd_name].value, line, cmdlen); data_set(evt_data[proto_smtp_cmd_name]); if (space) { PTYPE_STRING_SETVAL_N(evt_data[proto_smtp_cmd_arg].value, space + 1, len - 1 - cmdlen); data_set(evt_data[proto_smtp_cmd_arg]); } if (priv->flags & PROTO_SMTP_FLAG_CLIENT_DATA) { // The event ends at the end of the message priv->data_evt = evt; return event_process_begin(evt, stack, stack_index, p->ts); } else { return event_process(evt, stack, stack_index, p->ts); } } } } return PROTO_OK; }