2 * Unix SMB/CIFS implementation.
3 * Eventlog utility routines
4 * Copyright (C) Marcin Krzysztof Porwit 2005,
5 * Copyright (C) Brian Moran 2005.
6 * Copyright (C) Gerald (Jerry) Carter 2005.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 /* maintain a list of open eventlog tdbs with reference counts */
27 static ELOG_TDB *open_elog_list;
29 /********************************************************************
30 Init an Eventlog TDB, and return it. If null, something bad
32 ********************************************************************/
34 TDB_CONTEXT *elog_init_tdb( char *tdbfilename )
38 DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n",
41 tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT,
42 O_RDWR|O_CREAT|O_TRUNC, 0600 );
45 DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) );
49 /* initialize with defaults, copy real values in here from registry */
51 tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 );
52 tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 );
53 tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 );
54 tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 );
56 tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 );
61 /********************************************************************
62 make the tdb file name for an event log, given destination buffer
63 and size. Caller must free memory.
64 ********************************************************************/
66 char *elog_tdbname( const char *name )
70 char *eventlogdir = lock_path( "eventlog" );
72 pstr_sprintf( path, "%s/%s.tdb", eventlogdir, name );
74 tdb_fullpath = SMB_STRDUP( path );
80 /********************************************************************
81 this function is used to count up the number of bytes in a
83 ********************************************************************/
85 struct trav_size_struct {
90 static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
93 struct trav_size_struct *tsize = state;
95 tsize->size += data.dsize;
101 /********************************************************************
102 returns the size of the eventlog, and if MaxSize is a non-null
103 ptr, puts the MaxSize there. This is purely a way not to have yet
104 another function that solely reads the maxsize of the eventlog.
106 ********************************************************************/
108 int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
110 struct trav_size_struct tsize;
115 ZERO_STRUCT( tsize );
117 tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize );
119 if ( MaxSize != NULL ) {
120 *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE );
123 if ( Retention != NULL ) {
124 *Retention = tdb_fetch_int32( tdb, EVT_RETENTION );
128 ( "eventlog size: [%d] for [%d] records\n", tsize.size,
133 /********************************************************************
134 Discard early event logs until we have enough for 'needed' bytes...
135 NO checking done beforehand to see that we actually need to do
136 this, and it's going to pluck records one-by-one. So, it's best
137 to determine that this needs to be done before doing it.
139 Setting whack_by_date to True indicates that eventlogs falling
140 outside of the retention range need to go...
142 return True if we made enough room to accommodate needed bytes
143 ********************************************************************/
145 BOOL make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
148 int start_record, i, new_start;
150 int nbytes, reclen, len, Retention, MaxSize;
151 int tresv1, trecnum, timegen, timewr;
153 TALLOC_CTX *mem_ctx = NULL;
154 time_t current_time, exp_time;
156 /* discard some eventlogs */
158 /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
159 although records not necessarily guaranteed to have successive times */
161 mem_ctx = talloc_init( "make_way_for_eventlogs" ); /* Homage to BPG */
163 if ( mem_ctx == NULL )
164 return False; /* can't allocate memory indicates bigger problems */
166 tdb_lock_bystring( the_tdb, EVT_NEXT_RECORD, 1 );
168 end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
169 start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
170 Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
171 MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );
173 time( ¤t_time );
176 exp_time = current_time - Retention; /* discard older than exp_time */
178 /* todo - check for sanity in next_record */
182 ( "MaxSize [%d] Retention [%d] Current Time [%d] exp_time [%d]\n",
183 MaxSize, Retention, (uint32)current_time, (uint32)exp_time ) );
185 ( "Start Record [%d] End Record [%d]\n", start_record,
188 for ( i = start_record; i < end_record; i++ ) {
189 /* read a record, add the amt to nbytes */
190 key.dsize = sizeof( int32 );
191 key.dptr = ( char * ) ( int32 * ) & i;
192 ret = tdb_fetch( the_tdb, key );
193 if ( ret.dsize == 0 ) {
195 ( "Can't find a record for the key, record [%d]\n",
197 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
200 nbytes += ret.dsize; /* note this includes overhead */
202 len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
203 &tresv1, &trecnum, &timegen, &timewr );
205 ( "read record %d, record size is [%d], total so far [%d]\n",
206 i, reclen, nbytes ) );
208 SAFE_FREE( ret.dptr );
210 /* note that other servers may just stop writing records when the size limit
211 is reached, and there are no records older than 'retention'. This doesn't
212 like a very useful thing to do, so instead we whack (as in sleeps with the
213 fishes) just enough records to fit the what we need. This behavior could
214 be changed to 'match', if the need arises. */
216 if ( !whack_by_date && ( nbytes >= needed ) )
218 if ( whack_by_date && ( timegen >= exp_time ) )
223 ( "nbytes [%d] needed [%d] start_record is [%d], should be set to [%d]\n",
224 nbytes, needed, start_record, i ) );
225 /* todo - remove eventlog entries here and set starting record to start_record... */
227 if ( start_record != new_start ) {
228 for ( i = start_record; i < new_start; i++ ) {
229 key.dsize = sizeof( int32 );
230 key.dptr = ( char * ) ( int32 * ) & i;
231 tdb_delete( the_tdb, key );
234 tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
236 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
240 /********************************************************************
241 some hygiene for an eventlog - see how big it is, and then
242 calculate how many bytes we need to remove
243 ********************************************************************/
245 BOOL prune_eventlog( TDB_CONTEXT * tdb )
247 int MaxSize, Retention, CalcdSize;
250 DEBUG( 4, ( "No eventlog tdb handle\n" ) );
254 CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention );
256 ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
259 if ( CalcdSize > MaxSize ) {
260 return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
264 return make_way_for_eventlogs( tdb, 0, True );
267 /********************************************************************
268 ********************************************************************/
270 BOOL can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
273 int MaxSize, Retention;
275 /* see if we can write to the eventlog -- do a policy enforcement */
277 return False; /* tdb is null, so we can't write to it */
285 calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention );
287 if ( calcd_size <= MaxSize )
288 return True; /* you betcha */
289 if ( calcd_size + needed < MaxSize )
292 if ( Retention == 0xffffffff ) {
293 return False; /* see msdn - we can't write no room, discard */
296 note don't have to test, but always good to show intent, in case changes needed
300 if ( Retention == 0x00000000 ) {
301 /* discard record(s) */
302 /* todo - decide when to remove a bunch vs. just what we need... */
303 return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
307 return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
310 /*******************************************************************
311 *******************************************************************/
313 ELOG_TDB *elog_open_tdb( char *logname, BOOL force_clear )
315 TDB_CONTEXT *tdb = NULL;
320 ELOG_TDB *tdb_node = NULL;
323 /* first see if we have an open context */
325 for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
326 if ( strequal( ptr->name, logname ) ) {
329 /* trick to alow clearing of the eventlog tdb.
330 The force_clear flag should imply that someone
331 has done a force close. So make sure the tdb
332 is NULL. If this is a normal open, then just
333 return the existing reference */
336 SMB_ASSERT( ptr->tdb == NULL );
344 /* make sure that the eventlog dir exists */
346 eventlogdir = lock_path( "eventlog" );
347 if ( !directory_exist( eventlogdir, NULL ) )
348 mkdir( eventlogdir, 0755 );
350 /* get the path on disk */
352 tdbfilename = elog_tdbname( logname );
353 pstrcpy( tdbpath, tdbfilename );
354 SAFE_FREE( tdbfilename );
356 DEBUG(7,("elog_open_tdb: Opening %s...(force_clear == %s)\n",
357 tdbpath, force_clear?"True":"False" ));
359 /* the tdb wasn't already open or this is a forced clear open */
361 if ( !force_clear ) {
363 tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, O_RDWR , 0 );
365 vers_id = tdb_fetch_int32( tdb, EVT_VERSION );
367 if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) {
368 DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n",
371 tdb = elog_init_tdb( tdbpath );
377 tdb = elog_init_tdb( tdbpath );
379 /* if we got a valid context, then add it to the list */
382 /* on a forced clear, just reset the tdb context if we already
383 have an open entry in the list */
390 if ( !(tdb_node = TALLOC_ZERO_P( NULL, ELOG_TDB)) ) {
391 DEBUG(0,("elog_open_tdb: talloc() failure!\n"));
396 tdb_node->name = talloc_strdup( tdb_node, logname );
398 tdb_node->ref_count = 1;
400 DLIST_ADD( open_elog_list, tdb_node );
406 /*******************************************************************
407 Wrapper to handle reference counts to the tdb
408 *******************************************************************/
410 int elog_close_tdb( ELOG_TDB *etdb, BOOL force_close )
419 SMB_ASSERT( etdb->ref_count >= 0 );
421 if ( etdb->ref_count == 0 ) {
423 DLIST_REMOVE( open_elog_list, etdb );
425 return tdb_close( tdb );
431 return tdb_close( tdb );
438 /*******************************************************************
439 write an eventlog entry. Note that we have to lock, read next
440 eventlog, increment, write, write the record, unlock
442 coming into this, ee has the eventlog record, and the auxilliary date
443 (computer name, etc.) filled into the other structure. Before packing
444 into a record, this routine will calc the appropriate padding, etc.,
445 and then blast out the record in a form that can be read back in
446 *******************************************************************/
450 int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
454 TALLOC_CTX *mem_ctx = NULL;
461 mem_ctx = talloc_init( "write_eventlog_tdb" );
463 if ( mem_ctx == NULL )
468 /* discard any entries that have bogus time, which usually indicates a bogus entry as well. */
469 if ( ee->record.time_generated == 0 )
472 /* todo - check for sanity in next_record */
474 fixup_eventlog_entry( ee );
476 if ( !can_write_to_eventlog( the_tdb, ee->record.length ) ) {
477 DEBUG( 3, ( "Can't write to Eventlog, no room \n" ) );
478 talloc_destroy( mem_ctx );
482 /* alloc mem for the packed version */
483 packed_ee = TALLOC( mem_ctx, ee->record.length + MARGIN );
485 talloc_destroy( mem_ctx );
489 /* need to read the record number and insert it into the entry here */
492 tdb_lock_bystring( the_tdb, EVT_NEXT_RECORD, 1 );
494 next_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
497 tdb_pack( (char *)packed_ee, ee->record.length + MARGIN,
498 "ddddddwwwwddddddBBdBBBd", ee->record.length,
499 ee->record.reserved1, next_record,
500 ee->record.time_generated, ee->record.time_written,
501 ee->record.event_id, ee->record.event_type,
502 ee->record.num_strings, ee->record.event_category,
503 ee->record.reserved2,
504 ee->record.closing_record_number,
505 ee->record.string_offset,
506 ee->record.user_sid_length,
507 ee->record.user_sid_offset, ee->record.data_length,
508 ee->record.data_offset,
509 ee->data_record.source_name_len,
510 ee->data_record.source_name,
511 ee->data_record.computer_name_len,
512 ee->data_record.computer_name,
513 ee->data_record.sid_padding,
514 ee->record.user_sid_length, ee->data_record.sid,
515 ee->data_record.strings_len,
516 ee->data_record.strings,
517 ee->data_record.user_data_len,
518 ee->data_record.user_data,
519 ee->data_record.data_padding );
521 /*DEBUG(3,("write_eventlog_tdb: packed into %d\n",n_packed)); */
523 /* increment the record count */
525 kbuf.dsize = sizeof( int32 );
526 kbuf.dptr = (char * ) & next_record;
528 ebuf.dsize = n_packed;
529 ebuf.dptr = (char *)packed_ee;
531 if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
532 /* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
533 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
534 talloc_destroy( mem_ctx );
538 tdb_store_int32( the_tdb, EVT_NEXT_RECORD, next_record );
539 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
540 talloc_destroy( mem_ctx );
541 return ( next_record - 1 );
544 /*******************************************************************
545 calculate the correct fields etc for an eventlog entry
546 *******************************************************************/
548 void fixup_eventlog_entry( Eventlog_entry * ee )
550 /* fix up the eventlog entry structure as necessary */
552 ee->data_record.sid_padding =
554 ( ( ee->data_record.source_name_len +
555 ee->data_record.computer_name_len ) % 4 ) ) % 4 );
556 ee->data_record.data_padding =
558 ( ( ee->data_record.strings_len +
559 ee->data_record.user_data_len ) % 4 ) ) % 4;
560 ee->record.length = sizeof( Eventlog_record );
561 ee->record.length += ee->data_record.source_name_len;
562 ee->record.length += ee->data_record.computer_name_len;
563 if ( ee->record.user_sid_length == 0 ) {
564 /* Should not pad to a DWORD boundary for writing out the sid if there is
565 no SID, so just propagate the padding to pad the data */
566 ee->data_record.data_padding += ee->data_record.sid_padding;
567 ee->data_record.sid_padding = 0;
569 /* DEBUG(10, ("sid_padding is [%d].\n", ee->data_record.sid_padding)); */
570 /* DEBUG(10, ("data_padding is [%d].\n", ee->data_record.data_padding)); */
572 ee->record.length += ee->data_record.sid_padding;
573 ee->record.length += ee->record.user_sid_length;
574 ee->record.length += ee->data_record.strings_len;
575 ee->record.length += ee->data_record.user_data_len;
576 ee->record.length += ee->data_record.data_padding;
577 /* need another copy of length at the end of the data */
578 ee->record.length += sizeof( ee->record.length );
581 /********************************************************************
582 Note that it's a pretty good idea to initialize the Eventlog_entry
583 structure to zero's before calling parse_logentry on an batch of
584 lines that may resolve to a record. ALSO, it's a good idea to
585 remove any linefeeds (that's EOL to you and me) on the lines
587 ********************************************************************/
589 BOOL parse_logentry( char *line, Eventlog_entry * entry, BOOL * eor )
591 char *start = NULL, *stop = NULL;
597 /* empty line signyfiying record delimeter, or we're at the end of the buffer */
598 if ( start == NULL || strlen( start ) == 0 ) {
600 ( "parse_logentry: found end-of-record indicator.\n" ) );
604 if ( !( stop = strchr( line, ':' ) ) ) {
608 DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
610 if ( 0 == strncmp( start, "LEN", stop - start ) ) {
611 /* This will get recomputed later anyway -- probably not necessary */
612 entry->record.length = atoi( stop + 1 );
613 } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
614 /* For now all these reserved entries seem to have the same value,
615 which can be hardcoded to int(1699505740) for now */
616 entry->record.reserved1 = atoi( stop + 1 );
617 } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
618 entry->record.record_number = atoi( stop + 1 );
619 } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
620 entry->record.time_generated = atoi( stop + 1 );
621 } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
622 entry->record.time_written = atoi( stop + 1 );
623 } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
624 entry->record.event_id = atoi( stop + 1 );
625 } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
626 if ( strstr( start, "ERROR" ) ) {
627 entry->record.event_type = EVENTLOG_ERROR_TYPE;
628 } else if ( strstr( start, "WARNING" ) ) {
629 entry->record.event_type = EVENTLOG_WARNING_TYPE;
630 } else if ( strstr( start, "INFO" ) ) {
631 entry->record.event_type = EVENTLOG_INFORMATION_TYPE;
632 } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
633 entry->record.event_type = EVENTLOG_AUDIT_SUCCESS;
634 } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
635 entry->record.event_type = EVENTLOG_AUDIT_FAILURE;
636 } else if ( strstr( start, "SUCCESS" ) ) {
637 entry->record.event_type = EVENTLOG_SUCCESS;
639 /* some other eventlog type -- currently not defined in MSDN docs, so error out */
645 else if(0 == strncmp(start, "NST", stop - start))
647 entry->record.num_strings = atoi(stop + 1);
650 else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
651 entry->record.event_category = atoi( stop + 1 );
652 } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
653 entry->record.reserved2 = atoi( stop + 1 );
654 } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
655 entry->record.closing_record_number = atoi( stop + 1 );
656 } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
657 entry->record.user_sid_length = atoi( stop + 1 );
658 } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
659 memset( temp, 0, sizeof( temp ) );
661 while ( isspace( stop[0] ) ) {
664 temp_len = strlen( stop );
665 strncpy( temp, stop, temp_len );
666 rpcstr_push( ( void * ) ( entry->data_record.source_name ),
667 temp, sizeof( entry->data_record.source_name ),
669 entry->data_record.source_name_len =
670 ( strlen_w( entry->data_record.source_name ) * 2 ) +
672 } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
673 memset( temp, 0, sizeof( temp ) );
675 while ( isspace( stop[0] ) ) {
678 temp_len = strlen( stop );
679 strncpy( temp, stop, temp_len );
680 rpcstr_push( ( void * ) ( entry->data_record.computer_name ),
681 temp, sizeof( entry->data_record.computer_name ),
683 entry->data_record.computer_name_len =
684 ( strlen_w( entry->data_record.computer_name ) * 2 ) +
686 } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
687 memset( temp, 0, sizeof( temp ) );
689 while ( isspace( stop[0] ) ) {
692 temp_len = strlen( stop );
693 strncpy( temp, stop, temp_len );
694 rpcstr_push( ( void * ) ( entry->data_record.sid ), temp,
695 sizeof( entry->data_record.sid ),
697 entry->record.user_sid_length =
698 ( strlen_w( entry->data_record.sid ) * 2 ) + 2;
699 } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
700 /* skip past initial ":" */
702 /* now skip any other leading whitespace */
703 while ( isspace( stop[0] ) ) {
706 temp_len = strlen( stop );
707 memset( temp, 0, sizeof( temp ) );
708 strncpy( temp, stop, temp_len );
709 rpcstr_push( ( void * ) ( entry->data_record.strings +
710 ( entry->data_record.strings_len / 2 ) ),
712 sizeof( entry->data_record.strings ) -
713 ( entry->data_record.strings_len / 2 ), STR_TERMINATE );
714 entry->data_record.strings_len += ( temp_len * 2 ) + 2;
715 entry->record.num_strings++;
716 } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
717 /* skip past initial ":" */
719 /* now skip any other leading whitespace */
720 while ( isspace( stop[0] ) ) {
723 entry->data_record.user_data_len = strlen( stop );
724 memset( entry->data_record.user_data, 0,
725 sizeof( entry->data_record.user_data ) );
726 if ( entry->data_record.user_data_len > 0 ) {
727 /* copy no more than the first 1024 bytes */
728 if ( entry->data_record.user_data_len >
729 sizeof( entry->data_record.user_data ) )
730 entry->data_record.user_data_len =
731 sizeof( entry->data_record.
733 memcpy( entry->data_record.user_data, stop,
734 entry->data_record.user_data_len );
737 /* some other eventlog entry -- not implemented, so dropping on the floor */
738 DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
739 /* For now return true so that we can keep on parsing this mess. Eventually
740 we will return False here. */