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 3 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, see <http://www.gnu.org/licenses/>.
24 /* maintain a list of open eventlog tdbs with reference counts */
26 static ELOG_TDB *open_elog_list;
28 /********************************************************************
29 Init an Eventlog TDB, and return it. If null, something bad
31 ********************************************************************/
33 TDB_CONTEXT *elog_init_tdb( char *tdbfilename )
37 DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n",
40 tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT,
41 O_RDWR|O_CREAT|O_TRUNC, 0660 );
44 DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) );
48 /* initialize with defaults, copy real values in here from registry */
50 tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 );
51 tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 );
52 tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 );
53 tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 );
55 tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 );
60 /********************************************************************
61 make the tdb file name for an event log, given destination buffer
62 and size. Caller must free memory.
63 ********************************************************************/
65 char *elog_tdbname(TALLOC_CTX *ctx, const char *name )
67 char *path = talloc_asprintf(ctx, "%s/%s.tdb",
68 state_path("eventlog"),
78 /********************************************************************
79 this function is used to count up the number of bytes in a
81 ********************************************************************/
83 struct trav_size_struct {
88 static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
91 struct trav_size_struct *tsize = (struct trav_size_struct *)state;
93 tsize->size += data.dsize;
99 /********************************************************************
100 returns the size of the eventlog, and if MaxSize is a non-null
101 ptr, puts the MaxSize there. This is purely a way not to have yet
102 another function that solely reads the maxsize of the eventlog.
104 ********************************************************************/
106 int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
108 struct trav_size_struct tsize;
113 ZERO_STRUCT( tsize );
115 tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize );
117 if ( MaxSize != NULL ) {
118 *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE );
121 if ( Retention != NULL ) {
122 *Retention = tdb_fetch_int32( tdb, EVT_RETENTION );
126 ( "eventlog size: [%d] for [%d] records\n", tsize.size,
131 /********************************************************************
132 Discard early event logs until we have enough for 'needed' bytes...
133 NO checking done beforehand to see that we actually need to do
134 this, and it's going to pluck records one-by-one. So, it's best
135 to determine that this needs to be done before doing it.
137 Setting whack_by_date to True indicates that eventlogs falling
138 outside of the retention range need to go...
140 return True if we made enough room to accommodate needed bytes
141 ********************************************************************/
143 bool make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
146 int start_record, i, new_start;
148 int nbytes, reclen, len, Retention, MaxSize;
149 int tresv1, trecnum, timegen, timewr;
151 TALLOC_CTX *mem_ctx = NULL;
152 time_t current_time, exp_time;
154 /* discard some eventlogs */
156 /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
157 although records not necessarily guaranteed to have successive times */
159 mem_ctx = talloc_init( "make_way_for_eventlogs" ); /* Homage to BPG */
161 if ( mem_ctx == NULL )
162 return False; /* can't allocate memory indicates bigger problems */
164 tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
166 end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
167 start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
168 Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
169 MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );
171 time( ¤t_time );
174 exp_time = current_time - Retention; /* discard older than exp_time */
176 /* todo - check for sanity in next_record */
180 ( "MaxSize [%d] Retention [%d] Current Time [%d] exp_time [%d]\n",
181 MaxSize, Retention, (uint32)current_time, (uint32)exp_time ) );
183 ( "Start Record [%d] End Record [%d]\n", start_record,
186 for ( i = start_record; i < end_record; i++ ) {
187 /* read a record, add the amt to nbytes */
188 key.dsize = sizeof( int32 );
189 key.dptr = ( uint8 * ) ( int32 * ) & i;
190 ret = tdb_fetch( the_tdb, key );
191 if ( ret.dsize == 0 ) {
193 ( "Can't find a record for the key, record [%d]\n",
195 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
198 nbytes += ret.dsize; /* note this includes overhead */
200 len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
201 &tresv1, &trecnum, &timegen, &timewr );
203 DEBUG( 10,("make_way_for_eventlogs: tdb_unpack failed.\n"));
204 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
209 ( "read record %d, record size is [%d], total so far [%d]\n",
210 i, reclen, nbytes ) );
212 SAFE_FREE( ret.dptr );
214 /* note that other servers may just stop writing records when the size limit
215 is reached, and there are no records older than 'retention'. This doesn't
216 like a very useful thing to do, so instead we whack (as in sleeps with the
217 fishes) just enough records to fit the what we need. This behavior could
218 be changed to 'match', if the need arises. */
220 if ( !whack_by_date && ( nbytes >= needed ) )
222 if ( whack_by_date && ( timegen >= exp_time ) )
227 ( "nbytes [%d] needed [%d] start_record is [%d], should be set to [%d]\n",
228 nbytes, needed, start_record, i ) );
229 /* todo - remove eventlog entries here and set starting record to start_record... */
231 if ( start_record != new_start ) {
232 for ( i = start_record; i < new_start; i++ ) {
233 key.dsize = sizeof( int32 );
234 key.dptr = ( uint8 * ) ( int32 * ) & i;
235 tdb_delete( the_tdb, key );
238 tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
240 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
244 /********************************************************************
245 some hygiene for an eventlog - see how big it is, and then
246 calculate how many bytes we need to remove
247 ********************************************************************/
249 bool prune_eventlog( TDB_CONTEXT * tdb )
251 int MaxSize, Retention, CalcdSize;
254 DEBUG( 4, ( "No eventlog tdb handle\n" ) );
258 CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention );
260 ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
263 if ( CalcdSize > MaxSize ) {
264 return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
268 return make_way_for_eventlogs( tdb, 0, True );
271 /********************************************************************
272 ********************************************************************/
274 bool can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
277 int MaxSize, Retention;
279 /* see if we can write to the eventlog -- do a policy enforcement */
281 return False; /* tdb is null, so we can't write to it */
289 calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention );
291 if ( calcd_size <= MaxSize )
292 return True; /* you betcha */
293 if ( calcd_size + needed < MaxSize )
296 if ( Retention == 0xffffffff ) {
297 return False; /* see msdn - we can't write no room, discard */
300 note don't have to test, but always good to show intent, in case changes needed
304 if ( Retention == 0x00000000 ) {
305 /* discard record(s) */
306 /* todo - decide when to remove a bunch vs. just what we need... */
307 return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
311 return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
314 /*******************************************************************
315 *******************************************************************/
317 ELOG_TDB *elog_open_tdb( char *logname, bool force_clear )
319 TDB_CONTEXT *tdb = NULL;
322 char *tdbpath = NULL;
323 ELOG_TDB *tdb_node = NULL;
325 TALLOC_CTX *ctx = talloc_tos();
327 /* first see if we have an open context */
329 for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
330 if ( strequal( ptr->name, logname ) ) {
333 /* trick to alow clearing of the eventlog tdb.
334 The force_clear flag should imply that someone
335 has done a force close. So make sure the tdb
336 is NULL. If this is a normal open, then just
337 return the existing reference */
340 SMB_ASSERT( ptr->tdb == NULL );
348 /* make sure that the eventlog dir exists */
350 eventlogdir = state_path( "eventlog" );
351 if ( !directory_exist( eventlogdir, NULL ) )
352 mkdir( eventlogdir, 0755 );
354 /* get the path on disk */
356 tdbpath = elog_tdbname(ctx, logname);
361 DEBUG(7,("elog_open_tdb: Opening %s...(force_clear == %s)\n",
362 tdbpath, force_clear?"True":"False" ));
364 /* the tdb wasn't already open or this is a forced clear open */
366 if ( !force_clear ) {
368 tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, O_RDWR , 0 );
370 vers_id = tdb_fetch_int32( tdb, EVT_VERSION );
372 if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) {
373 DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n",
376 tdb = elog_init_tdb( tdbpath );
382 tdb = elog_init_tdb( tdbpath );
384 /* if we got a valid context, then add it to the list */
387 /* on a forced clear, just reset the tdb context if we already
388 have an open entry in the list */
395 if ( !(tdb_node = TALLOC_ZERO_P( NULL, ELOG_TDB)) ) {
396 DEBUG(0,("elog_open_tdb: talloc() failure!\n"));
401 tdb_node->name = talloc_strdup( tdb_node, logname );
403 tdb_node->ref_count = 1;
405 DLIST_ADD( open_elog_list, tdb_node );
411 /*******************************************************************
412 Wrapper to handle reference counts to the tdb
413 *******************************************************************/
415 int elog_close_tdb( ELOG_TDB *etdb, bool force_close )
424 SMB_ASSERT( etdb->ref_count >= 0 );
426 if ( etdb->ref_count == 0 ) {
428 DLIST_REMOVE( open_elog_list, etdb );
430 return tdb_close( tdb );
436 return tdb_close( tdb );
443 /*******************************************************************
444 write an eventlog entry. Note that we have to lock, read next
445 eventlog, increment, write, write the record, unlock
447 coming into this, ee has the eventlog record, and the auxilliary date
448 (computer name, etc.) filled into the other structure. Before packing
449 into a record, this routine will calc the appropriate padding, etc.,
450 and then blast out the record in a form that can be read back in
451 *******************************************************************/
455 int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
459 TALLOC_CTX *mem_ctx = NULL;
466 mem_ctx = talloc_init( "write_eventlog_tdb" );
468 if ( mem_ctx == NULL )
473 /* discard any entries that have bogus time, which usually indicates a bogus entry as well. */
474 if ( ee->record.time_generated == 0 )
477 /* todo - check for sanity in next_record */
479 fixup_eventlog_entry( ee );
481 if ( !can_write_to_eventlog( the_tdb, ee->record.length ) ) {
482 DEBUG( 3, ( "Can't write to Eventlog, no room \n" ) );
483 talloc_destroy( mem_ctx );
487 /* alloc mem for the packed version */
488 packed_ee = (uint8 *)TALLOC( mem_ctx, ee->record.length + MARGIN );
490 talloc_destroy( mem_ctx );
494 /* need to read the record number and insert it into the entry here */
497 tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
499 next_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
502 tdb_pack( (uint8 *)packed_ee, ee->record.length + MARGIN,
503 "ddddddwwwwddddddBBdBBBd", ee->record.length,
504 ee->record.reserved1, next_record,
505 ee->record.time_generated, ee->record.time_written,
506 ee->record.event_id, ee->record.event_type,
507 ee->record.num_strings, ee->record.event_category,
508 ee->record.reserved2,
509 ee->record.closing_record_number,
510 ee->record.string_offset,
511 ee->record.user_sid_length,
512 ee->record.user_sid_offset, ee->record.data_length,
513 ee->record.data_offset,
514 ee->data_record.source_name_len,
515 ee->data_record.source_name,
516 ee->data_record.computer_name_len,
517 ee->data_record.computer_name,
518 ee->data_record.sid_padding,
519 ee->record.user_sid_length, ee->data_record.sid,
520 ee->data_record.strings_len,
521 ee->data_record.strings,
522 ee->data_record.user_data_len,
523 ee->data_record.user_data,
524 ee->data_record.data_padding );
526 /*DEBUG(3,("write_eventlog_tdb: packed into %d\n",n_packed)); */
528 /* increment the record count */
530 kbuf.dsize = sizeof( int32 );
531 kbuf.dptr = (uint8 * ) & next_record;
533 ebuf.dsize = n_packed;
534 ebuf.dptr = (uint8 *)packed_ee;
536 if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
537 /* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
538 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
539 talloc_destroy( mem_ctx );
543 tdb_store_int32( the_tdb, EVT_NEXT_RECORD, next_record );
544 tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
545 talloc_destroy( mem_ctx );
546 return ( next_record - 1 );
549 /*******************************************************************
550 calculate the correct fields etc for an eventlog entry
551 *******************************************************************/
553 void fixup_eventlog_entry( Eventlog_entry * ee )
555 /* fix up the eventlog entry structure as necessary */
557 ee->data_record.sid_padding =
559 ( ( ee->data_record.source_name_len +
560 ee->data_record.computer_name_len ) % 4 ) ) % 4 );
561 ee->data_record.data_padding =
563 ( ( ee->data_record.strings_len +
564 ee->data_record.user_data_len ) % 4 ) ) % 4;
565 ee->record.length = sizeof( Eventlog_record );
566 ee->record.length += ee->data_record.source_name_len;
567 ee->record.length += ee->data_record.computer_name_len;
568 if ( ee->record.user_sid_length == 0 ) {
569 /* Should not pad to a DWORD boundary for writing out the sid if there is
570 no SID, so just propagate the padding to pad the data */
571 ee->data_record.data_padding += ee->data_record.sid_padding;
572 ee->data_record.sid_padding = 0;
574 /* DEBUG(10, ("sid_padding is [%d].\n", ee->data_record.sid_padding)); */
575 /* DEBUG(10, ("data_padding is [%d].\n", ee->data_record.data_padding)); */
577 ee->record.length += ee->data_record.sid_padding;
578 ee->record.length += ee->record.user_sid_length;
579 ee->record.length += ee->data_record.strings_len;
580 ee->record.length += ee->data_record.user_data_len;
581 ee->record.length += ee->data_record.data_padding;
582 /* need another copy of length at the end of the data */
583 ee->record.length += sizeof( ee->record.length );
586 /********************************************************************
587 Note that it's a pretty good idea to initialize the Eventlog_entry
588 structure to zero's before calling parse_logentry on an batch of
589 lines that may resolve to a record. ALSO, it's a good idea to
590 remove any linefeeds (that's EOL to you and me) on the lines
592 ********************************************************************/
594 bool parse_logentry( char *line, Eventlog_entry * entry, bool * eor )
596 TALLOC_CTX *ctx = talloc_tos();
597 char *start = NULL, *stop = NULL;
601 /* empty line signyfiying record delimeter, or we're at the end of the buffer */
602 if ( start == NULL || strlen( start ) == 0 ) {
604 ( "parse_logentry: found end-of-record indicator.\n" ) );
608 if ( !( stop = strchr( line, ':' ) ) ) {
612 DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
614 if ( 0 == strncmp( start, "LEN", stop - start ) ) {
615 /* This will get recomputed later anyway -- probably not necessary */
616 entry->record.length = atoi( stop + 1 );
617 } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
618 /* For now all these reserved entries seem to have the same value,
619 which can be hardcoded to int(1699505740) for now */
620 entry->record.reserved1 = atoi( stop + 1 );
621 } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
622 entry->record.record_number = atoi( stop + 1 );
623 } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
624 entry->record.time_generated = atoi( stop + 1 );
625 } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
626 entry->record.time_written = atoi( stop + 1 );
627 } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
628 entry->record.event_id = atoi( stop + 1 );
629 } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
630 if ( strstr( start, "ERROR" ) ) {
631 entry->record.event_type = EVENTLOG_ERROR_TYPE;
632 } else if ( strstr( start, "WARNING" ) ) {
633 entry->record.event_type = EVENTLOG_WARNING_TYPE;
634 } else if ( strstr( start, "INFO" ) ) {
635 entry->record.event_type = EVENTLOG_INFORMATION_TYPE;
636 } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
637 entry->record.event_type = EVENTLOG_AUDIT_SUCCESS;
638 } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
639 entry->record.event_type = EVENTLOG_AUDIT_FAILURE;
640 } else if ( strstr( start, "SUCCESS" ) ) {
641 entry->record.event_type = EVENTLOG_SUCCESS;
643 /* some other eventlog type -- currently not defined in MSDN docs, so error out */
649 else if(0 == strncmp(start, "NST", stop - start))
651 entry->record.num_strings = atoi(stop + 1);
654 else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
655 entry->record.event_category = atoi( stop + 1 );
656 } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
657 entry->record.reserved2 = atoi( stop + 1 );
658 } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
659 entry->record.closing_record_number = atoi( stop + 1 );
660 } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
661 entry->record.user_sid_length = atoi( stop + 1 );
662 } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
664 while ( isspace( stop[0] ) ) {
667 entry->data_record.source_name_len = rpcstr_push_talloc(ctx,
668 &entry->data_record.source_name,
670 if (entry->data_record.source_name_len == (uint32_t)-1 ||
671 entry->data_record.source_name == NULL) {
674 } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
676 while ( isspace( stop[0] ) ) {
679 entry->data_record.computer_name_len = rpcstr_push_talloc(ctx,
680 &entry->data_record.computer_name,
682 if (entry->data_record.computer_name_len == (uint32_t)-1 ||
683 entry->data_record.computer_name == NULL) {
686 } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
688 while ( isspace( stop[0] ) ) {
691 entry->record.user_sid_length = rpcstr_push_talloc(ctx,
692 &entry->data_record.sid,
694 if (entry->record.user_sid_length == (uint32_t)-1 ||
695 entry->data_record.sid == NULL) {
698 } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
699 smb_ucs2_t *temp = NULL;
702 /* skip past initial ":" */
704 /* now skip any other leading whitespace */
705 while ( isspace(stop[0])) {
708 tmp_len = rpcstr_push_talloc(ctx,
711 if (tmp_len == (size_t)-1 || !temp) {
714 old_len = entry->data_record.strings_len;
715 entry->data_record.strings = (smb_ucs2_t *)TALLOC_REALLOC_ARRAY(ctx,
716 entry->data_record.strings,
719 if (!entry->data_record.strings) {
722 memcpy(entry->data_record.strings + old_len,
725 entry->data_record.strings_len += tmp_len;
726 entry->record.num_strings++;
727 } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
728 /* skip past initial ":" */
730 /* now skip any other leading whitespace */
731 while ( isspace( stop[0] ) ) {
734 entry->data_record.user_data_len = strlen(stop);
735 entry->data_record.user_data = talloc_strdup(ctx,
737 if (!entry->data_record.user_data) {
741 /* some other eventlog entry -- not implemented, so dropping on the floor */
742 DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
743 /* For now return true so that we can keep on parsing this mess. Eventually
744 we will return False here. */