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 struct elog_open_tdb {
28 struct elog_open_tdb *prev, *next;
34 static struct elog_open_tdb *open_elog_list;
36 /********************************************************************
37 Init an Eventlog TDB, and return it. If null, something bad
39 ********************************************************************/
41 TDB_CONTEXT *elog_init_tdb( char *tdbfilename )
45 DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n",
48 tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT,
49 O_RDWR|O_CREAT|O_TRUNC, 0600 );
52 DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) );
56 /* initialize with defaults, copy real values in here from registry */
58 tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 );
59 tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 );
60 tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 );
61 tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 );
63 tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 );
68 /********************************************************************
69 make the tdb file name for an event log, given destination buffer
70 and size. Caller must free memory.
71 ********************************************************************/
73 char *elog_tdbname( const char *name )
77 char *eventlogdir = lock_path( "eventlog" );
79 pstr_sprintf( path, "%s/%s.tdb", eventlogdir, name );
81 tdb_fullpath = SMB_STRDUP( path );
87 /********************************************************************
88 this function is used to count up the number of bytes in a
90 ********************************************************************/
92 struct trav_size_struct {
97 static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
100 struct trav_size_struct *tsize = state;
102 tsize->size += data.dsize;
108 /********************************************************************
109 returns the size of the eventlog, and if MaxSize is a non-null
110 ptr, puts the MaxSize there. This is purely a way not to have yet
111 another function that solely reads the maxsize of the eventlog.
113 ********************************************************************/
115 int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
117 struct trav_size_struct tsize;
122 ZERO_STRUCT( tsize );
124 tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize );
126 if ( MaxSize != NULL ) {
127 *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE );
130 if ( Retention != NULL ) {
131 *Retention = tdb_fetch_int32( tdb, EVT_RETENTION );
135 ( "eventlog size: [%d] for [%d] records\n", tsize.size,
140 /********************************************************************
141 Discard early event logs until we have enough for 'needed' bytes...
142 NO checking done beforehand to see that we actually need to do
143 this, and it's going to pluck records one-by-one. So, it's best
144 to determine that this needs to be done before doing it.
146 Setting whack_by_date to True indicates that eventlogs falling
147 outside of the retention range need to go...
149 return True if we made enough room to accommodate needed bytes
150 ********************************************************************/
152 BOOL make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
155 int start_record, i, new_start;
157 int nbytes, reclen, len, Retention, MaxSize;
158 int tresv1, trecnum, timegen, timewr;
160 TALLOC_CTX *mem_ctx = NULL;
161 time_t current_time, exp_time;
163 /* discard some eventlogs */
165 /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
166 although records not necessarily guaranteed to have successive times */
168 mem_ctx = talloc_init( "make_way_for_eventlogs" ); /* Homage to BPG */
170 if ( mem_ctx == NULL )
171 return False; /* can't allocate memory indicates bigger problems */
173 tdb_lock_bystring( the_tdb, EVT_NEXT_RECORD, 1 );
175 end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
176 start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
177 Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
178 MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );
180 time( ¤t_time );
183 exp_time = current_time - Retention; /* discard older than exp_time */
185 /* todo - check for sanity in next_record */
189 ( "MaxSize [%d] Retention [%d] Current Time [%d] exp_time [%d]\n",
190 MaxSize, Retention, (uint32)current_time, (uint32)exp_time ) );
192 ( "Start Record [%d] End Record [%d]\n", start_record,
195 for ( i = start_record; i < end_record; i++ ) {
196 /* read a record, add the amt to nbytes */
197 key.dsize = sizeof( int32 );
198 key.dptr = ( char * ) ( int32 * ) & i;
199 ret = tdb_fetch( the_tdb, key );
200 if ( ret.dsize == 0 ) {
202 ( "Can't find a record for the key, record [%d]\n",
204 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
207 nbytes += ret.dsize; /* note this includes overhead */
209 len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
210 &tresv1, &trecnum, &timegen, &timewr );
212 ( "read record %d, record size is [%d], total so far [%d]\n",
213 i, reclen, nbytes ) );
215 SAFE_FREE( ret.dptr );
217 /* note that other servers may just stop writing records when the size limit
218 is reached, and there are no records older than 'retention'. This doesn't
219 like a very useful thing to do, so instead we whack (as in sleeps with the
220 fishes) just enough records to fit the what we need. This behavior could
221 be changed to 'match', if the need arises. */
223 if ( !whack_by_date && ( nbytes >= needed ) )
225 if ( whack_by_date && ( timegen >= exp_time ) )
230 ( "nbytes [%d] needed [%d] start_record is [%d], should be set to [%d]\n",
231 nbytes, needed, start_record, i ) );
232 /* todo - remove eventlog entries here and set starting record to start_record... */
234 if ( start_record != new_start ) {
235 for ( i = start_record; i < new_start; i++ ) {
236 key.dsize = sizeof( int32 );
237 key.dptr = ( char * ) ( int32 * ) & i;
238 tdb_delete( the_tdb, key );
241 tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
243 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
247 /********************************************************************
248 some hygiene for an eventlog - see how big it is, and then
249 calculate how many bytes we need to remove
250 ********************************************************************/
252 BOOL prune_eventlog( TDB_CONTEXT * tdb )
254 int MaxSize, Retention, CalcdSize;
257 DEBUG( 4, ( "No eventlog tdb handle\n" ) );
261 CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention );
263 ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
266 if ( CalcdSize > MaxSize ) {
267 return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
271 return make_way_for_eventlogs( tdb, 0, True );
274 /********************************************************************
275 ********************************************************************/
277 BOOL can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
280 int MaxSize, Retention;
282 /* see if we can write to the eventlog -- do a policy enforcement */
284 return False; /* tdb is null, so we can't write to it */
292 calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention );
294 if ( calcd_size <= MaxSize )
295 return True; /* you betcha */
296 if ( calcd_size + needed < MaxSize )
299 if ( Retention == 0xffffffff ) {
300 return False; /* see msdn - we can't write no room, discard */
303 note don't have to test, but always good to show intent, in case changes needed
307 if ( Retention == 0x00000000 ) {
308 /* discard record(s) */
309 /* todo - decide when to remove a bunch vs. just what we need... */
310 return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
314 return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
317 /*******************************************************************
318 *******************************************************************/
320 TDB_CONTEXT *elog_open_tdb( char *logname )
324 struct elog_open_tdb *ptr;
327 struct elog_open_tdb *tdb_node;
330 /* first see if we have an open context */
332 for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
333 if ( strequal( ptr->name, logname ) ) {
339 /* make sure that the eventlog dir exists */
341 eventlogdir = lock_path( "eventlog" );
342 if ( !directory_exist( eventlogdir, NULL ) )
343 mkdir( eventlogdir, 0755 );
345 /* get the path on disk */
347 tdbfilename = elog_tdbname( logname );
348 pstrcpy( tdbpath, tdbfilename );
349 SAFE_FREE( tdbfilename );
351 DEBUG(7,("elog_open_tdb: Opening %s...\n", tdbpath ));
353 tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, O_RDWR , 0 );
355 vers_id = tdb_fetch_int32( tdb, EVT_VERSION );
357 if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) {
358 DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n",
361 tdb = elog_init_tdb( tdbpath );
365 tdb = elog_init_tdb( tdbpath );
368 /* if we got a valid context, then add it to the list */
371 if ( !(tdb_node = TALLOC_ZERO_P( NULL, struct elog_open_tdb )) ) {
372 DEBUG(0,("elog_open_tdb: talloc() failure!\n"));
377 tdb_node->name = talloc_strdup( tdb_node, logname );
379 tdb_node->ref_count = 1;
381 DLIST_ADD( open_elog_list, tdb_node );
387 /*******************************************************************
388 Wrapper to handle reference counts to the tdb
389 *******************************************************************/
391 int elog_close_tdb( TDB_CONTEXT *tdb )
393 struct elog_open_tdb *ptr;
398 /* See if we can just decrement the ref_count.
399 Just compare pointer values (not names ) */
401 for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
402 if ( tdb == ptr->tdb ) {
408 /* if we have a NULL pointer; it means we are trying to
409 close a tdb not in the list of open eventlogs */
411 SMB_ASSERT( ptr != NULL );
413 return tdb_close( tdb );
415 SMB_ASSERT( ptr->ref_count >= 0 );
417 if ( ptr->ref_count == 0 ) {
418 DLIST_REMOVE( open_elog_list, ptr );
420 return tdb_close( tdb );
427 /*******************************************************************
428 write an eventlog entry. Note that we have to lock, read next
429 eventlog, increment, write, write the record, unlock
431 coming into this, ee has the eventlog record, and the auxilliary date
432 (computer name, etc.) filled into the other structure. Before packing
433 into a record, this routine will calc the appropriate padding, etc.,
434 and then blast out the record in a form that can be read back in
435 *******************************************************************/
439 int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
443 TALLOC_CTX *mem_ctx = NULL;
450 mem_ctx = talloc_init( "write_eventlog_tdb" );
452 if ( mem_ctx == NULL )
457 /* discard any entries that have bogus time, which usually indicates a bogus entry as well. */
458 if ( ee->record.time_generated == 0 )
461 /* todo - check for sanity in next_record */
463 fixup_eventlog_entry( ee );
465 if ( !can_write_to_eventlog( the_tdb, ee->record.length ) ) {
466 DEBUG( 3, ( "Can't write to Eventlog, no room \n" ) );
467 talloc_destroy( mem_ctx );
471 /* alloc mem for the packed version */
472 packed_ee = TALLOC( mem_ctx, ee->record.length + MARGIN );
474 talloc_destroy( mem_ctx );
478 /* need to read the record number and insert it into the entry here */
481 tdb_lock_bystring( the_tdb, EVT_NEXT_RECORD, 1 );
483 next_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
486 tdb_pack( (char *)packed_ee, ee->record.length + MARGIN,
487 "ddddddwwwwddddddBBdBBBd", ee->record.length,
488 ee->record.reserved1, next_record,
489 ee->record.time_generated, ee->record.time_written,
490 ee->record.event_id, ee->record.event_type,
491 ee->record.num_strings, ee->record.event_category,
492 ee->record.reserved2,
493 ee->record.closing_record_number,
494 ee->record.string_offset,
495 ee->record.user_sid_length,
496 ee->record.user_sid_offset, ee->record.data_length,
497 ee->record.data_offset,
498 ee->data_record.source_name_len,
499 ee->data_record.source_name,
500 ee->data_record.computer_name_len,
501 ee->data_record.computer_name,
502 ee->data_record.sid_padding,
503 ee->record.user_sid_length, ee->data_record.sid,
504 ee->data_record.strings_len,
505 ee->data_record.strings,
506 ee->data_record.user_data_len,
507 ee->data_record.user_data,
508 ee->data_record.data_padding );
510 /*DEBUG(3,("write_eventlog_tdb: packed into %d\n",n_packed)); */
512 /* increment the record count */
514 kbuf.dsize = sizeof( int32 );
515 kbuf.dptr = (char * ) & next_record;
517 ebuf.dsize = n_packed;
518 ebuf.dptr = (char *)packed_ee;
520 if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
521 /* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
522 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
523 talloc_destroy( mem_ctx );
527 tdb_store_int32( the_tdb, EVT_NEXT_RECORD, next_record );
528 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
529 talloc_destroy( mem_ctx );
530 return ( next_record - 1 );
533 /*******************************************************************
534 calculate the correct fields etc for an eventlog entry
535 *******************************************************************/
537 void fixup_eventlog_entry( Eventlog_entry * ee )
539 /* fix up the eventlog entry structure as necessary */
541 ee->data_record.sid_padding =
543 ( ( ee->data_record.source_name_len +
544 ee->data_record.computer_name_len ) % 4 ) ) % 4 );
545 ee->data_record.data_padding =
547 ( ( ee->data_record.strings_len +
548 ee->data_record.user_data_len ) % 4 ) ) % 4;
549 ee->record.length = sizeof( Eventlog_record );
550 ee->record.length += ee->data_record.source_name_len;
551 ee->record.length += ee->data_record.computer_name_len;
552 if ( ee->record.user_sid_length == 0 ) {
553 /* Should not pad to a DWORD boundary for writing out the sid if there is
554 no SID, so just propagate the padding to pad the data */
555 ee->data_record.data_padding += ee->data_record.sid_padding;
556 ee->data_record.sid_padding = 0;
558 /* DEBUG(10, ("sid_padding is [%d].\n", ee->data_record.sid_padding)); */
559 /* DEBUG(10, ("data_padding is [%d].\n", ee->data_record.data_padding)); */
561 ee->record.length += ee->data_record.sid_padding;
562 ee->record.length += ee->record.user_sid_length;
563 ee->record.length += ee->data_record.strings_len;
564 ee->record.length += ee->data_record.user_data_len;
565 ee->record.length += ee->data_record.data_padding;
566 /* need another copy of length at the end of the data */
567 ee->record.length += sizeof( ee->record.length );
570 /********************************************************************
571 Note that it's a pretty good idea to initialize the Eventlog_entry
572 structure to zero's before calling parse_logentry on an batch of
573 lines that may resolve to a record. ALSO, it's a good idea to
574 remove any linefeeds (that's EOL to you and me) on the lines
576 ********************************************************************/
578 BOOL parse_logentry( char *line, Eventlog_entry * entry, BOOL * eor )
580 char *start = NULL, *stop = NULL;
586 /* empty line signyfiying record delimeter, or we're at the end of the buffer */
587 if ( start == NULL || strlen( start ) == 0 ) {
589 ( "parse_logentry: found end-of-record indicator.\n" ) );
593 if ( !( stop = strchr( line, ':' ) ) ) {
597 DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
599 if ( 0 == strncmp( start, "LEN", stop - start ) ) {
600 /* This will get recomputed later anyway -- probably not necessary */
601 entry->record.length = atoi( stop + 1 );
602 } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
603 /* For now all these reserved entries seem to have the same value,
604 which can be hardcoded to int(1699505740) for now */
605 entry->record.reserved1 = atoi( stop + 1 );
606 } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
607 entry->record.record_number = atoi( stop + 1 );
608 } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
609 entry->record.time_generated = atoi( stop + 1 );
610 } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
611 entry->record.time_written = atoi( stop + 1 );
612 } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
613 entry->record.event_id = atoi( stop + 1 );
614 } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
615 if ( strstr( start, "ERROR" ) ) {
616 entry->record.event_type = EVENTLOG_ERROR_TYPE;
617 } else if ( strstr( start, "WARNING" ) ) {
618 entry->record.event_type = EVENTLOG_WARNING_TYPE;
619 } else if ( strstr( start, "INFO" ) ) {
620 entry->record.event_type = EVENTLOG_INFORMATION_TYPE;
621 } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
622 entry->record.event_type = EVENTLOG_AUDIT_SUCCESS;
623 } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
624 entry->record.event_type = EVENTLOG_AUDIT_FAILURE;
625 } else if ( strstr( start, "SUCCESS" ) ) {
626 entry->record.event_type = EVENTLOG_SUCCESS;
628 /* some other eventlog type -- currently not defined in MSDN docs, so error out */
634 else if(0 == strncmp(start, "NST", stop - start))
636 entry->record.num_strings = atoi(stop + 1);
639 else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
640 entry->record.event_category = atoi( stop + 1 );
641 } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
642 entry->record.reserved2 = atoi( stop + 1 );
643 } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
644 entry->record.closing_record_number = atoi( stop + 1 );
645 } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
646 entry->record.user_sid_length = atoi( stop + 1 );
647 } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
648 memset( temp, 0, sizeof( temp ) );
650 while ( isspace( stop[0] ) ) {
653 temp_len = strlen( stop );
654 strncpy( temp, stop, temp_len );
655 rpcstr_push( ( void * ) ( entry->data_record.source_name ),
656 temp, sizeof( entry->data_record.source_name ),
658 entry->data_record.source_name_len =
659 ( strlen_w( entry->data_record.source_name ) * 2 ) +
661 } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
662 memset( temp, 0, sizeof( temp ) );
664 while ( isspace( stop[0] ) ) {
667 temp_len = strlen( stop );
668 strncpy( temp, stop, temp_len );
669 rpcstr_push( ( void * ) ( entry->data_record.computer_name ),
670 temp, sizeof( entry->data_record.computer_name ),
672 entry->data_record.computer_name_len =
673 ( strlen_w( entry->data_record.computer_name ) * 2 ) +
675 } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
676 memset( temp, 0, sizeof( temp ) );
678 while ( isspace( stop[0] ) ) {
681 temp_len = strlen( stop );
682 strncpy( temp, stop, temp_len );
683 rpcstr_push( ( void * ) ( entry->data_record.sid ), temp,
684 sizeof( entry->data_record.sid ),
686 entry->record.user_sid_length =
687 ( strlen_w( entry->data_record.sid ) * 2 ) + 2;
688 } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
689 /* skip past initial ":" */
691 /* now skip any other leading whitespace */
692 while ( isspace( stop[0] ) ) {
695 temp_len = strlen( stop );
696 memset( temp, 0, sizeof( temp ) );
697 strncpy( temp, stop, temp_len );
698 rpcstr_push( ( void * ) ( entry->data_record.strings +
699 entry->data_record.strings_len ),
701 sizeof( entry->data_record.strings ) -
702 entry->data_record.strings_len, STR_TERMINATE );
703 entry->data_record.strings_len += temp_len + 1;
704 entry->record.num_strings++;
705 } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
706 /* Now that we're done processing the STR data, adjust the length to account for
707 unicode, then proceed with the DAT data. */
708 entry->data_record.strings_len *= 2;
709 /* skip past initial ":" */
711 /* now skip any other leading whitespace */
712 while ( isspace( stop[0] ) ) {
715 entry->data_record.user_data_len = strlen( stop );
716 memset( entry->data_record.user_data, 0,
717 sizeof( entry->data_record.user_data ) );
718 if ( entry->data_record.user_data_len > 0 ) {
719 /* copy no more than the first 1024 bytes */
720 if ( entry->data_record.user_data_len >
721 sizeof( entry->data_record.user_data ) )
722 entry->data_record.user_data_len =
723 sizeof( entry->data_record.
725 memcpy( entry->data_record.user_data, stop,
726 entry->data_record.user_data_len );
729 /* some other eventlog entry -- not implemented, so dropping on the floor */
730 DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
731 /* For now return true so that we can keep on parsing this mess. Eventually
732 we will return False here. */