📄 elog_write.c
字号:
/** This file is part of Firestorm NIDS* Copyright (c) 2002 Gianni Tedesco* This program is released under the terms of the GNU GPL version 2** Alert Spool API* o spool_new() - create a new spool object* o spool_open() - open logfile and write header* o spool_close() - flus buffer, close file* o spool_rotate() - rotate a logfile, creation of new file is optional* o spool_check_old() - hack to rotate after a crash* o spool_delete() - close and destroy a spool object* o spool_do_packet() - construct an elog record* o spool_write_slow() - unbuffered write to the spool* o spool_flush() - flush any buffers* o spool_write() - write a packet* o spool_packet() - log a packet** Stormwall Notification API* o stormwall_msg() - send a message to stormwall* o stormwall_open() - open the stormwall fifo* o stormwall_close() - close the stormwall fifo** TODO* o Need to do clever things when writes fail.* o Round-robin spooling* o Weighted round-robin*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <signal.h>#include <sys/stat.h>#include <fcntl.h>#include <netinet/in.h>#include <sys/uio.h>#include <firestorm.h>#include <packet.h>#include <cleanup.h>#include <alert.h>#include <signature.h>#include <decode.h>#include <stormwall.h>#include <elog.h>#include <elog_write.h>/* A blank 4 byte area we can point to for zero padding */static const char alert_padding[4]={0,0,0,0};/* create a new spool object */struct elog_spool *spool_new(void){ struct elog_spool *ret; if ( !(ret=calloc(1, sizeof(*ret))) ) return NULL; ret->fd=-1; ret->stormwall=STORMWALL_NONE; ret->fifo_fd=-1; ret->max_bytes=SPOOL_DEFAULT_SIZE; ret->max_time=SPOOL_DEFAULT_MINS; ret->buf_sz=SPOOL_DEFAULT_BUFFER; return ret;}/* Set the buffer size */int spool_set_buf(struct elog_spool *s, size_t sz){ if ( sz ) { if ( !(s->buf=malloc(sz)) ) { return 0; } } s->ptr=s->buf; s->buf_len=sz; s->buf_sz=sz; return 1;}/* Open a new logfile and write the header */int spool_open(struct elog_spool *s){ struct efile_hdr fhdr; int ret; /* Open up the log file */ if ( (s->fd=open(s->alert_fn, O_WRONLY|O_CREAT|O_EXCL, 0600))<0 ) { mesg(M_CRIT,"%s: open(): %s", s->alert_fn, get_err()); return 0; } /* Construct header */ fhdr.magic=htonl(EF_MAGIC); fhdr.flags=0; fhdr.vers_minor=EF_VERS_MIN; fhdr.vers_major=EF_VERS_MAJ; /* Write header */ if ( (ret=write(s->fd, &fhdr, sizeof(fhdr)))<sizeof(fhdr) ) { if ( ret < 0 ) { mesg(M_CRIT,"elog: %s: write(): %s", s->alert_fn, get_err()); return 0; }else{ mesg(M_CRIT,"elog: %s: writing file header: " "truncated", s->alert_fn); return 0; } } /* Setup the structures */ s->size=sizeof(struct efile_hdr); return 1;}/* spool_close() - flush data to disk and close file */void spool_close(struct elog_spool *s){ spool_flush(s); if ( fsync(s->fd) ) mesg(M_CRIT,"alert: fsync(): %s", get_err()); if ( close(s->fd) ) mesg(M_CRIT,"alert: close(): %s", get_err()); s->fd=-1;}/* All-in-one function to perform a log rotation */void spool_rotate(struct elog_spool *s, int new){ struct stat st; /* XXX: Never happens right? */ if ( s->fd<0 ) { mesg(M_DEBUG, "alert: spool_rotate fd=%i", s->fd); spool_open(s); return; } /* Get the inode number before closing */ if ( fstat(s->fd, &st) ) { mesg(M_CRIT, "alert: fstat(): %s", get_err()); st.st_ino=0xdeadbeef; /* uh-oh */ } /* Flush and close the old one */ spool_close(s); /* Build a name to rotate the old file to */ snprintf(s->rotate_fn, ALERT_FNLEN+1, "@%.8lx.%.8lx.elog", s->tv.tv_sec, st.st_ino); /* Rotate it */ if ( rename(s->alert_fn, s->log_dir) ) { mesg(M_CRIT,"alert: rename(): %s", get_err()); }else{ mesg(M_DEBUG,"logrotate: %s", s->log_dir); } /* Wake up the stormwall daemon if necessary */ stormwall_msg(s, STORMWALL_ROTATE); s->tv.tv_sec=0; /* Open a new one */ if ( new ) spool_open(s);}/* Rotate a stale logfile. It's racy, but fuck it. */int spool_check_old(struct elog_spool *s){ struct { struct efile_hdr fh; struct elog_pkthdr pkth; }buf; if ( (s->fd=open(s->alert_fn, O_RDONLY))<0 ) return spool_open(s); /* Read the date of the first packet and then rotate it */ if ( read(s->fd, &buf, sizeof(buf))<sizeof(buf) ) { goto kill_it; } /* Check it aint corrupt */ if ( ntohl(buf.fh.magic) != EF_MAGIC || buf.fh.vers_major != EF_VERS_MAJ || buf.fh.vers_minor != EF_VERS_MIN ) goto kill_it; /* Grab the timestamp of the first entry */ s->tv.tv_sec=buf.pkth.h.ts.tv_sec; s->tv.tv_usec=buf.pkth.h.ts.tv_usec; /* Rotate it */ spool_rotate(s, 1); return 1;kill_it: close(s->fd); /* If there is no first packet, just unlink it */ if ( unlink(s->alert_fn) ) { mesg(M_WARN, "alert: %s: unlink(): ", s->alert_fn, get_err()); return 0; } mesg(M_WARN, "alert: obliterated %s", s->alert_fn); return spool_open(s);}/* finish up with a spool */void spool_delete(struct elog_spool *s){ if ( !spool_isempty(s) ) { /* If we have logged to this file then rotate it */ spool_rotate(s, 0); }else if ( s->fd>=0 ) { /* If not, then close and delete it */ spool_close(s); if ( unlink(s->alert_fn) ) mesg(M_CRIT,"alert: unlink(): %s", get_err()); } /* Close stormwall */ stormwall_close(s); /* Free up all our path information */ if ( s->alert_fn ) free(s->alert_fn); if ( s->log_dir ) free(s->log_dir); if ( s->fifo_fn ) free(s->fifo_fn); if ( s->buf ) free(s->buf); free(s);}/* Send a message to stormwall */int stormwall_msg(struct elog_spool *s, int msg){ unsigned char code=msg&0xff; if ( s->stormwall ) { if ( write(s->fifo_fd, (char *)&code, sizeof(code))<0 ) { mesg(M_CRIT, "alert: stormwall: %s", get_err()); return -1; } } return 0;}/* Open the stormwall communications FIFO */void stormwall_open(struct elog_spool *s){ int flags=0; if ( s->stormwall==STORMWALL_FAIL ) { flags=O_NONBLOCK; }else if ( s->stormwall==STORMWALL_WAIT ) { mesg(M_DEBUG,"alert: attempting to contact stormwall..."); }else return; if ( (s->fifo_fd=open(s->fifo_fn, O_WRONLY|flags))<0 ) { mesg(M_ERR, "%s: open(): %s", s->fifo_fn, get_err()); cleanup(EXIT_ERR, "alert: unable to contact stormwall"); return; } if ( s->stormwall==STORMWALL_WAIT ) { mesg(M_DEBUG,"alert: ...SUCCESS!"); }}/* close a stormwall socket */void stormwall_close(struct elog_spool *s){ if ( s->fifo_fd<0 ) return; if ( close(s->fifo_fd) ) mesg(M_WARN, "alert: %s: close(): %s", s->fifo_fn, get_err());}/* Convert a alert information in to an elog record */static int spool_do_packet(struct elog_spool *s, struct event_alert *ev){ static char decode_buf[4096]; static char sbuf[512]; struct iovec *iov=s->iov; struct packet *pkt=ev->pkt; struct elog_dhdr *dh; char *ptr=decode_buf; char *ptr_end=decode_buf+sizeof(decode_buf); size_t tot_len=0; int i=0; int j; /* Construct record header */ s->ph.h.type=ELOG_ALERT; s->ph.h.prio=ev->a->priority; s->ph.h.reserved=0; s->ph.h.ts.tv_sec=pkt->time.tv_sec; s->ph.h.ts.tv_usec=pkt->time.tv_usec; /* Construct alert specific bit */ s->ph.sid=ev->a->sid; s->ph.rev=ev->a->rev; s->ph.pflags=pkt->flags; s->ph.decode_len=0; s->ph.gen_len=strlen(ev->gen->name)+1; s->ph.alert_len=strlen(ev->a->alert)+1; s->ph.pkt_len=pkt->len; s->ph.pkt_caplen=pkt->caplen; /* Record header vector */ iov[i].iov_base=&s->ph; iov[i].iov_len=sizeof(s->ph); tot_len+=iov[i++].iov_len; /* Construct the decode data */ for(j=0; j<pkt->llen; j++) { int slen=0, nlen, real; if ( pkt->layer[j].proto ) { struct layer *l=&pkt->layer[j]; /* Call plugins to serialise state data */ if ( l->session && l->proto->serialize ) { slen=l->proto->serialize(pkt, j, sbuf, sizeof(sbuf)); if ( slen<0 ) { mesg(M_WARN, "alert: %s state data too big", l->proto->name); } /* Round to the nearest multiple of 4 */ if ( slen & 0x3 ) slen+=(4-(slen&0x3)); } real=nlen=strlen(l->proto->name)+1; }else{ real=nlen=slen=0; } /* Round to the nearest multiple of 4 */ if ( nlen & 0x3 ) nlen+=(4-(nlen&0x3)); if ( ptr+nlen+sizeof(*dh) > ptr_end ) { mesg(M_CRIT,"elog: decode buffer too small!"); return -1; } /* Fill in the header */ dh=(struct elog_dhdr *)ptr; ptr+=sizeof(*dh); dh->tot_len=(sizeof(*dh)+nlen+slen)>>2; dh->name_len=nlen>>2; dh->pkt_ofs=htons(pkt->layer[j].h.raw - pkt->base); dh->flags=htonl(pkt->layer[j].flags); /* Then the protocol name */ if ( real ) { memcpy(ptr, pkt->layer[j].proto->name, real); ptr+=real; /* make sure padding is zeroed */ if ( nlen-real ) { memset(ptr, 0, nlen-real); ptr+=nlen-real; } } /* then the session data */ if ( slen ) { memcpy(ptr, sbuf, slen); ptr+=slen; } } /* decode data vector */ iov[i].iov_base=decode_buf; iov[i].iov_len=ptr-decode_buf; /* fill in/update record header fields */ s->ph.decode_len=(iov[i].iov_len>>2); tot_len+=iov[i++].iov_len; /* raw packet data vector */ iov[i].iov_base=pkt->base; iov[i].iov_len=pkt->caplen; tot_len+=iov[i++].iov_len; /* generator string */ iov[i].iov_base=ev->gen->name; iov[i].iov_len=s->ph.gen_len; tot_len+=iov[i++].iov_len; /* Alert string */ iov[i].iov_base=ev->a->alert; iov[i].iov_len=s->ph.alert_len; tot_len+=iov[i++].iov_len; /* Pad to multiples of 4 bytes (ensures * aligned access to next header if file * is mmap()ed in) */ if ( tot_len & 3 ) { iov[i].iov_base=(char *)alert_padding; iov[i].iov_len=(4-(tot_len&3)); tot_len+=iov[i++].iov_len; } /* Convert to network byte order */ s->ph.h.reclen=htonl(tot_len); s->ph.h.type=htons(s->ph.h.type); s->ph.h.ts.tv_sec=htonl(s->ph.h.ts.tv_sec); s->ph.h.ts.tv_usec=htonl(s->ph.h.ts.tv_usec); s->ph.sid=htonl(s->ph.sid); s->ph.rev=htonl(s->ph.rev); s->ph.pflags=htonl(s->ph.pflags); s->ph.decode_len=htons(s->ph.decode_len); s->ph.pkt_len=htonl(s->ph.pkt_len); s->ph.pkt_caplen=htonl(s->ph.pkt_caplen); s->tot_len=tot_len; s->io_len=i; return 0;}/* Unbuffered write to the spool */int spool_write_slow(struct elog_spool *s){ int ret; /* Actually write it all */ if ( (ret=writev(s->fd, s->iov, s->io_len))==s->tot_len ) { s->size+=s->tot_len; return 0; } /* Check for errors: a total error is recoverable */ if ( ret<0 ) { mesg(M_CRIT,"elog: %s: writev(): %s", s->alert_fn, get_err()); return -1; } /* Holy shits! It didn't get written out, maybe we should * rotate now to prevent further losses?? */ mesg(M_CRIT, "elog: %u/%u bytes written: your log is corrupt!", s->tot_len-ret, s->tot_len); s->size+=ret; return -1;}/* flush any output buffers, out of line */int spool_flush(struct elog_spool *s){ int len=s->buf_sz-s->buf_len; int ret; /* write the buffer */ if ( (ret=write(s->fd, s->buf, len))==len ) { /* reset the variables */ s->ptr=s->buf; s->buf_len=s->buf_sz; s->size+=len; return 0; } if ( ret<0 ) { mesg(M_CRIT,"elog: %s: write(): %s", s->alert_fn, get_err()); return -1; } mesg(M_CRIT, "elog: %u/%u bytes written: your log is corrupt!", s->tot_len-ret, s->tot_len); s->size+=ret; return -1;}/* Spool writing fast path */static inline int spool_write(struct elog_spool *s){ int i; /* If this packet can't fit in the remaining * buffer space, then flush the buffer */ if ( s->tot_len > s->buf_len ) { spool_flush(s); /* If the buffer is flushed, and the packet is * still too big, then just write it out as is. */ if ( s->tot_len > s->buf_len ) return spool_write_slow(s); } /* common case, append to the buffer */ for (i=0; i<s->io_len; i++) { memcpy(s->ptr, s->iov[i].iov_base, s->iov[i].iov_len); s->ptr+=s->iov[i].iov_len; } s->buf_len-=s->tot_len; return 0;}/* Send a packet to the alert spool */int spool_packet(struct elog_spool *s, struct event_alert *ev){ /* Convert to elog format */ if ( spool_do_packet(s, ev)<0 ) return -1; /* Rotation filenames contain the timestamp * and serial number of the first packet in * that file */ if ( !s->tv.tv_sec ) { s->tv.tv_sec=ev->pkt->time.tv_sec; s->tv.tv_usec=ev->pkt->time.tv_usec; } /* Check to see if we exceeded max logfile size or * haven't rotated in a while */ if ( (s->max_bytes && (s->size + s->tot_len > s->max_bytes)) || (s->max_time && (ev->pkt->time.tv_sec > s->tv.tv_sec + s->max_time)) ) { spool_rotate(s, 1); } return spool_write(s);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -