/* ** Modular Logfile Analyzer ** Copyright 2000 Jan Kneschke ** ** Homepage: http://www.modlogan.org ** This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version, and provided that the above copyright and permission notice is included with all distributed copies of this or derived software. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ** ** $Id: parse.c,v 1.12 2004/03/18 02:31:50 ostborn Exp $ */ #include #include #include #include #include #include #include #include "mlocale.h" #include "mplugins.h" #include "mrecord.h" #include "mdatatypes.h" #include "misc.h" #include "plugin_config.h" #define DEBUG_PCRE const char *short_month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL}; enum { M_BSDFTPD_UNSET, M_BSDFTPD_USER_LOGIN_SUCCESS, M_BSDFTPD_CONNECTION, M_BSDFTPD_RENAME, M_BSDFTPD_MKDIR, M_BSDFTPD_USER, M_BSDFTPD_GET, M_BSDFTPD_PUT, M_BSDFTPD_DELETE, M_BSDFTPD_RMDIR, M_BSDFTPD_APPEND, M_BSDFTPD_USER_LOGIN_FAILED,M_BSDFTPD_USER_LOGIN_REFUSED, M_BSDFTPD_ANONYMOUS_LOGIN_SUCCESS,M_BSDFTPD_ANONYMOUS_LOGIN_REFUSED }; time_t parse_timestamp(mconfig *ext_conf, const char *str) { #define N 20 + 1 int ovector[3 * N], n, i; char buf[10]; struct tm tm; config_input *conf = ext_conf->plugin_conf; if ((n = pcre_exec(conf->match_bsdftpd_syslog_timestamp, NULL, str, strlen(str), 0, 0, ovector, 3 * N)) < 0) { if (n == PCRE_ERROR_NOMATCH) { fprintf(stderr, "%s.%d: string doesn't match: %s\n", __FILE__, __LINE__, str); } else { fprintf(stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n); } return 0; } memset(&tm, 0, sizeof(struct tm)); /* everything has matched, take the different pieces and be happy :) */ pcre_copy_substring(str, ovector, n, 2, buf, sizeof(buf)); tm.tm_mday = strtol(buf, NULL, 10); pcre_copy_substring(str, ovector, n, 1, buf, sizeof(buf)); for (i = 0; short_month[i];i++) { if (!strcmp(buf, short_month[i])) { tm.tm_mon = i; } } /* no year provided :( */ tm.tm_year = 2000 - 1900; pcre_copy_substring(str, ovector, n, 3, buf, sizeof(buf)); tm.tm_hour = strtol(buf, NULL, 10); pcre_copy_substring(str, ovector, n, 4, buf, sizeof(buf)); tm.tm_min = strtol(buf, NULL, 10); pcre_copy_substring(str, ovector, n, 5, buf, sizeof(buf)); tm.tm_sec = strtol(buf, NULL, 10); return mktime (&tm); #undef N } int create_connection(mconfig *ext_conf, pid_t pid, time_t timestamp, const char *hostname, const char *ip) { config_input *conf = ext_conf->plugin_conf; int i; if (conf->connections == NULL) { /* setup connection container */ conf->connection_size = 128; conf->connections = malloc( sizeof(bsdftpd_user *) * conf->connection_size ); for (i = 0; i < conf->connection_size; i++) { conf->connections[i] = NULL; } } /* search for empty slot */ for (i = 0; i < conf->connection_size; i++) { if (conf->connections[i] == NULL) { conf->connections[i] = malloc( sizeof(bsdftpd_user) ); conf->connections[i]->pid = pid; conf->connections[i]->starttstmp = timestamp; conf->connections[i]->lasttstmp = timestamp; conf->connections[i]->username = NULL; conf->connections[i]->hostname = malloc(strlen(hostname) + 1); strcpy(conf->connections[i]->hostname, hostname); conf->connections[i]->ip = malloc(strlen(ip) + 1); strcpy(conf->connections[i]->ip, ip); conf->connections[i]->state = M_BSDFTPD_UNSET; fprintf(stderr, "-> %5d [%s]\n", conf->connections[i]->pid, conf->connections[i]->hostname); break; } } if (i == conf->connection_size) { printf("full\n"); } return 0; } int cleanup_connections(mconfig *ext_conf, time_t timestamp) { config_input *conf = ext_conf->plugin_conf; int i; /* search for 'pid' slot */ for (i = 0; i < conf->connection_size; i++) { if (conf->connections[i] != NULL) { int del = 0; if (conf->connections[i]->lasttstmp + 1200 < timestamp) { /* client has already closed the connection */ fprintf(stderr, "<- %5d - server timeout\n", conf->connections[i]->pid); del = 1; } else if (conf->connections[i]->state != M_BSDFTPD_USER_LOGIN_SUCCESS && conf->connections[i]->state != M_BSDFTPD_UNSET) { /* we are not logged in */ del = 1; switch(conf->connections[i]->state) { case M_BSDFTPD_USER: fprintf(stderr, "<- %5d - user timeout\n", conf->connections[i]->pid); break; case M_BSDFTPD_USER_LOGIN_REFUSED: fprintf(stderr, "<- %5d - login refused\n", conf->connections[i]->pid); break; case M_BSDFTPD_USER_LOGIN_FAILED: fprintf(stderr, "<- %5d - login failed\n", conf->connections[i]->pid); break; case M_BSDFTPD_ANONYMOUS_LOGIN_REFUSED: fprintf(stderr, "<- %5d - anonymous disabled\n", conf->connections[i]->pid); break; default: fprintf(stderr, "<- %5d - ??\n", conf->connections[i]->pid); } } if (del) { free(conf->connections[i]->hostname); free(conf->connections[i]->ip); free(conf->connections[i]->username); free(conf->connections[i]); conf->connections[i] = NULL; } } } return 0; } int set_connection_state(mconfig *ext_conf, pid_t pid, time_t timestamp, int state, const char *v1) { config_input *conf = ext_conf->plugin_conf; int i; /* search for 'pid' slot */ for (i = 0; i < conf->connection_size; i++) { if (conf->connections[i] != NULL && conf->connections[i]->pid == pid) { conf->connections[i]->state = state; if (state != M_BSDFTPD_USER_LOGIN_SUCCESS) { fprintf(stderr, "st: pid %5d state -> %d\n", pid, state); } conf->connections[i]->lasttstmp = timestamp; if (v1) { if (conf->connections[i]->username) { fprintf(stderr, " !! %s -> %s\n", conf->connections[i]->username, v1); free(conf->connections[i]->username); } conf->connections[i]->username = strdup(v1); } break; } } if (i == conf->connection_size) { fprintf(stderr, "st: pid %5d not found\n", pid); } return 0; } int handle_command(mconfig *ext_conf, pid_t pid, time_t timestamp, int cmd, const char *v1, const char *v2, mlogrec *rec) { config_input *conf = ext_conf->plugin_conf; int i; /* search for 'pid' slot */ for (i = 0; i < conf->connection_size; i++) { if (conf->connections[i] != NULL && conf->connections[i]->pid == pid) { mlogrec_web *recweb; mlogrec_web_ftp *recftp; conf->connections[i]->lasttstmp = timestamp; rec->timestamp = timestamp; if (rec->ext_type != M_RECORD_TYPE_WEB) { if (rec->ext_type != M_RECORD_TYPE_UNSET) { mrecord_free_ext(rec); } rec->ext_type = M_RECORD_TYPE_WEB; rec->ext = mrecord_init_web(); } recweb = rec->ext; if (recweb == NULL) return M_RECORD_HARD_ERROR; buffer_copy_string(recweb->req_user, conf->connections[i]->username); switch(cmd) { case M_BSDFTPD_PUT: case M_BSDFTPD_GET: case M_BSDFTPD_DELETE: recweb->ext = mrecord_init_web_ftp(); recweb->ext_type = M_RECORD_TYPE_WEB_FTP; recftp = recweb->ext; buffer_copy_string(recweb->req_url, v1); if (cmd == M_BSDFTPD_GET) { recftp->trans_command = M_RECORD_FTP_COMMAND_GET; recweb->xfersize = strtod(v2, NULL); } else if (cmd == M_BSDFTPD_PUT) { recftp->trans_command = M_RECORD_FTP_COMMAND_PUT; recweb->xfersize = strtod(v2, NULL); } else if (cmd == M_BSDFTPD_DELETE) { recftp->trans_command = M_RECORD_FTP_COMMAND_DELETE; } break; default: break; } break; } } if (i == conf->connection_size) { fprintf(stderr, "hn: pid %5d not found\n", pid); } return 0; } typedef struct { int type; pcre *match; } Matches; int parse_record_pcre(mconfig *ext_conf, mlogrec *record, buffer *b) { #define N 20 + 1 const char **list; int ovector[3 * N], n; int i, match; config_input *conf = ext_conf->plugin_conf; const Matches matches [] = { { M_BSDFTPD_USER_LOGIN_SUCCESS, conf->match_bsdftpd_user_login_success }, { M_BSDFTPD_CONNECTION, conf->match_bsdftpd_connection }, { M_BSDFTPD_RENAME, conf->match_bsdftpd_rename }, { M_BSDFTPD_MKDIR, conf->match_bsdftpd_mkdir }, { M_BSDFTPD_USER, conf->match_bsdftpd_user }, { M_BSDFTPD_GET, conf->match_bsdftpd_get }, { M_BSDFTPD_PUT, conf->match_bsdftpd_put }, { M_BSDFTPD_DELETE, conf->match_bsdftpd_delete }, { M_BSDFTPD_RMDIR, conf->match_bsdftpd_rmdir }, { M_BSDFTPD_APPEND, conf->match_bsdftpd_append }, { M_BSDFTPD_USER_LOGIN_FAILED, conf->match_bsdftpd_user_login_failed }, { M_BSDFTPD_USER_LOGIN_REFUSED, conf->match_bsdftpd_user_login_refused }, { M_BSDFTPD_ANONYMOUS_LOGIN_SUCCESS, conf->match_bsdftpd_anonymous_login_success }, { M_BSDFTPD_ANONYMOUS_LOGIN_REFUSED, conf->match_bsdftpd_anonymous_login_refused }, { 0, NULL } }; /* try to match the syslog prefix */ if ((n = pcre_exec(conf->match_bsdftpd_syslog_pre, NULL, b->ptr, b->used - 1, 0, 0, ovector, 3 * N)) < 0) { if (n == PCRE_ERROR_NOMATCH) { #if 0 fprintf(stderr, "%s.%d: syslog prefix doesn't match: %s\n", __FILE__, __LINE__, b->ptr); #endif return M_RECORD_IGNORED; } else { fprintf(stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n); return M_RECORD_HARD_ERROR; } } /* ok, it's a syslog - the rest should be bsdftpd specific */ if (n) { pid_t pid; time_t tstmp; /* get timestamp */ /* * 1 - timestamp * 5 - pid */ pcre_get_substring_list(b->ptr, ovector, n, &list); pid = strtol(list[5], NULL, 10); tstmp = parse_timestamp(ext_conf, list[1]); pcre_free(list); match = -1; for ( i = 0; matches[i].match != NULL; i++ ) { /* find the corresponding match */ if ((n = pcre_exec(matches[i].match, NULL, b->ptr, b->used - 1, 0, 0, ovector, 3 * N)) < 0) { if (n != PCRE_ERROR_NOMATCH) { fprintf(stderr, "%s.%d: execution error while matching: %d\n", __FILE__, __LINE__, n); return M_RECORD_HARD_ERROR; } } else { match = matches[i].type; break; } } if (n > 1 && match != -1) { pcre_get_substring_list(b->ptr, ovector, n, &list); /* disable */ record->timestamp = 0; switch (match) { case M_BSDFTPD_CONNECTION: create_connection(ext_conf, pid, tstmp, list[1], list[2]); break; case M_BSDFTPD_USER_LOGIN_SUCCESS: case M_BSDFTPD_ANONYMOUS_LOGIN_SUCCESS: case M_BSDFTPD_USER_LOGIN_REFUSED: case M_BSDFTPD_USER_LOGIN_FAILED: case M_BSDFTPD_USER: set_connection_state(ext_conf, pid, tstmp, match, list[2]); break; case M_BSDFTPD_ANONYMOUS_LOGIN_REFUSED: set_connection_state(ext_conf, pid, tstmp, match, NULL); break; case M_BSDFTPD_RENAME: case M_BSDFTPD_APPEND: case M_BSDFTPD_GET: case M_BSDFTPD_PUT: /* filename + filesize */ handle_command(ext_conf, pid, tstmp, match, list[1], list[2], record); break; case M_BSDFTPD_RMDIR: case M_BSDFTPD_MKDIR: case M_BSDFTPD_DELETE: /* dir/filename */ handle_command(ext_conf, pid, tstmp, match, list[1], NULL, record); break; default: #if 1 for (i = 0; i < n; i++) { printf("%d: %s\n", i, list[i]); } #endif } free(list); cleanup_connections(ext_conf, tstmp); return record->ext ? M_RECORD_NO_ERROR : M_RECORD_IGNORED; } else { fprintf(stderr, "%s.%d: was das ?? %s\n", __FILE__, __LINE__, b->ptr); return M_RECORD_CORRUPT; } } else { return M_RECORD_CORRUPT; } return M_RECORD_IGNORED; #undef N } int mplugins_input_bsdftpd_get_next_record(mconfig *ext_conf, mlogrec *record) { int ret = 0; config_input *conf = ext_conf->plugin_conf; if (record == NULL) return M_RECORD_HARD_ERROR; /* fill the line buffer */ if (NULL == mgets(&(conf->inputfile), conf->buf)) return M_RECORD_EOF; ret = parse_record_pcre (ext_conf, record, conf->buf); if (ret == M_RECORD_CORRUPT) { M_DEBUG1(ext_conf->debug_level, M_DEBUG_SECTION_PARSING, M_DEBUG_LEVEL_WARNINGS, "affected Record: %s\n", conf->buf->ptr ); } return ret; }