3 * Unix SMB/CIFS implementation.
4 * Eventlog utility routines
5 * Copyright (C) Marcin Krzysztof Porwit 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.
26 /****************************************************************
27 Init an Eventlog TDB, and return it. If null, something bad happened.
28 ****************************************************************/
29 TDB_CONTEXT *init_eventlog_tdb( char *tdbfilename )
33 unlink( tdbfilename );
36 tdb_open_log( tdbfilename, 0, TDB_DEFAULT, O_RDWR | O_CREAT,
38 if ( the_tdb == NULL ) {
39 DEBUG( 1, ( "Can't open tdb for [%s]\n", tdbfilename ) );
42 tdb_store_int32( the_tdb, VN_oldest_entry, 1 );
43 tdb_store_int32( the_tdb, VN_next_record, 1 );
45 /* initialize with defaults, copy real values in here from registry */
47 tdb_store_int32( the_tdb, VN_maxsize, 0x80000 );
48 tdb_store_int32( the_tdb, VN_retention, 0x93A80 );
50 tdb_store_int32( the_tdb, VN_version, EVENTLOG_DATABASE_VERSION_V1 );
54 /* make the tdb file name for an event log, given destination buffer and size */
55 char *mk_tdbfilename( char *dest_buffer, char *eventlog_name, int size_dest )
61 pstrcpy( ondisk_name, "EV" );
62 pstrcat( ondisk_name, eventlog_name );
63 pstrcat( ondisk_name, ".tdb" );
65 memset( dest_buffer, 0, size_dest );
67 /* BAD things could happen if the dest_buffer is not large enough... */
68 if ( strlen( ondisk_name ) > size_dest ) {
69 DEBUG( 3, ( "Buffer not big enough for filename\n" ) );
73 strncpy( dest_buffer, ondisk_name, size_dest );
79 /* count the number of bytes in the TDB */
81 /* Arg! Static Globals! */
83 static int eventlog_tdbcount;
84 static int eventlog_tdbsize;
86 /* this function is used to count up the number of bytes in a particular TDB */
87 int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
90 eventlog_tdbsize += data.dsize;
95 /* returns the size of the eventlog, and if MaxSize is a non-null ptr, puts
96 the MaxSize there. This is purely a way not to have yet another function that solely
97 reads the maxsize of the eventlog. Yeah, that's it. */
99 int eventlog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
103 eventlog_tdbcount = 0;
104 eventlog_tdbsize = 0;
106 tdb_traverse( tdb, eventlog_tdb_size_fn, NULL );
108 if ( MaxSize != NULL ) {
109 *MaxSize = tdb_fetch_int32( tdb, VN_maxsize );
112 if ( Retention != NULL ) {
113 *Retention = tdb_fetch_int32( tdb, VN_retention );
117 ( "eventlog size: [%d] for [%d] records\n", eventlog_tdbsize,
118 eventlog_tdbcount ) );
119 return eventlog_tdbsize;
124 Discard early event logs until we have enough for 'needed' bytes...
125 NO checking done beforehand to see that we actually need to do this, and
126 it's going to pluck records one-by-one. So, it's best to determine that this
127 needs to be done before doing it.
129 Setting whack_by_date to True indicates that eventlogs falling outside of the
130 retention range need to go...
134 /* return True if we made enough room to accommodate needed bytes */
136 BOOL make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
139 int start_record, i, new_start;
141 int nbytes, reclen, len, Retention, MaxSize;
143 int tresv1, trecnum, timegen, timewr;
146 TALLOC_CTX *mem_ctx = NULL;
148 time_t current_time, exp_time;
150 /* discard some eventlogs */
152 /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
153 although records not necessarily guaranteed to have successive times */
155 mem_ctx = talloc_init( "make_way_for_eventlogs" ); /* Homage to BPG */
157 if ( mem_ctx == NULL )
158 return False; /* can't allocate memory indicates bigger problems */
160 tdb_lock_bystring( the_tdb, VN_next_record, 1 );
162 end_record = tdb_fetch_int32( the_tdb, VN_next_record );
163 start_record = tdb_fetch_int32( the_tdb, VN_oldest_entry );
164 Retention = tdb_fetch_int32( the_tdb, VN_retention );
165 MaxSize = tdb_fetch_int32( the_tdb, VN_maxsize );
167 time( ¤t_time );
170 exp_time = current_time - Retention; /* discard older than exp_time */
172 /* todo - check for sanity in next_record */
176 ( "MaxSize [%d] Retention [%d] Current Time [%d] exp_time [%d]\n",
177 MaxSize, Retention, (uint32)current_time, (uint32)exp_time ) );
179 ( "Start Record [%d] End Record [%d]\n", start_record,
182 for ( i = start_record; i < end_record; i++ ) {
183 /* read a record, add the amt to nbytes */
184 key.dsize = sizeof( int32 );
185 key.dptr = ( char * ) ( int32 * ) & i;
186 ret = tdb_fetch( the_tdb, key );
187 if ( ret.dsize == 0 ) {
189 ( "Can't find a record for the key, record [%d]\n",
191 tdb_unlock_bystring( the_tdb, VN_next_record );
194 nbytes += ret.dsize; /* note this includes overhead */
196 len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
197 &tresv1, &trecnum, &timegen, &timewr );
199 ( "read record %d, record size is [%d], total so far [%d]\n",
200 i, reclen, nbytes ) );
202 SAFE_FREE( ret.dptr );
204 /* note that other servers may just stop writing records when the size limit
205 is reached, and there are no records older than 'retention'. This doesn't
206 like a very useful thing to do, so instead we whack (as in sleeps with the
207 fishes) just enough records to fit the what we need. This behavior could
208 be changed to 'match', if the need arises. */
210 if ( !whack_by_date && ( nbytes >= needed ) )
212 if ( whack_by_date && ( timegen >= exp_time ) )
217 ( "nbytes [%d] needed [%d] start_record is [%d], should be set to [%d]\n",
218 nbytes, needed, start_record, i ) );
219 /* todo - remove eventlog entries here and set starting record to start_record... */
221 if ( start_record != new_start ) {
222 for ( i = start_record; i < new_start; i++ ) {
223 key.dsize = sizeof( int32 );
224 key.dptr = ( char * ) ( int32 * ) & i;
225 tdb_delete( the_tdb, key );
228 tdb_store_int32( the_tdb, VN_oldest_entry, new_start );
230 tdb_unlock_bystring( the_tdb, VN_next_record );
235 some hygiene for an eventlog - see how big it is, and then
236 calculate how many bytes we need to remove
239 BOOL prune_eventlog( TDB_CONTEXT * tdb )
241 int MaxSize, Retention, CalcdSize;
244 DEBUG( 4, ( "No eventlog tdb handle\n" ) );
248 CalcdSize = eventlog_tdb_size( tdb, &MaxSize, &Retention );
250 ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
253 if ( CalcdSize > MaxSize ) {
254 return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
258 return make_way_for_eventlogs( tdb, 0, True );
261 BOOL can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
264 int MaxSize, Retention;
266 /* see if we can write to the eventlog -- do a policy enforcement */
268 return False; /* tdb is null, so we can't write to it */
276 calcd_size = eventlog_tdb_size( tdb, &MaxSize, &Retention );
278 if ( calcd_size <= MaxSize )
279 return True; /* you betcha */
280 if ( calcd_size + needed < MaxSize )
283 if ( Retention == 0xffffffff ) {
284 return False; /* see msdn - we can't write no room, discard */
287 note don't have to test, but always good to show intent, in case changes needed
291 if ( Retention == 0x00000000 ) {
292 /* discard record(s) */
293 /* todo - decide when to remove a bunch vs. just what we need... */
294 return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
298 return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
301 TDB_CONTEXT *open_eventlog_tdb( char *tdbfilename )
303 TDB_CONTEXT *the_tdb;
306 tdb_open_log( tdbfilename, 0, TDB_DEFAULT, O_RDWR | O_CREAT,
308 if ( the_tdb == NULL ) {
309 return init_eventlog_tdb( tdbfilename );
311 if ( EVENTLOG_DATABASE_VERSION_V1 !=
312 tdb_fetch_int32( the_tdb, VN_version ) ) {
313 tdb_close( the_tdb );
314 return init_eventlog_tdb( tdbfilename );
319 /* write an eventlog entry. Note that we have to lock, read next eventlog, increment, write, write the record, unlock */
321 /* coming into this, ee has the eventlog record, and the auxilliary date (computer name, etc.)
322 filled into the other structure. Before packing into a record, this routine will calc the
323 appropriate padding, etc., and then blast out the record in a form that can be read back in */
324 int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
328 TALLOC_CTX *mem_ctx = NULL;
335 mem_ctx = talloc_init( "write_eventlog_tdb" );
337 if ( mem_ctx == NULL )
342 /* discard any entries that have bogus time, which usually indicates a bogus entry as well. */
343 if ( ee->record.time_generated == 0 )
348 /* todo - check for sanity in next_record */
350 fixup_eventlog_entry( ee );
352 if ( !can_write_to_eventlog( the_tdb, ee->record.length ) ) {
353 DEBUG( 3, ( "Can't write to Eventlog, no room \n" ) );
354 talloc_destroy( mem_ctx );
358 /* alloc mem for the packed version */
359 packed_ee = TALLOC( mem_ctx, ee->record.length + MARGIN );
361 talloc_destroy( mem_ctx );
365 /* need to read the record number and insert it into the entry here */
368 tdb_lock_bystring( the_tdb, VN_next_record, 1 );
370 next_record = tdb_fetch_int32( the_tdb, VN_next_record );
373 tdb_pack( packed_ee, ee->record.length + MARGIN,
374 "ddddddwwwwddddddBBdBBBd", ee->record.length,
375 ee->record.reserved1, next_record,
376 ee->record.time_generated, ee->record.time_written,
377 ee->record.event_id, ee->record.event_type,
378 ee->record.num_strings, ee->record.event_category,
379 ee->record.reserved2,
380 ee->record.closing_record_number,
381 ee->record.string_offset,
382 ee->record.user_sid_length,
383 ee->record.user_sid_offset, ee->record.data_length,
384 ee->record.data_offset,
385 ee->data_record.source_name_len,
386 ee->data_record.source_name,
387 ee->data_record.computer_name_len,
388 ee->data_record.computer_name,
389 ee->data_record.sid_padding,
390 ee->record.user_sid_length, ee->data_record.sid,
391 ee->data_record.strings_len,
392 ee->data_record.strings,
393 ee->data_record.user_data_len,
394 ee->data_record.user_data,
395 ee->data_record.data_padding );
397 /*DEBUG(3,("write_eventlog_tdb: packed into %d\n",n_packed)); */
399 /* increment the record count */
401 kbuf.dsize = sizeof( int32 );
402 kbuf.dptr = ( uint8 * ) & next_record;
404 ebuf.dsize = n_packed;
405 ebuf.dptr = packed_ee;
407 if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
408 /* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
409 tdb_unlock_bystring( the_tdb, VN_next_record );
410 talloc_destroy( mem_ctx );
414 tdb_store_int32( the_tdb, VN_next_record, next_record );
415 tdb_unlock_bystring( the_tdb, VN_next_record );
416 talloc_destroy( mem_ctx );
417 return ( next_record - 1 );
420 /* calculate the correct fields etc for an eventlog entry */
422 void fixup_eventlog_entry( Eventlog_entry * ee )
424 /* fix up the eventlog entry structure as necessary */
426 ee->data_record.sid_padding =
428 ( ( ee->data_record.source_name_len +
429 ee->data_record.computer_name_len ) % 4 ) ) % 4 );
430 ee->data_record.data_padding =
432 ( ( ee->data_record.strings_len +
433 ee->data_record.user_data_len ) % 4 ) ) % 4;
434 ee->record.length = sizeof( Eventlog_record );
435 ee->record.length += ee->data_record.source_name_len;
436 ee->record.length += ee->data_record.computer_name_len;
437 if ( ee->record.user_sid_length == 0 ) {
438 /* Should not pad to a DWORD boundary for writing out the sid if there is
439 no SID, so just propagate the padding to pad the data */
440 ee->data_record.data_padding += ee->data_record.sid_padding;
441 ee->data_record.sid_padding = 0;
443 /* DEBUG(10, ("sid_padding is [%d].\n", ee->data_record.sid_padding)); */
444 /* DEBUG(10, ("data_padding is [%d].\n", ee->data_record.data_padding)); */
446 ee->record.length += ee->data_record.sid_padding;
447 ee->record.length += ee->record.user_sid_length;
448 ee->record.length += ee->data_record.strings_len;
449 ee->record.length += ee->data_record.user_data_len;
450 ee->record.length += ee->data_record.data_padding;
451 /* need another copy of length at the end of the data */
452 ee->record.length += sizeof( ee->record.length );
455 /********************************************************************
456 Note that it's a pretty good idea to initialize the Eventlog_entry structure to zero's before
457 calling parse_logentry on an batch of lines that may resolve to a record.
458 ALSO, it's a good idea to remove any linefeeds (that's EOL to you and me) on the lines going in.
460 ********************************************************************/
462 BOOL parse_logentry( char *line, Eventlog_entry * entry, BOOL * eor )
464 char *start = NULL, *stop = NULL;
470 /* empty line signyfiying record delimeter, or we're at the end of the buffer */
471 if ( start == NULL || strlen( start ) == 0 ) {
473 ( "parse_logentry: found end-of-record indicator.\n" ) );
477 if ( !( stop = strchr( line, ':' ) ) ) {
481 DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
483 if ( 0 == strncmp( start, "LEN", stop - start ) ) {
484 /* This will get recomputed later anyway -- probably not necessary */
485 entry->record.length = atoi( stop + 1 );
486 } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
487 /* For now all these reserved entries seem to have the same value,
488 which can be hardcoded to int(1699505740) for now */
489 entry->record.reserved1 = atoi( stop + 1 );
490 } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
491 entry->record.record_number = atoi( stop + 1 );
492 } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
493 entry->record.time_generated = atoi( stop + 1 );
494 } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
495 entry->record.time_written = atoi( stop + 1 );
496 } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
497 entry->record.event_id = atoi( stop + 1 );
498 } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
499 if ( strstr( start, "ERROR" ) ) {
500 entry->record.event_type = EVENTLOG_ERROR_TYPE;
501 } else if ( strstr( start, "WARNING" ) ) {
502 entry->record.event_type = EVENTLOG_WARNING_TYPE;
503 } else if ( strstr( start, "INFO" ) ) {
504 entry->record.event_type = EVENTLOG_INFORMATION_TYPE;
505 } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
506 entry->record.event_type = EVENTLOG_AUDIT_SUCCESS;
507 } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
508 entry->record.event_type = EVENTLOG_AUDIT_FAILURE;
509 } else if ( strstr( start, "SUCCESS" ) ) {
510 entry->record.event_type = EVENTLOG_SUCCESS;
512 /* some other eventlog type -- currently not defined in MSDN docs, so error out */
518 else if(0 == strncmp(start, "NST", stop - start))
520 entry->record.num_strings = atoi(stop + 1);
523 else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
524 entry->record.event_category = atoi( stop + 1 );
525 } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
526 entry->record.reserved2 = atoi( stop + 1 );
527 } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
528 entry->record.closing_record_number = atoi( stop + 1 );
529 } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
530 entry->record.user_sid_length = atoi( stop + 1 );
531 } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
532 memset( temp, 0, sizeof( temp ) );
534 while ( isspace( stop[0] ) ) {
537 temp_len = strlen( stop );
538 strncpy( temp, stop, temp_len );
539 rpcstr_push( ( void * ) ( entry->data_record.source_name ),
540 temp, sizeof( entry->data_record.source_name ),
542 entry->data_record.source_name_len =
543 ( strlen_w( entry->data_record.source_name ) * 2 ) +
545 } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
546 memset( temp, 0, sizeof( temp ) );
548 while ( isspace( stop[0] ) ) {
551 temp_len = strlen( stop );
552 strncpy( temp, stop, temp_len );
553 rpcstr_push( ( void * ) ( entry->data_record.computer_name ),
554 temp, sizeof( entry->data_record.computer_name ),
556 entry->data_record.computer_name_len =
557 ( strlen_w( entry->data_record.computer_name ) * 2 ) +
559 } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
560 memset( temp, 0, sizeof( temp ) );
562 while ( isspace( stop[0] ) ) {
565 temp_len = strlen( stop );
566 strncpy( temp, stop, temp_len );
567 rpcstr_push( ( void * ) ( entry->data_record.sid ), temp,
568 sizeof( entry->data_record.sid ),
570 entry->record.user_sid_length =
571 ( strlen_w( entry->data_record.sid ) * 2 ) + 2;
572 } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
573 /* skip past initial ":" */
575 /* now skip any other leading whitespace */
576 while ( isspace( stop[0] ) ) {
579 temp_len = strlen( stop );
580 memset( temp, 0, sizeof( temp ) );
581 strncpy( temp, stop, temp_len );
582 rpcstr_push( ( void * ) ( entry->data_record.strings +
583 entry->data_record.strings_len ),
585 sizeof( entry->data_record.strings ) -
586 entry->data_record.strings_len, STR_TERMINATE );
587 entry->data_record.strings_len += temp_len + 1;
588 entry->record.num_strings++;
589 } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
590 /* Now that we're done processing the STR data, adjust the length to account for
591 unicode, then proceed with the DAT data. */
592 entry->data_record.strings_len *= 2;
593 /* skip past initial ":" */
595 /* now skip any other leading whitespace */
596 while ( isspace( stop[0] ) ) {
599 entry->data_record.user_data_len = strlen( stop );
600 memset( entry->data_record.user_data, 0,
601 sizeof( entry->data_record.user_data ) );
602 if ( entry->data_record.user_data_len > 0 ) {
603 /* copy no more than the first 1024 bytes */
604 if ( entry->data_record.user_data_len >
605 sizeof( entry->data_record.user_data ) )
606 entry->data_record.user_data_len =
607 sizeof( entry->data_record.
609 memcpy( entry->data_record.user_data, stop,
610 entry->data_record.user_data_len );
613 /* some other eventlog entry -- not implemented, so dropping on the floor */
614 DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
615 /* For now return true so that we can keep on parsing this mess. Eventually
616 we will return False here. */