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 )
62 pstrcpy( ondisk_name, "EV" );
63 pstrcat( ondisk_name, eventlog_name );
64 pstrcat( ondisk_name, ".tdb" );
66 memset( dest_buffer, 0, size_dest );
68 /* BAD things could happen if the dest_buffer is not large enough... */
69 if ( strlen( ondisk_name ) > size_dest ) {
70 DEBUG( 3, ( "Buffer not big enough for filename\n" ) );
74 strncpy( dest_buffer, ondisk_name, size_dest );
80 /* count the number of bytes in the TDB */
82 /* Arg! Static Globals! */
84 static int eventlog_tdbcount;
85 static int eventlog_tdbsize;
87 /* this function is used to count up the number of bytes in a particular TDB */
88 int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
91 eventlog_tdbsize += data.dsize;
96 /* returns the size of the eventlog, and if MaxSize is a non-null ptr, puts
97 the MaxSize there. This is purely a way not to have yet another function that solely
98 reads the maxsize of the eventlog. Yeah, that's it. */
100 int eventlog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
104 eventlog_tdbcount = 0;
105 eventlog_tdbsize = 0;
107 tdb_traverse( tdb, eventlog_tdb_size_fn, NULL );
109 if ( MaxSize != NULL ) {
110 *MaxSize = tdb_fetch_int32( tdb, VN_maxsize );
113 if ( Retention != NULL ) {
114 *Retention = tdb_fetch_int32( tdb, VN_retention );
118 ( "eventlog size: [%d] for [%d] records\n", eventlog_tdbsize,
119 eventlog_tdbcount ) );
120 return eventlog_tdbsize;
125 Discard early event logs until we have enough for 'needed' bytes...
126 NO checking done beforehand to see that we actually need to do this, and
127 it's going to pluck records one-by-one. So, it's best to determine that this
128 needs to be done before doing it.
130 Setting whack_by_date to True indicates that eventlogs falling outside of the
131 retention range need to go...
135 /* return True if we made enough room to accommodate needed bytes */
137 BOOL make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32 needed,
140 int start_record, i, new_start;
142 int nbytes, reclen, len, Retention, MaxSize;
144 int tresv1, trecnum, timegen, timewr;
147 TALLOC_CTX *mem_ctx = NULL;
149 time_t current_time, exp_time;
151 /* discard some eventlogs */
153 /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
154 although records not necessarily guaranteed to have successive times */
156 mem_ctx = talloc_init( "make_way_for_eventlogs" ); /* Homage to BPG */
158 if ( mem_ctx == NULL )
159 return False; /* can't allocate memory indicates bigger problems */
161 tdb_lock_bystring( the_tdb, VN_next_record, 1 );
163 end_record = tdb_fetch_int32( the_tdb, VN_next_record );
164 start_record = tdb_fetch_int32( the_tdb, VN_oldest_entry );
165 Retention = tdb_fetch_int32( the_tdb, VN_retention );
166 MaxSize = tdb_fetch_int32( the_tdb, VN_maxsize );
168 time( ¤t_time );
171 exp_time = current_time - Retention; /* discard older than exp_time */
173 /* todo - check for sanity in next_record */
177 ( "MaxSize [%d] Retention [%d] Current Time [%d] exp_time [%d]\n",
178 MaxSize, Retention, (uint32)current_time, (uint32)exp_time ) );
180 ( "Start Record [%d] End Record [%d]\n", start_record,
183 for ( i = start_record; i < end_record; i++ ) {
184 /* read a record, add the amt to nbytes */
185 key.dsize = sizeof( int32 );
186 key.dptr = ( char * ) ( int32 * ) & i;
187 ret = tdb_fetch( the_tdb, key );
188 if ( ret.dsize == 0 ) {
190 ( "Can't find a record for the key, record [%d]\n",
192 tdb_unlock_bystring( the_tdb, VN_next_record );
195 nbytes += ret.dsize; /* note this includes overhead */
197 len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
198 &tresv1, &trecnum, &timegen, &timewr );
200 ( "read record %d, record size is [%d], total so far [%d]\n",
201 i, reclen, nbytes ) );
203 SAFE_FREE( ret.dptr );
205 /* note that other servers may just stop writing records when the size limit
206 is reached, and there are no records older than 'retention'. This doesn't
207 like a very useful thing to do, so instead we whack (as in sleeps with the
208 fishes) just enough records to fit the what we need. This behavior could
209 be changed to 'match', if the need arises. */
211 if ( !whack_by_date && ( nbytes >= needed ) )
213 if ( whack_by_date && ( timegen >= exp_time ) )
218 ( "nbytes [%d] needed [%d] start_record is [%d], should be set to [%d]\n",
219 nbytes, needed, start_record, i ) );
220 /* todo - remove eventlog entries here and set starting record to start_record... */
222 if ( start_record != new_start ) {
223 for ( i = start_record; i < new_start; i++ ) {
224 key.dsize = sizeof( int32 );
225 key.dptr = ( char * ) ( int32 * ) & i;
226 tdb_delete( the_tdb, key );
229 tdb_store_int32( the_tdb, VN_oldest_entry, new_start );
231 tdb_unlock_bystring( the_tdb, VN_next_record );
236 some hygiene for an eventlog - see how big it is, and then
237 calculate how many bytes we need to remove
240 BOOL prune_eventlog( TDB_CONTEXT * tdb )
242 int MaxSize, Retention, CalcdSize;
245 DEBUG( 4, ( "No eventlog tdb handle\n" ) );
249 CalcdSize = eventlog_tdb_size( tdb, &MaxSize, &Retention );
251 ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
254 if ( CalcdSize > MaxSize ) {
255 return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
259 return make_way_for_eventlogs( tdb, 0, True );
262 BOOL can_write_to_eventlog( TDB_CONTEXT * tdb, int32 needed )
265 int MaxSize, Retention;
267 /* see if we can write to the eventlog -- do a policy enforcement */
269 return False; /* tdb is null, so we can't write to it */
277 calcd_size = eventlog_tdb_size( tdb, &MaxSize, &Retention );
279 if ( calcd_size <= MaxSize )
280 return True; /* you betcha */
281 if ( calcd_size + needed < MaxSize )
284 if ( Retention == 0xffffffff ) {
285 return False; /* see msdn - we can't write no room, discard */
288 note don't have to test, but always good to show intent, in case changes needed
292 if ( Retention == 0x00000000 ) {
293 /* discard record(s) */
294 /* todo - decide when to remove a bunch vs. just what we need... */
295 return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
299 return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
302 TDB_CONTEXT *open_eventlog_tdb( char *tdbfilename )
304 TDB_CONTEXT *the_tdb;
307 tdb_open_log( tdbfilename, 0, TDB_DEFAULT, O_RDWR | O_CREAT,
309 if ( the_tdb == NULL ) {
310 return init_eventlog_tdb( tdbfilename );
312 if ( EVENTLOG_DATABASE_VERSION_V1 !=
313 tdb_fetch_int32( the_tdb, VN_version ) ) {
314 tdb_close( the_tdb );
315 return init_eventlog_tdb( tdbfilename );
320 /* write an eventlog entry. Note that we have to lock, read next eventlog, increment, write, write the record, unlock */
322 /* coming into this, ee has the eventlog record, and the auxilliary date (computer name, etc.)
323 filled into the other structure. Before packing into a record, this routine will calc the
324 appropriate padding, etc., and then blast out the record in a form that can be read back in */
325 int write_eventlog_tdb( TDB_CONTEXT * the_tdb, Eventlog_entry * ee )
329 TALLOC_CTX *mem_ctx = NULL;
336 mem_ctx = talloc_init( "write_eventlog_tdb" );
338 if ( mem_ctx == NULL )
343 /* discard any entries that have bogus time, which usually indicates a bogus entry as well. */
344 if ( ee->record.time_generated == 0 )
349 /* todo - check for sanity in next_record */
351 fixup_eventlog_entry( ee );
353 if ( !can_write_to_eventlog( the_tdb, ee->record.length ) ) {
354 DEBUG( 3, ( "Can't write to Eventlog, no room \n" ) );
355 talloc_destroy( mem_ctx );
359 /* alloc mem for the packed version */
360 packed_ee = TALLOC( mem_ctx, ee->record.length + MARGIN );
362 talloc_destroy( mem_ctx );
366 /* need to read the record number and insert it into the entry here */
369 tdb_lock_bystring( the_tdb, VN_next_record, 1 );
371 next_record = tdb_fetch_int32( the_tdb, VN_next_record );
374 tdb_pack( packed_ee, ee->record.length + MARGIN,
375 "ddddddwwwwddddddBBdBBBd", ee->record.length,
376 ee->record.reserved1, next_record,
377 ee->record.time_generated, ee->record.time_written,
378 ee->record.event_id, ee->record.event_type,
379 ee->record.num_strings, ee->record.event_category,
380 ee->record.reserved2,
381 ee->record.closing_record_number,
382 ee->record.string_offset,
383 ee->record.user_sid_length,
384 ee->record.user_sid_offset, ee->record.data_length,
385 ee->record.data_offset,
386 ee->data_record.source_name_len,
387 ee->data_record.source_name,
388 ee->data_record.computer_name_len,
389 ee->data_record.computer_name,
390 ee->data_record.sid_padding,
391 ee->record.user_sid_length, ee->data_record.sid,
392 ee->data_record.strings_len,
393 ee->data_record.strings,
394 ee->data_record.user_data_len,
395 ee->data_record.user_data,
396 ee->data_record.data_padding );
398 /*DEBUG(3,("write_eventlog_tdb: packed into %d\n",n_packed)); */
400 /* increment the record count */
402 kbuf.dsize = sizeof( int32 );
403 kbuf.dptr = ( uint8 * ) & next_record;
405 ebuf.dsize = n_packed;
406 ebuf.dptr = packed_ee;
408 if ( tdb_store( the_tdb, kbuf, ebuf, 0 ) ) {
409 /* DEBUG(1,("write_eventlog_tdb: Can't write record %d to eventlog\n",next_record)); */
410 tdb_unlock_bystring( the_tdb, VN_next_record );
411 talloc_destroy( mem_ctx );
415 tdb_store_int32( the_tdb, VN_next_record, next_record );
416 tdb_unlock_bystring( the_tdb, VN_next_record );
417 talloc_destroy( mem_ctx );
418 return ( next_record - 1 );
421 /* calculate the correct fields etc for an eventlog entry */
423 void fixup_eventlog_entry( Eventlog_entry * ee )
425 /* fix up the eventlog entry structure as necessary */
427 ee->data_record.sid_padding =
429 ( ( ee->data_record.source_name_len +
430 ee->data_record.computer_name_len ) % 4 ) ) % 4 );
431 ee->data_record.data_padding =
433 ( ( ee->data_record.strings_len +
434 ee->data_record.user_data_len ) % 4 ) ) % 4;
435 ee->record.length = sizeof( Eventlog_record );
436 ee->record.length += ee->data_record.source_name_len;
437 ee->record.length += ee->data_record.computer_name_len;
438 if ( ee->record.user_sid_length == 0 ) {
439 /* Should not pad to a DWORD boundary for writing out the sid if there is
440 no SID, so just propagate the padding to pad the data */
441 ee->data_record.data_padding += ee->data_record.sid_padding;
442 ee->data_record.sid_padding = 0;
444 /* DEBUG(10, ("sid_padding is [%d].\n", ee->data_record.sid_padding)); */
445 /* DEBUG(10, ("data_padding is [%d].\n", ee->data_record.data_padding)); */
447 ee->record.length += ee->data_record.sid_padding;
448 ee->record.length += ee->record.user_sid_length;
449 ee->record.length += ee->data_record.strings_len;
450 ee->record.length += ee->data_record.user_data_len;
451 ee->record.length += ee->data_record.data_padding;
452 /* need another copy of length at the end of the data */
453 ee->record.length += sizeof( ee->record.length );
456 /********************************************************************
457 Note that it's a pretty good idea to initialize the Eventlog_entry structure to zero's before
458 calling parse_logentry on an batch of lines that may resolve to a record.
459 ALSO, it's a good idea to remove any linefeeds (that's EOL to you and me) on the lines going in.
461 ********************************************************************/
463 BOOL parse_logentry( char *line, Eventlog_entry * entry, BOOL * eor )
465 char *start = NULL, *stop = NULL;
471 /* empty line signyfiying record delimeter, or we're at the end of the buffer */
472 if ( start == NULL || strlen( start ) == 0 ) {
474 ( "parse_logentry: found end-of-record indicator.\n" ) );
478 if ( !( stop = strchr( line, ':' ) ) ) {
482 DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
484 if ( 0 == strncmp( start, "LEN", stop - start ) ) {
485 /* This will get recomputed later anyway -- probably not necessary */
486 entry->record.length = atoi( stop + 1 );
487 } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
488 /* For now all these reserved entries seem to have the same value,
489 which can be hardcoded to int(1699505740) for now */
490 entry->record.reserved1 = atoi( stop + 1 );
491 } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
492 entry->record.record_number = atoi( stop + 1 );
493 } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
494 entry->record.time_generated = atoi( stop + 1 );
495 } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
496 entry->record.time_written = atoi( stop + 1 );
497 } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
498 entry->record.event_id = atoi( stop + 1 );
499 } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
500 if ( strstr( start, "ERROR" ) ) {
501 entry->record.event_type = EVENTLOG_ERROR_TYPE;
502 } else if ( strstr( start, "WARNING" ) ) {
503 entry->record.event_type = EVENTLOG_WARNING_TYPE;
504 } else if ( strstr( start, "INFO" ) ) {
505 entry->record.event_type = EVENTLOG_INFORMATION_TYPE;
506 } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
507 entry->record.event_type = EVENTLOG_AUDIT_SUCCESS;
508 } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
509 entry->record.event_type = EVENTLOG_AUDIT_FAILURE;
510 } else if ( strstr( start, "SUCCESS" ) ) {
511 entry->record.event_type = EVENTLOG_SUCCESS;
513 /* some other eventlog type -- currently not defined in MSDN docs, so error out */
519 else if(0 == strncmp(start, "NST", stop - start))
521 entry->record.num_strings = atoi(stop + 1);
524 else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
525 entry->record.event_category = atoi( stop + 1 );
526 } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
527 entry->record.reserved2 = atoi( stop + 1 );
528 } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
529 entry->record.closing_record_number = atoi( stop + 1 );
530 } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
531 entry->record.user_sid_length = atoi( stop + 1 );
532 } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
533 memset( temp, 0, sizeof( temp ) );
535 while ( isspace( stop[0] ) ) {
538 temp_len = strlen( stop );
539 strncpy( temp, stop, temp_len );
540 rpcstr_push( ( void * ) ( entry->data_record.source_name ),
541 temp, sizeof( entry->data_record.source_name ),
543 entry->data_record.source_name_len =
544 ( strlen_w( entry->data_record.source_name ) * 2 ) +
546 } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
547 memset( temp, 0, sizeof( temp ) );
549 while ( isspace( stop[0] ) ) {
552 temp_len = strlen( stop );
553 strncpy( temp, stop, temp_len );
554 rpcstr_push( ( void * ) ( entry->data_record.computer_name ),
555 temp, sizeof( entry->data_record.computer_name ),
557 entry->data_record.computer_name_len =
558 ( strlen_w( entry->data_record.computer_name ) * 2 ) +
560 } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
561 memset( temp, 0, sizeof( temp ) );
563 while ( isspace( stop[0] ) ) {
566 temp_len = strlen( stop );
567 strncpy( temp, stop, temp_len );
568 rpcstr_push( ( void * ) ( entry->data_record.sid ), temp,
569 sizeof( entry->data_record.sid ),
571 entry->record.user_sid_length =
572 ( strlen_w( entry->data_record.sid ) * 2 ) + 2;
573 } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
574 /* skip past initial ":" */
576 /* now skip any other leading whitespace */
577 while ( isspace( stop[0] ) ) {
580 temp_len = strlen( stop );
581 memset( temp, 0, sizeof( temp ) );
582 strncpy( temp, stop, temp_len );
583 rpcstr_push( ( void * ) ( entry->data_record.strings +
584 entry->data_record.strings_len ),
586 sizeof( entry->data_record.strings ) -
587 entry->data_record.strings_len, STR_TERMINATE );
588 entry->data_record.strings_len += temp_len + 1;
589 entry->record.num_strings++;
590 } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
591 /* Now that we're done processing the STR data, adjust the length to account for
592 unicode, then proceed with the DAT data. */
593 entry->data_record.strings_len *= 2;
594 /* skip past initial ":" */
596 /* now skip any other leading whitespace */
597 while ( isspace( stop[0] ) ) {
600 entry->data_record.user_data_len = strlen( stop );
601 memset( entry->data_record.user_data, 0,
602 sizeof( entry->data_record.user_data ) );
603 if ( entry->data_record.user_data_len > 0 ) {
604 /* copy no more than the first 1024 bytes */
605 if ( entry->data_record.user_data_len >
606 sizeof( entry->data_record.user_data ) )
607 entry->data_record.user_data_len =
608 sizeof( entry->data_record.
610 memcpy( entry->data_record.user_data, stop,
611 entry->data_record.user_data_len );
614 /* some other eventlog entry -- not implemented, so dropping on the floor */
615 DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
616 /* For now return true so that we can keep on parsing this mess. Eventually
617 we will return False here. */