static void LogHttpLogExtended(LogHttpLogThread *aft, htp_tx_t *tx) { MemBufferWriteString(aft->buffer, " [**] "); /* referer */ htp_header_t *h_referer = NULL; if (tx->request_headers != NULL) { h_referer = table_getc(tx->request_headers, "referer"); } if (h_referer != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(h_referer->value), bstr_len(h_referer->value)); } else { MemBufferWriteString(aft->buffer, "<no referer>"); } MemBufferWriteString(aft->buffer, " [**] "); /* method */ if (tx->request_method != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_method), bstr_len(tx->request_method)); } MemBufferWriteString(aft->buffer, " [**] "); /* protocol */ if (tx->request_protocol != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_protocol), bstr_len(tx->request_protocol)); } else { MemBufferWriteString(aft->buffer, "<no protocol>"); } MemBufferWriteString(aft->buffer, " [**] "); /* response status */ if (tx->response_status != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->response_status), bstr_len(tx->response_status)); /* Redirect? */ if ((tx->response_status_number > 300) && ((tx->response_status_number) < 303)) { htp_header_t *h_location = table_getc(tx->response_headers, "location"); if (h_location != NULL) { MemBufferWriteString(aft->buffer, " => "); PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(h_location->value), bstr_len(h_location->value)); } } } else { MemBufferWriteString(aft->buffer, "<no status>"); } /* length */ MemBufferWriteString(aft->buffer, " [**] %"PRIuMAX" bytes", (uintmax_t)tx->response_message_len); }
int DetectEngineRunHttpUAMpm(DetectEngineThreadCtx *det_ctx, Flow *f, HtpState *htp_state, uint8_t flags) { htp_tx_t *tx = NULL; uint32_t cnt = 0; int idx; /* we need to lock because the buffers are not actually true buffers * but are ones that point to a buffer given by libhtp */ FLOWLOCK_RDLOCK(f); if (htp_state == NULL) { SCLogDebug("no HTTP state"); goto end; } if (htp_state->connp == NULL || htp_state->connp->conn == NULL) { SCLogDebug("HTP state has no conn(p)"); goto end; } idx = AppLayerTransactionGetInspectId(f); if (idx == -1) { goto end; } int size = (int)list_size(htp_state->connp->conn->transactions); for (; idx < size; idx++) { tx = list_get(htp_state->connp->conn->transactions, idx); if (tx == NULL) continue; htp_header_t *h = (htp_header_t *)table_getc(tx->request_headers, "User-Agent"); if (h == NULL) { SCLogDebug("HTTP user agent header not present in this request"); continue; } cnt += HttpUAPatternSearch(det_ctx, (uint8_t *)bstr_ptr(h->value), bstr_len(h->value), flags); } end: FLOWLOCK_UNLOCK(f); return cnt; }
static TmEcode LogHttpLogIPWrapper(ThreadVars *tv, Packet *p, void *data, PacketQueue *pq, PacketQueue *postpq, int ipproto) { SCEnter(); LogHttpLogThread *aft = (LogHttpLogThread *)data; LogHttpFileCtx *hlog = aft->httplog_ctx; char timebuf[64]; size_t idx = 0; /* no flow, no htp state */ if (p->flow == NULL) { SCReturnInt(TM_ECODE_OK); } /* check if we have HTTP state or not */ FLOWLOCK_WRLOCK(p->flow); /* WRITE lock before we updated flow logged id */ uint16_t proto = AppLayerGetProtoFromPacket(p); if (proto != ALPROTO_HTTP) goto end; int r = AppLayerTransactionGetLoggedId(p->flow); if (r < 0) { goto end; } size_t logged = (size_t)r; r = HtpTransactionGetLoggableId(p->flow); if (r < 0) { goto end; } size_t loggable = (size_t)r; /* nothing to do */ if (logged >= loggable) { goto end; } HtpState *htp_state = (HtpState *)AppLayerGetProtoStateFromPacket(p); if (htp_state == NULL) { SCLogDebug("no http state, so no request logging"); goto end; } if (htp_state->connp == NULL || htp_state->connp->conn == NULL) goto end; htp_tx_t *tx = NULL; CreateTimeString(&p->ts, timebuf, sizeof(timebuf)); char srcip[46], dstip[46]; Port sp, dp; if ((PKT_IS_TOSERVER(p))) { switch (ipproto) { case AF_INET: PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), srcip, sizeof(srcip)); PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), dstip, sizeof(dstip)); break; case AF_INET6: PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), srcip, sizeof(srcip)); PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), dstip, sizeof(dstip)); break; default: goto end; } sp = p->sp; dp = p->dp; } else { switch (ipproto) { case AF_INET: PrintInet(AF_INET, (const void *)GET_IPV4_DST_ADDR_PTR(p), srcip, sizeof(srcip)); PrintInet(AF_INET, (const void *)GET_IPV4_SRC_ADDR_PTR(p), dstip, sizeof(dstip)); break; case AF_INET6: PrintInet(AF_INET6, (const void *)GET_IPV6_DST_ADDR(p), srcip, sizeof(srcip)); PrintInet(AF_INET6, (const void *)GET_IPV6_SRC_ADDR(p), dstip, sizeof(dstip)); break; default: goto end; } sp = p->dp; dp = p->sp; } for (idx = logged; idx < loggable; idx++) { tx = list_get(htp_state->connp->conn->transactions, idx); if (tx == NULL) { SCLogDebug("tx is NULL not logging !!"); continue; } SCLogDebug("got a HTTP request and now logging !!"); /* reset */ MemBufferReset(aft->buffer); if (hlog->flags & LOG_HTTP_CUSTOM) { LogHttpLogCustom(aft, tx, &p->ts, srcip, sp, dstip, dp); } else { /* time */ MemBufferWriteString(aft->buffer, "%s ", timebuf); /* hostname */ if (tx->parsed_uri != NULL && tx->parsed_uri->hostname != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->parsed_uri->hostname), bstr_len(tx->parsed_uri->hostname)); } else { MemBufferWriteString(aft->buffer, "<hostname unknown>"); } MemBufferWriteString(aft->buffer, " [**] "); /* uri */ if (tx->request_uri != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_uri), bstr_len(tx->request_uri)); } MemBufferWriteString(aft->buffer, " [**] "); /* user agent */ htp_header_t *h_user_agent = NULL; if (tx->request_headers != NULL) { h_user_agent = table_getc(tx->request_headers, "user-agent"); } if (h_user_agent != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(h_user_agent->value), bstr_len(h_user_agent->value)); } else { MemBufferWriteString(aft->buffer, "<useragent unknown>"); } if (hlog->flags & LOG_HTTP_EXTENDED) { LogHttpLogExtended(aft, tx); } /* ip/tcp header info */ MemBufferWriteString(aft->buffer, " [**] %s:%" PRIu16 " -> %s:%" PRIu16 "\n", srcip, sp, dstip, dp); } aft->uri_cnt ++; SCMutexLock(&hlog->file_ctx->fp_mutex); MemBufferPrintToFPAsString(aft->buffer, hlog->file_ctx->fp); fflush(hlog->file_ctx->fp); SCMutexUnlock(&hlog->file_ctx->fp_mutex); AppLayerTransactionUpdateLoggedId(p->flow); } end: FLOWLOCK_UNLOCK(p->flow); SCReturnInt(TM_ECODE_OK); }
/* Custom format logging */ static void LogHttpLogCustom(LogHttpLogThread *aft, htp_tx_t *tx, const struct timeval *ts, char *srcip, Port sp, char *dstip, Port dp) { LogHttpFileCtx *httplog_ctx = aft->httplog_ctx; uint32_t i; char buf[128]; htp_header_t *h_request_hdr = NULL; htp_header_t *h_response_hdr = NULL; time_t time = ts->tv_sec; struct tm local_tm; struct tm *timestamp = (struct tm *)SCLocalTime(time, &local_tm); for (i = 0; i < httplog_ctx->cf_n; i++) { switch (httplog_ctx->cf_nodes[i]->type){ case LOG_HTTP_CF_LITERAL: /* LITERAL */ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)httplog_ctx->cf_nodes[i]->data, strlen(httplog_ctx->cf_nodes[i]->data)); break; case LOG_HTTP_CF_TIMESTAMP: /* TIMESTAMP */ if (httplog_ctx->cf_nodes[i]->data == '\0') { strftime(buf, 62, TIMESTAMP_DEFAULT_FORMAT, timestamp); } else { strftime(buf, 62, httplog_ctx->cf_nodes[i]->data, timestamp); } PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)buf,strlen(buf)); break; case LOG_HTTP_CF_TIMESTAMP_U: /* TIMESTAMP USECONDS */ snprintf(buf, 62, "%06u", (unsigned int) ts->tv_usec); PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)buf,strlen(buf)); break; case LOG_HTTP_CF_CLIENT_IP: /* CLIENT IP ADDRESS */ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)srcip,strlen(srcip)); break; case LOG_HTTP_CF_SERVER_IP: /* SERVER IP ADDRESS */ PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)dstip,strlen(dstip)); break; case LOG_HTTP_CF_CLIENT_PORT: /* CLIENT PORT */ MemBufferWriteString(aft->buffer, "%" PRIu16 "", sp); break; case LOG_HTTP_CF_SERVER_PORT: /* SERVER PORT */ MemBufferWriteString(aft->buffer, "%" PRIu16 "", dp); break; case LOG_HTTP_CF_REQUEST_METHOD: /* METHOD */ if (tx->request_method != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_method), bstr_len(tx->request_method)); } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_REQUEST_URI: /* URI */ if (tx->request_uri != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_uri), bstr_len(tx->request_uri)); } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_REQUEST_HOST: /* HOSTNAME */ if (tx->parsed_uri != NULL && tx->parsed_uri->hostname != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->parsed_uri->hostname), bstr_len(tx->parsed_uri->hostname)); } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_REQUEST_PROTOCOL: /* PROTOCOL */ if (tx->request_protocol != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->request_protocol), bstr_len(tx->request_protocol)); } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_REQUEST_HEADER: /* REQUEST HEADER */ if (tx->request_headers != NULL) { h_request_hdr = table_getc(tx->request_headers, httplog_ctx->cf_nodes[i]->data); } if (h_request_hdr != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(h_request_hdr->value), bstr_len(h_request_hdr->value)); } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_RESPONSE_STATUS: /* RESPONSE STATUS */ if (tx->response_status != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(tx->response_status), bstr_len(tx->response_status)); /* Redirect? */ if ((tx->response_status_number > 300) && ((tx->response_status_number) < 303)){ htp_header_t *h_location = table_getc(tx->response_headers, "location"); if (h_location != NULL) { MemBufferWriteString(aft->buffer, "("); PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(h_location->value), bstr_len(h_location->value)); MemBufferWriteString(aft->buffer, ")"); } } } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_RESPONSE_HEADER: /* RESPONSE HEADER */ if (tx->response_headers != NULL) { h_response_hdr = table_getc(tx->response_headers, httplog_ctx->cf_nodes[i]->data); } if (h_response_hdr != NULL) { PrintRawUriBuf((char *)aft->buffer->buffer, &aft->buffer->offset, aft->buffer->size, (uint8_t *)bstr_ptr(h_response_hdr->value), bstr_len(h_response_hdr->value)); } else { MemBufferWriteString(aft->buffer, LOG_HTTP_CF_NONE); } break; case LOG_HTTP_CF_RESPONSE_LEN: /* RESPONSE LEN */ MemBufferWriteString(aft->buffer, "%"PRIuMAX"", (uintmax_t)tx->response_message_len); break; } } MemBufferWriteString(aft->buffer, "\n"); }
/** * \brief Do the http_user_agent content inspection for a signature. * * \param de_ctx Detection engine context. * \param det_ctx Detection engine thread context. * \param s Signature to inspect. * \param f Flow. * \param flags App layer flags. * \param state App layer state. * * \retval 0 No match. * \retval 1 Match. */ int DetectEngineInspectHttpUA(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Signature *s, Flow *f, uint8_t flags, void *alstate) { SCEnter(); int r = 0; HtpState *htp_state = NULL; htp_tx_t *tx = NULL; int idx; FLOWLOCK_RDLOCK(f); htp_state = (HtpState *)alstate; if (htp_state == NULL) { SCLogDebug("no HTTP state"); goto end; } if (htp_state->connp == NULL || htp_state->connp->conn == NULL) { SCLogDebug("HTP state has no conn(p)"); goto end; } idx = AppLayerTransactionGetInspectId(f); if (idx == -1) { goto end; } int size = (int)list_size(htp_state->connp->conn->transactions); for (; idx < size; idx++) { tx = list_get(htp_state->connp->conn->transactions, idx); if (tx == NULL) continue; htp_header_t *h = (htp_header_t *)table_getc(tx->request_headers, "User-Agent"); if (h == NULL) { SCLogDebug("HTTP user agent header not present in this request"); continue; } det_ctx->buffer_offset = 0; det_ctx->discontinue_matching = 0; det_ctx->inspection_recursion_counter = 0; r = DetectEngineContentInspection(de_ctx, det_ctx, s, s->sm_lists[DETECT_SM_LIST_HUADMATCH], f, (uint8_t *)bstr_ptr(h->value), bstr_len(h->value), DETECT_ENGINE_CONTENT_INSPECTION_MODE_HUAD, NULL); if (r == 1) { break; } } end: FLOWLOCK_UNLOCK(f); SCReturnInt(r); }
/** * Determines presence (and encoding) of a request body. * * @param connp * @returns HTP_OK on state change, HTTP_ERROR on error, or HTP_DATA when more data is needed. */ int htp_connp_REQ_BODY_DETERMINE(htp_connp_t *connp) { htp_header_t *cl = table_getc(connp->in_tx->request_headers, "content-length"); htp_header_t *te = table_getc(connp->in_tx->request_headers, "transfer-encoding"); // Check for the Transfer-Encoding header, which // would indicate a chunked request body if (te != NULL && te->value != NULL) { // Make sure it contains "chunked" only if (bstr_cmpc(te->value, "chunked") != 0) { // Invalid T-E header value htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid T-E value in request"); } // Chunked encoding is a HTTP/1.1 feature. Check // that some other protocol is not used. The flag will // also be set if the protocol could not be parsed. // // TODO IIS 7.0, for example, would ignore the T-E header when it // it is used with a protocol below HTTP 1.1. if (connp->in_tx->request_protocol_number < HTTP_1_1) { connp->in_tx->flags |= HTP_INVALID_CHUNKING; // TODO Log } // If the T-E header is present we are going to use it. connp->in_tx->request_transfer_coding = CHUNKED; // We are still going to check for the presence of C-L if (cl != NULL) { // This is a violation of the RFC connp->in_tx->flags |= HTP_REQUEST_SMUGGLING; // TODO Log } connp->in_state = htp_connp_REQ_BODY_CHUNKED_LENGTH; connp->in_tx->progress[0] = TX_PROGRESS_REQ_BODY; } else // Next check for the presence of the Content-Length header if (cl != NULL && cl->value != NULL) { // It seems that we have a request body. connp->in_tx->request_transfer_coding = IDENTITY; // Check for a folded C-L header if (cl->flags & HTP_FIELD_FOLDED) { connp->in_tx->flags |= HTP_REQUEST_SMUGGLING; // TODO Log } // Check for multiple C-L headers if (cl->flags & HTP_FIELD_REPEATED) { connp->in_tx->flags |= HTP_REQUEST_SMUGGLING; // TODO Log } // Get body length int i = htp_parse_content_length(cl->value); if (i < 0) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Invalid C-L field in request"); return HTP_ERROR; } else { connp->in_content_length = i; connp->in_body_data_left = connp->in_content_length; if (connp->in_content_length != 0) { connp->in_state = htp_connp_REQ_BODY_IDENTITY; connp->in_tx->progress[0] = TX_PROGRESS_REQ_BODY; } else { connp->in_state = htp_connp_REQ_IDLE; connp->in_tx->progress[0] = TX_PROGRESS_WAIT; } } } else { // This request does not have a body, which // means that we're done with it connp->in_state = htp_connp_REQ_IDLE; connp->in_tx->progress[0] = TX_PROGRESS_WAIT; } // Host resolution htp_header_t *h = table_getc(connp->in_tx->request_headers, "host"); if (h == NULL) { // No host information in the headers // HTTP/1.1 requires host information in the headers if (connp->in_tx->request_protocol_number >= HTTP_1_1) { connp->in_tx->flags |= HTP_HOST_MISSING; htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Host information in request headers required by HTTP/1.1"); } } else { // Host information available in the headers // Is there host information in the URI? if (connp->in_tx->parsed_uri->hostname == NULL) { // There is no host information in the URI. Place the // hostname from the headers into the parsed_uri structure. htp_replace_hostname(connp, connp->in_tx->parsed_uri, h->value); } else if (bstr_cmp_nocase(h->value, connp->in_tx->parsed_uri->hostname) != 0) { // The host information is different in the // headers and the URI. The HTTP RFC states that // we should ignore the headers copy. connp->in_tx->flags |= HTP_AMBIGUOUS_HOST; htp_log(connp, HTP_LOG_MARK, HTP_LOG_WARNING, 0, "Host information ambiguous"); } } // Run hook REQUEST_HEADERS int rc = hook_run_all(connp->cfg->hook_request_headers, connp); if (rc != HOOK_OK) { htp_log(connp, HTP_LOG_MARK, HTP_LOG_ERROR, 0, "Request headers callback returned error (%d)", rc); return HTP_ERROR; } return HTP_OK; }