0ff0cb1fa80cdaa5e2b13c2b4da32944d4586928
[kai/samba.git] / source3 / lib / eventlog / eventlog.c
1 /*
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.
7  *  Copyright (C) Guenther Deschner          2009.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 3 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "lib/eventlog/eventlog.h"
26 #include "../libcli/security/security.h"
27 #include "util_tdb.h"
28
29 /* maintain a list of open eventlog tdbs with reference counts */
30
31 static ELOG_TDB *open_elog_list;
32
33 /********************************************************************
34  Init an Eventlog TDB, and return it. If null, something bad
35  happened.
36 ********************************************************************/
37
38 TDB_CONTEXT *elog_init_tdb( char *tdbfilename )
39 {
40         TDB_CONTEXT *tdb;
41
42         DEBUG(10,("elog_init_tdb: Initializing eventlog tdb (%s)\n",
43                 tdbfilename));
44
45         tdb = tdb_open_log( tdbfilename, 0, TDB_DEFAULT,
46                 O_RDWR|O_CREAT|O_TRUNC, 0660 );
47
48         if ( !tdb ) {
49                 DEBUG( 0, ( "Can't open tdb for [%s]\n", tdbfilename ) );
50                 return NULL;
51         }
52
53         /* initialize with defaults, copy real values in here from registry */
54
55         tdb_store_int32( tdb, EVT_OLDEST_ENTRY, 1 );
56         tdb_store_int32( tdb, EVT_NEXT_RECORD, 1 );
57         tdb_store_int32( tdb, EVT_MAXSIZE, 0x80000 );
58         tdb_store_int32( tdb, EVT_RETENTION, 0x93A80 );
59
60         tdb_store_int32( tdb, EVT_VERSION, EVENTLOG_DATABASE_VERSION_V1 );
61
62         return tdb;
63 }
64
65 /********************************************************************
66  make the tdb file name for an event log, given destination buffer
67  and size. Caller must free memory.
68 ********************************************************************/
69
70 char *elog_tdbname(TALLOC_CTX *ctx, const char *name )
71 {
72         char *path;
73         char *file;
74         char *tdbname;
75
76         path = talloc_strdup(ctx, state_path("eventlog"));
77         if (!path) {
78                 return NULL;
79         }
80
81         file = talloc_asprintf_strlower_m(path, "%s.tdb", name);
82         if (!file) {
83                 talloc_free(path);
84                 return NULL;
85         }
86
87         tdbname = talloc_asprintf(path, "%s/%s", state_path("eventlog"), file);
88         if (!tdbname) {
89                 talloc_free(path);
90                 return NULL;
91         }
92
93         return tdbname;
94 }
95
96
97 /********************************************************************
98  this function is used to count up the number of bytes in a
99  particular TDB
100 ********************************************************************/
101
102 struct trav_size_struct {
103         int size;
104         int rec_count;
105 };
106
107 static int eventlog_tdb_size_fn( TDB_CONTEXT * tdb, TDB_DATA key, TDB_DATA data,
108                           void *state )
109 {
110         struct trav_size_struct  *tsize = (struct trav_size_struct *)state;
111
112         tsize->size += data.dsize;
113         tsize->rec_count++;
114
115         return 0;
116 }
117
118 /********************************************************************
119  returns the size of the eventlog, and if MaxSize is a non-null
120  ptr, puts the MaxSize there. This is purely a way not to have yet
121  another function that solely reads the maxsize of the eventlog.
122  Yeah, that's it.
123 ********************************************************************/
124
125 int elog_tdb_size( TDB_CONTEXT * tdb, int *MaxSize, int *Retention )
126 {
127         struct trav_size_struct tsize;
128
129         if ( !tdb )
130                 return 0;
131
132         ZERO_STRUCT( tsize );
133
134         tdb_traverse( tdb, eventlog_tdb_size_fn, &tsize );
135
136         if ( MaxSize != NULL ) {
137                 *MaxSize = tdb_fetch_int32( tdb, EVT_MAXSIZE );
138         }
139
140         if ( Retention != NULL ) {
141                 *Retention = tdb_fetch_int32( tdb, EVT_RETENTION );
142         }
143
144         DEBUG( 1,
145                ( "eventlog size: [%d] for [%d] records\n", tsize.size,
146                  tsize.rec_count ) );
147         return tsize.size;
148 }
149
150 /********************************************************************
151  Discard early event logs until we have enough for 'needed' bytes...
152  NO checking done beforehand to see that we actually need to do
153  this, and it's going to pluck records one-by-one. So, it's best
154  to determine that this needs to be done before doing it.
155
156  Setting whack_by_date to True indicates that eventlogs falling
157  outside of the retention range need to go...
158
159  return True if we made enough room to accommodate needed bytes
160 ********************************************************************/
161
162 static bool make_way_for_eventlogs( TDB_CONTEXT * the_tdb, int32_t needed,
163                                     bool whack_by_date )
164 {
165         int32_t start_record, i, new_start;
166         int32_t end_record;
167         int32_t reclen, tresv1, trecnum, timegen, timewr;
168         int nbytes, len, Retention, MaxSize;
169         TDB_DATA key, ret;
170         time_t current_time, exp_time;
171
172         /* discard some eventlogs */
173
174         /* read eventlogs from oldest_entry -- there can't be any discontinuity in recnos,
175            although records not necessarily guaranteed to have successive times */
176         /* */
177
178         /* lock */
179         tdb_lock_bystring_with_timeout( the_tdb, EVT_NEXT_RECORD, 1 );
180         /* read */
181         end_record = tdb_fetch_int32( the_tdb, EVT_NEXT_RECORD );
182         start_record = tdb_fetch_int32( the_tdb, EVT_OLDEST_ENTRY );
183         Retention = tdb_fetch_int32( the_tdb, EVT_RETENTION );
184         MaxSize = tdb_fetch_int32( the_tdb, EVT_MAXSIZE );
185
186         time( &current_time );
187
188         /* calculate ... */
189         exp_time = current_time - Retention;    /* discard older than exp_time */
190
191         /* todo - check for sanity in next_record */
192         nbytes = 0;
193
194         DEBUG( 3,
195                ( "MaxSize [%d] Retention [%d] Current Time [%u]  exp_time [%u]\n",
196                  MaxSize, Retention, (unsigned int)current_time, (unsigned int)exp_time ) );
197         DEBUG( 3,
198                ( "Start Record [%u] End Record [%u]\n",
199                 (unsigned int)start_record,
200                 (unsigned int)end_record ));
201
202         for ( i = start_record; i < end_record; i++ ) {
203                 /* read a record, add the amt to nbytes */
204                 key.dsize = sizeof(int32_t);
205                 key.dptr = (unsigned char *)&i;
206                 ret = tdb_fetch( the_tdb, key );
207                 if ( ret.dsize == 0 ) {
208                         DEBUG( 8,
209                                ( "Can't find a record for the key, record [%d]\n",
210                                  i ) );
211                         tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
212                         return False;
213                 }
214                 nbytes += ret.dsize;    /* note this includes overhead */
215
216                 len = tdb_unpack( ret.dptr, ret.dsize, "ddddd", &reclen,
217                                   &tresv1, &trecnum, &timegen, &timewr );
218                 if (len == -1) {
219                         DEBUG( 10,("make_way_for_eventlogs: tdb_unpack failed.\n"));
220                         tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
221                         SAFE_FREE( ret.dptr );
222                         return False;
223                 }
224
225                 DEBUG( 8,
226                        ( "read record %u, record size is [%d], total so far [%d]\n",
227                          (unsigned int)i, reclen, nbytes ) );
228
229                 SAFE_FREE( ret.dptr );
230
231                 /* note that other servers may just stop writing records when the size limit
232                    is reached, and there are no records older than 'retention'. This doesn't
233                    like a very useful thing to do, so instead we whack (as in sleeps with the
234                    fishes) just enough records to fit the what we need.  This behavior could
235                    be changed to 'match', if the need arises. */
236
237                 if ( !whack_by_date && ( nbytes >= needed ) )
238                         break;  /* done */
239                 if ( whack_by_date && ( timegen >= exp_time ) )
240                         break;  /* done */
241         }
242
243         DEBUG( 3,
244                ( "nbytes [%d] needed [%d] start_record is [%u], should be set to [%u]\n",
245                  nbytes, needed, (unsigned int)start_record, (unsigned int)i ) );
246         /* todo - remove eventlog entries here and set starting record to start_record... */
247         new_start = i;
248         if ( start_record != new_start ) {
249                 for ( i = start_record; i < new_start; i++ ) {
250                         key.dsize = sizeof(int32_t);
251                         key.dptr = (unsigned char *)&i;
252                         tdb_delete( the_tdb, key );
253                 }
254
255                 tdb_store_int32( the_tdb, EVT_OLDEST_ENTRY, new_start );
256         }
257         tdb_unlock_bystring( the_tdb, EVT_NEXT_RECORD );
258         return True;
259 }
260
261 /********************************************************************
262   some hygiene for an eventlog - see how big it is, and then
263   calculate how many bytes we need to remove
264 ********************************************************************/
265
266 bool prune_eventlog( TDB_CONTEXT * tdb )
267 {
268         int MaxSize, Retention, CalcdSize;
269
270         if ( !tdb ) {
271                 DEBUG( 4, ( "No eventlog tdb handle\n" ) );
272                 return False;
273         }
274
275         CalcdSize = elog_tdb_size( tdb, &MaxSize, &Retention );
276         DEBUG( 3,
277                ( "Calculated size [%d] MaxSize [%d]\n", CalcdSize,
278                  MaxSize ) );
279
280         if ( CalcdSize > MaxSize ) {
281                 return make_way_for_eventlogs( tdb, CalcdSize - MaxSize,
282                                                False );
283         }
284
285         return make_way_for_eventlogs( tdb, 0, True );
286 }
287
288 /********************************************************************
289 ********************************************************************/
290
291 static bool can_write_to_eventlog( TDB_CONTEXT * tdb, int32_t needed )
292 {
293         int calcd_size;
294         int MaxSize, Retention;
295
296         /* see if we can write to the eventlog -- do a policy enforcement */
297         if ( !tdb )
298                 return False;   /* tdb is null, so we can't write to it */
299
300
301         if ( needed < 0 )
302                 return False;
303         MaxSize = 0;
304         Retention = 0;
305
306         calcd_size = elog_tdb_size( tdb, &MaxSize, &Retention );
307
308         if ( calcd_size <= MaxSize )
309                 return True;    /* you betcha */
310         if ( calcd_size + needed < MaxSize )
311                 return True;
312
313         if ( Retention == 0xffffffff ) {
314                 return False;   /* see msdn - we can't write no room, discard */
315         }
316         /*
317            note don't have to test, but always good to show intent, in case changes needed
318            later
319          */
320
321         if ( Retention == 0x00000000 ) {
322                 /* discard record(s) */
323                 /* todo  - decide when to remove a bunch vs. just what we need... */
324                 return make_way_for_eventlogs( tdb, calcd_size - MaxSize,
325                                                True );
326         }
327
328         return make_way_for_eventlogs( tdb, calcd_size - MaxSize, False );
329 }
330
331 /*******************************************************************
332 *******************************************************************/
333
334 ELOG_TDB *elog_open_tdb( const char *logname, bool force_clear, bool read_only )
335 {
336         TDB_CONTEXT *tdb = NULL;
337         uint32_t vers_id;
338         ELOG_TDB *ptr;
339         char *tdbpath = NULL;
340         ELOG_TDB *tdb_node = NULL;
341         char *eventlogdir;
342         TALLOC_CTX *ctx = talloc_tos();
343
344         /* check for invalid options */
345
346         if (force_clear && read_only) {
347                 DEBUG(1,("elog_open_tdb: Invalid flags\n"));
348                 return NULL;
349         }
350
351         /* first see if we have an open context */
352
353         for ( ptr=open_elog_list; ptr; ptr=ptr->next ) {
354                 if ( strequal( ptr->name, logname ) ) {
355                         ptr->ref_count++;
356
357                         /* trick to alow clearing of the eventlog tdb.
358                            The force_clear flag should imply that someone
359                            has done a force close.  So make sure the tdb
360                            is NULL.  If this is a normal open, then just
361                            return the existing reference */
362
363                         if ( force_clear ) {
364                                 SMB_ASSERT( ptr->tdb == NULL );
365                                 break;
366                         }
367                         else
368                                 return ptr;
369                 }
370         }
371
372         /* make sure that the eventlog dir exists */
373
374         eventlogdir = state_path( "eventlog" );
375         if ( !directory_exist( eventlogdir ) )
376                 mkdir( eventlogdir, 0755 );
377
378         /* get the path on disk */
379
380         tdbpath = elog_tdbname(ctx, logname);
381         if (!tdbpath) {
382                 return NULL;
383         }
384
385         DEBUG(7,("elog_open_tdb: Opening %s...(force_clear == %s)\n",
386                 tdbpath, force_clear?"True":"False" ));
387
388         /* the tdb wasn't already open or this is a forced clear open */
389
390         if ( !force_clear ) {
391
392                 tdb = tdb_open_log( tdbpath, 0, TDB_DEFAULT, read_only ? O_RDONLY : O_RDWR , 0 );
393                 if ( tdb ) {
394                         vers_id = tdb_fetch_int32( tdb, EVT_VERSION );
395
396                         if ( vers_id != EVENTLOG_DATABASE_VERSION_V1 ) {
397                                 DEBUG(1,("elog_open_tdb: Invalid version [%d] on file [%s].\n",
398                                         vers_id, tdbpath));
399                                 tdb_close( tdb );
400                                 tdb = elog_init_tdb( tdbpath );
401                         }
402                 }
403         }
404
405         if ( !tdb )
406                 tdb = elog_init_tdb( tdbpath );
407
408         /* if we got a valid context, then add it to the list */
409
410         if ( tdb ) {
411                 /* on a forced clear, just reset the tdb context if we already
412                    have an open entry in the list */
413
414                 if ( ptr ) {
415                         ptr->tdb = tdb;
416                         return ptr;
417                 }
418
419                 if ( !(tdb_node = TALLOC_ZERO_P( NULL, ELOG_TDB)) ) {
420                         DEBUG(0,("elog_open_tdb: talloc() failure!\n"));
421                         tdb_close( tdb );
422                         return NULL;
423                 }
424
425                 tdb_node->name = talloc_strdup( tdb_node, logname );
426                 tdb_node->tdb = tdb;
427                 tdb_node->ref_count = 1;
428
429                 DLIST_ADD( open_elog_list, tdb_node );
430         }
431
432         return tdb_node;
433 }
434
435 /*******************************************************************
436  Wrapper to handle reference counts to the tdb
437 *******************************************************************/
438
439 int elog_close_tdb( ELOG_TDB *etdb, bool force_close )
440 {
441         TDB_CONTEXT *tdb;
442
443         if ( !etdb )
444                 return 0;
445
446         etdb->ref_count--;
447
448         SMB_ASSERT( etdb->ref_count >= 0 );
449
450         if ( etdb->ref_count == 0 ) {
451                 tdb = etdb->tdb;
452                 DLIST_REMOVE( open_elog_list, etdb );
453                 TALLOC_FREE( etdb );
454                 return tdb_close( tdb );
455         }
456
457         if ( force_close ) {
458                 tdb = etdb->tdb;
459                 etdb->tdb = NULL;
460                 return tdb_close( tdb );
461         }
462
463         return 0;
464 }
465
466 /********************************************************************
467  Note that it's a pretty good idea to initialize the Eventlog_entry
468  structure to zero's before calling parse_logentry on an batch of
469  lines that may resolve to a record.  ALSO, it's a good idea to
470  remove any linefeeds (that's EOL to you and me) on the lines
471  going in.
472 ********************************************************************/
473
474 bool parse_logentry( TALLOC_CTX *mem_ctx, char *line, struct eventlog_Record_tdb *entry, bool * eor )
475 {
476         char *start = NULL, *stop = NULL;
477
478         start = line;
479
480         /* empty line signyfiying record delimeter, or we're at the end of the buffer */
481         if ( start == NULL || strlen( start ) == 0 ) {
482                 DEBUG( 6,
483                        ( "parse_logentry: found end-of-record indicator.\n" ) );
484                 *eor = True;
485                 return True;
486         }
487         if ( !( stop = strchr( line, ':' ) ) ) {
488                 return False;
489         }
490
491         DEBUG( 6, ( "parse_logentry: trying to parse [%s].\n", line ) );
492
493         if ( 0 == strncmp( start, "LEN", stop - start ) ) {
494                 /* This will get recomputed later anyway -- probably not necessary */
495                 entry->size = atoi( stop + 1 );
496         } else if ( 0 == strncmp( start, "RS1", stop - start ) ) {
497                 /* For now all these reserved entries seem to have the same value,
498                    which can be hardcoded to int(1699505740) for now */
499                 entry->reserved = talloc_strdup(mem_ctx, "eLfL");
500         } else if ( 0 == strncmp( start, "RCN", stop - start ) ) {
501                 entry->record_number = atoi( stop + 1 );
502         } else if ( 0 == strncmp( start, "TMG", stop - start ) ) {
503                 entry->time_generated = atoi( stop + 1 );
504         } else if ( 0 == strncmp( start, "TMW", stop - start ) ) {
505                 entry->time_written = atoi( stop + 1 );
506         } else if ( 0 == strncmp( start, "EID", stop - start ) ) {
507                 entry->event_id = atoi( stop + 1 );
508         } else if ( 0 == strncmp( start, "ETP", stop - start ) ) {
509                 if ( strstr( start, "ERROR" ) ) {
510                         entry->event_type = EVENTLOG_ERROR_TYPE;
511                 } else if ( strstr( start, "WARNING" ) ) {
512                         entry->event_type = EVENTLOG_WARNING_TYPE;
513                 } else if ( strstr( start, "INFO" ) ) {
514                         entry->event_type = EVENTLOG_INFORMATION_TYPE;
515                 } else if ( strstr( start, "AUDIT_SUCCESS" ) ) {
516                         entry->event_type = EVENTLOG_AUDIT_SUCCESS;
517                 } else if ( strstr( start, "AUDIT_FAILURE" ) ) {
518                         entry->event_type = EVENTLOG_AUDIT_FAILURE;
519                 } else if ( strstr( start, "SUCCESS" ) ) {
520                         entry->event_type = EVENTLOG_SUCCESS;
521                 } else {
522                         /* some other eventlog type -- currently not defined in MSDN docs, so error out */
523                         return False;
524                 }
525         }
526
527 /*
528   else if(0 == strncmp(start, "NST", stop - start))
529   {
530   entry->num_of_strings = atoi(stop + 1);
531   }
532 */
533         else if ( 0 == strncmp( start, "ECT", stop - start ) ) {
534                 entry->event_category = atoi( stop + 1 );
535         } else if ( 0 == strncmp( start, "RS2", stop - start ) ) {
536                 entry->reserved_flags = atoi( stop + 1 );
537         } else if ( 0 == strncmp( start, "CRN", stop - start ) ) {
538                 entry->closing_record_number = atoi( stop + 1 );
539         } else if ( 0 == strncmp( start, "USL", stop - start ) ) {
540                 entry->sid_length = atoi( stop + 1 );
541         } else if ( 0 == strncmp( start, "SRC", stop - start ) ) {
542                 stop++;
543                 while ( isspace( stop[0] ) ) {
544                         stop++;
545                 }
546                 entry->source_name_len = strlen_m_term(stop);
547                 entry->source_name = talloc_strdup(mem_ctx, stop);
548                 if (entry->source_name_len == (uint32_t)-1 ||
549                                 entry->source_name == NULL) {
550                         return false;
551                 }
552         } else if ( 0 == strncmp( start, "SRN", stop - start ) ) {
553                 stop++;
554                 while ( isspace( stop[0] ) ) {
555                         stop++;
556                 }
557                 entry->computer_name_len = strlen_m_term(stop);
558                 entry->computer_name = talloc_strdup(mem_ctx, stop);
559                 if (entry->computer_name_len == (uint32_t)-1 ||
560                                 entry->computer_name == NULL) {
561                         return false;
562                 }
563         } else if ( 0 == strncmp( start, "SID", stop - start ) ) {
564                 smb_ucs2_t *dummy = NULL;
565                 stop++;
566                 while ( isspace( stop[0] ) ) {
567                         stop++;
568                 }
569                 entry->sid_length = rpcstr_push_talloc(mem_ctx,
570                                 &dummy,
571                                 stop);
572                 if (entry->sid_length == (uint32_t)-1) {
573                         return false;
574                 }
575                 entry->sid = data_blob_talloc(mem_ctx, dummy, entry->sid_length);
576                 if (entry->sid.data == NULL) {
577                         return false;
578                 }
579         } else if ( 0 == strncmp( start, "STR", stop - start ) ) {
580                 size_t tmp_len;
581                 int num_of_strings;
582                 /* skip past initial ":" */
583                 stop++;
584                 /* now skip any other leading whitespace */
585                 while ( isspace(stop[0])) {
586                         stop++;
587                 }
588                 tmp_len = strlen_m_term(stop);
589                 if (tmp_len == (size_t)-1) {
590                         return false;
591                 }
592                 num_of_strings = entry->num_of_strings;
593                 if (!add_string_to_array(mem_ctx, stop, &entry->strings,
594                                          &num_of_strings)) {
595                         return false;
596                 }
597                 if (num_of_strings > 0xffff) {
598                         return false;
599                 }
600                 entry->num_of_strings = num_of_strings;
601                 entry->strings_len += tmp_len;
602         } else if ( 0 == strncmp( start, "DAT", stop - start ) ) {
603                 /* skip past initial ":" */
604                 stop++;
605                 /* now skip any other leading whitespace */
606                 while ( isspace( stop[0] ) ) {
607                         stop++;
608                 }
609                 entry->data_length = strlen_m(stop);
610                 entry->data = data_blob_talloc(mem_ctx, stop, entry->data_length);
611                 if (!entry->data.data) {
612                         return false;
613                 }
614         } else {
615                 /* some other eventlog entry -- not implemented, so dropping on the floor */
616                 DEBUG( 10, ( "Unknown entry [%s]. Ignoring.\n", line ) );
617                 /* For now return true so that we can keep on parsing this mess. Eventually
618                    we will return False here. */
619                 return true;
620         }
621         return true;
622 }
623
624 /*******************************************************************
625  calculate the correct fields etc for an eventlog entry
626 *******************************************************************/
627
628 size_t fixup_eventlog_record_tdb(struct eventlog_Record_tdb *r)
629 {
630         size_t size = 56; /* static size of integers before buffers start */
631
632         r->source_name_len = strlen_m_term(r->source_name) * 2;
633         r->computer_name_len = strlen_m_term(r->computer_name) * 2;
634         r->strings_len = ndr_size_string_array(r->strings,
635                 r->num_of_strings, LIBNDR_FLAG_STR_NULLTERM) * 2;
636
637         /* fix up the eventlog entry structure as necessary */
638         r->sid_padding = ( ( 4 - ( ( r->source_name_len + r->computer_name_len ) % 4 ) ) % 4 );
639         r->padding =       ( 4 - ( ( r->strings_len + r->data_length ) % 4 ) ) % 4;
640
641         if (r->sid_length == 0) {
642                 /* Should not pad to a DWORD boundary for writing out the sid if there is
643                    no SID, so just propagate the padding to pad the data */
644                 r->padding += r->sid_padding;
645                 r->sid_padding = 0;
646         }
647
648         size += r->source_name_len;
649         size += r->computer_name_len;
650         size += r->sid_padding;
651         size += r->sid_length;
652         size += r->strings_len;
653         size += r->data_length;
654         size += r->padding;
655         /* need another copy of length at the end of the data */
656         size += sizeof(r->size);
657
658         r->size = size;
659
660         return size;
661 }
662
663
664 /********************************************************************
665  ********************************************************************/
666
667 struct eventlog_Record_tdb *evlog_pull_record_tdb(TALLOC_CTX *mem_ctx,
668                                                   TDB_CONTEXT *tdb,
669                                                   uint32_t record_number)
670 {
671         struct eventlog_Record_tdb *r;
672         TDB_DATA data, key;
673
674         int32_t srecno;
675         enum ndr_err_code ndr_err;
676         DATA_BLOB blob;
677
678         srecno = record_number;
679         key.dptr = (unsigned char *)&srecno;
680         key.dsize = sizeof(int32_t);
681
682         data = tdb_fetch(tdb, key);
683         if (data.dsize == 0) {
684                 DEBUG(8,("evlog_pull_record_tdb: "
685                         "Can't find a record for the key, record %d\n",
686                         record_number));
687                 return NULL;
688         }
689
690         r = talloc_zero(mem_ctx, struct eventlog_Record_tdb);
691         if (!r) {
692                 goto done;
693         }
694
695         blob = data_blob_const(data.dptr, data.dsize);
696
697         ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, r,
698                            (ndr_pull_flags_fn_t)ndr_pull_eventlog_Record_tdb);
699
700         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
701                 DEBUG(10,("evlog_pull_record_tdb: failed to decode record %d\n",
702                         record_number));
703                 TALLOC_FREE(r);
704                 goto done;
705         }
706
707         if (DEBUGLEVEL >= 10) {
708                 NDR_PRINT_DEBUG(eventlog_Record_tdb, r);
709         }
710
711         DEBUG(10,("evlog_pull_record_tdb: retrieved entry for record %d\n",
712                 record_number));
713  done:
714         SAFE_FREE(data.dptr);
715
716         return r;
717 }
718
719 /********************************************************************
720  ********************************************************************/
721
722 struct EVENTLOGRECORD *evlog_pull_record(TALLOC_CTX *mem_ctx,
723                                          TDB_CONTEXT *tdb,
724                                          uint32_t record_number)
725 {
726         struct eventlog_Record_tdb *t;
727         struct EVENTLOGRECORD *r;
728         NTSTATUS status;
729
730         r = talloc_zero(mem_ctx, struct EVENTLOGRECORD);
731         if (!r) {
732                 return NULL;
733         }
734
735         t = evlog_pull_record_tdb(r, tdb, record_number);
736         if (!t) {
737                 talloc_free(r);
738                 return NULL;
739         }
740
741         status = evlog_tdb_entry_to_evt_entry(r, t, r);
742         if (!NT_STATUS_IS_OK(status)) {
743                 talloc_free(r);
744                 return NULL;
745         }
746
747         r->Length = r->Length2 = ndr_size_EVENTLOGRECORD(r, 0);
748
749         return r;
750 }
751
752 /********************************************************************
753  write an eventlog entry. Note that we have to lock, read next
754  eventlog, increment, write, write the record, unlock
755
756  coming into this, ee has the eventlog record, and the auxilliary date
757  (computer name, etc.) filled into the other structure. Before packing
758  into a record, this routine will calc the appropriate padding, etc.,
759  and then blast out the record in a form that can be read back in
760  ********************************************************************/
761
762 NTSTATUS evlog_push_record_tdb(TALLOC_CTX *mem_ctx,
763                                TDB_CONTEXT *tdb,
764                                struct eventlog_Record_tdb *r,
765                                uint32_t *record_number)
766 {
767         TDB_DATA kbuf, ebuf;
768         DATA_BLOB blob;
769         enum ndr_err_code ndr_err;
770         int ret;
771
772         if (!r) {
773                 return NT_STATUS_INVALID_PARAMETER;
774         }
775
776         if (!can_write_to_eventlog(tdb, r->size)) {
777                 return NT_STATUS_EVENTLOG_CANT_START;
778         }
779
780         /* need to read the record number and insert it into the entry here */
781
782         /* lock */
783         ret = tdb_lock_bystring_with_timeout(tdb, EVT_NEXT_RECORD, 1);
784         if (ret == -1) {
785                 return NT_STATUS_LOCK_NOT_GRANTED;
786         }
787
788         /* read */
789         r->record_number = tdb_fetch_int32(tdb, EVT_NEXT_RECORD);
790
791         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, r,
792                       (ndr_push_flags_fn_t)ndr_push_eventlog_Record_tdb);
793         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
794                 tdb_unlock_bystring(tdb, EVT_NEXT_RECORD);
795                 return ndr_map_error2ntstatus(ndr_err);
796         }
797
798         /* increment the record count */
799
800         kbuf.dsize = sizeof(int32_t);
801         kbuf.dptr = (uint8_t *)&r->record_number;
802
803         ebuf.dsize = blob.length;
804         ebuf.dptr  = blob.data;
805
806         ret = tdb_store(tdb, kbuf, ebuf, 0);
807         if (ret == -1) {
808                 tdb_unlock_bystring(tdb, EVT_NEXT_RECORD);
809                 return NT_STATUS_EVENTLOG_FILE_CORRUPT;
810         }
811
812         ret = tdb_store_int32(tdb, EVT_NEXT_RECORD, r->record_number + 1);
813         if (ret == -1) {
814                 tdb_unlock_bystring(tdb, EVT_NEXT_RECORD);
815                 return NT_STATUS_EVENTLOG_FILE_CORRUPT;
816         }
817         tdb_unlock_bystring(tdb, EVT_NEXT_RECORD);
818
819         if (record_number) {
820                 *record_number = r->record_number;
821         }
822
823         return NT_STATUS_OK;
824 }
825
826 /********************************************************************
827  ********************************************************************/
828
829 NTSTATUS evlog_push_record(TALLOC_CTX *mem_ctx,
830                            TDB_CONTEXT *tdb,
831                            struct EVENTLOGRECORD *r,
832                            uint32_t *record_number)
833 {
834         struct eventlog_Record_tdb *t;
835         NTSTATUS status;
836
837         t = talloc_zero(mem_ctx, struct eventlog_Record_tdb);
838         if (!t) {
839                 return NT_STATUS_NO_MEMORY;
840         }
841
842         status = evlog_evt_entry_to_tdb_entry(t, r, t);
843         if (!NT_STATUS_IS_OK(status)) {
844                 talloc_free(t);
845                 return status;
846         }
847
848         status = evlog_push_record_tdb(mem_ctx, tdb, t, record_number);
849         talloc_free(t);
850
851         return status;
852 }
853
854 /********************************************************************
855  ********************************************************************/
856
857 NTSTATUS evlog_evt_entry_to_tdb_entry(TALLOC_CTX *mem_ctx,
858                                       const struct EVENTLOGRECORD *e,
859                                       struct eventlog_Record_tdb *t)
860 {
861         uint32_t i;
862
863         ZERO_STRUCTP(t);
864
865         t->size                         = e->Length;
866         t->reserved                     = e->Reserved;
867         t->record_number                = e->RecordNumber;
868         t->time_generated               = e->TimeGenerated;
869         t->time_written                 = e->TimeWritten;
870         t->event_id                     = e->EventID;
871         t->event_type                   = e->EventType;
872         t->num_of_strings               = e->NumStrings;
873         t->event_category               = e->EventCategory;
874         t->reserved_flags               = e->ReservedFlags;
875         t->closing_record_number        = e->ClosingRecordNumber;
876
877         t->stringoffset                 = e->StringOffset;
878         t->sid_length                   = e->UserSidLength;
879         t->sid_offset                   = e->UserSidOffset;
880         t->data_length                  = e->DataLength;
881         t->data_offset                  = e->DataOffset;
882
883         t->source_name_len              = 2 * strlen_m_term(e->SourceName);
884         t->source_name                  = talloc_strdup(mem_ctx, e->SourceName);
885         NT_STATUS_HAVE_NO_MEMORY(t->source_name);
886
887         t->computer_name_len            = 2 * strlen_m_term(e->Computername);
888         t->computer_name                = talloc_strdup(mem_ctx, e->Computername);
889         NT_STATUS_HAVE_NO_MEMORY(t->computer_name);
890
891         /* t->sid_padding; */
892         if (e->UserSidLength > 0) {
893                 const char *sid_str = NULL;
894                 smb_ucs2_t *dummy = NULL;
895                 sid_str = sid_string_talloc(mem_ctx, &e->UserSid);
896                 t->sid_length = rpcstr_push_talloc(mem_ctx, &dummy, sid_str);
897                 if (t->sid_length == -1) {
898                         return NT_STATUS_NO_MEMORY;
899                 }
900                 t->sid = data_blob_talloc(mem_ctx, (uint8_t *)dummy, t->sid_length);
901                 NT_STATUS_HAVE_NO_MEMORY(t->sid.data);
902         }
903
904         t->strings                      = talloc_array(mem_ctx, const char *, e->NumStrings);
905         for (i=0; i < e->NumStrings; i++) {
906                 t->strings[i]           = talloc_strdup(t->strings, e->Strings[i]);
907                 NT_STATUS_HAVE_NO_MEMORY(t->strings[i]);
908         }
909
910         t->strings_len                  = 2 * ndr_size_string_array(t->strings, t->num_of_strings, LIBNDR_FLAG_STR_NULLTERM);
911         t->data                         = data_blob_talloc(mem_ctx, e->Data, e->DataLength);
912         /* t->padding                   = r->Pad; */
913
914         return NT_STATUS_OK;
915 }
916
917 /********************************************************************
918  ********************************************************************/
919
920 NTSTATUS evlog_tdb_entry_to_evt_entry(TALLOC_CTX *mem_ctx,
921                                       const struct eventlog_Record_tdb *t,
922                                       struct EVENTLOGRECORD *e)
923 {
924         uint32_t i;
925
926         ZERO_STRUCTP(e);
927
928         e->Length               = t->size;
929         e->Reserved             = t->reserved;
930         e->RecordNumber         = t->record_number;
931         e->TimeGenerated        = t->time_generated;
932         e->TimeWritten          = t->time_written;
933         e->EventID              = t->event_id;
934         e->EventType            = t->event_type;
935         e->NumStrings           = t->num_of_strings;
936         e->EventCategory        = t->event_category;
937         e->ReservedFlags        = t->reserved_flags;
938         e->ClosingRecordNumber  = t->closing_record_number;
939
940         e->StringOffset         = t->stringoffset;
941         e->UserSidLength        = t->sid_length;
942         e->UserSidOffset        = t->sid_offset;
943         e->DataLength           = t->data_length;
944         e->DataOffset           = t->data_offset;
945
946         e->SourceName           = talloc_strdup(mem_ctx, t->source_name);
947         NT_STATUS_HAVE_NO_MEMORY(e->SourceName);
948
949         e->Computername         = talloc_strdup(mem_ctx, t->computer_name);
950         NT_STATUS_HAVE_NO_MEMORY(e->Computername);
951
952         if (t->sid_length > 0) {
953                 const char *sid_str = NULL;
954                 size_t len;
955                 if (!convert_string_talloc(mem_ctx, CH_UTF16, CH_UNIX,
956                                            t->sid.data, t->sid.length,
957                                            (void *)&sid_str, &len)) {
958                         return NT_STATUS_INVALID_SID;
959                 }
960                 if (len > 0) {
961                         string_to_sid(&e->UserSid, sid_str);
962                 }
963         }
964
965         e->Strings              = talloc_array(mem_ctx, const char *, t->num_of_strings);
966         for (i=0; i < t->num_of_strings; i++) {
967                 e->Strings[i] = talloc_strdup(e->Strings, t->strings[i]);
968                 NT_STATUS_HAVE_NO_MEMORY(e->Strings[i]);
969         }
970
971         e->Data                 = (uint8_t *)talloc_memdup(mem_ctx, t->data.data, t->data_length);
972         e->Pad                  = talloc_strdup(mem_ctx, "");
973         NT_STATUS_HAVE_NO_MEMORY(e->Pad);
974
975         e->Length2              = t->size;
976
977         return NT_STATUS_OK;
978 }
979
980 /********************************************************************
981  ********************************************************************/
982
983 NTSTATUS evlog_convert_tdb_to_evt(TALLOC_CTX *mem_ctx,
984                                   ELOG_TDB *etdb,
985                                   DATA_BLOB *blob_p,
986                                   uint32_t *num_records_p)
987 {
988         NTSTATUS status = NT_STATUS_OK;
989         enum ndr_err_code ndr_err;
990         DATA_BLOB blob;
991         uint32_t num_records = 0;
992         struct EVENTLOG_EVT_FILE evt;
993         uint32_t count = 1;
994         size_t endoffset = 0;
995
996         ZERO_STRUCT(evt);
997
998         while (1) {
999
1000                 struct eventlog_Record_tdb *r;
1001                 struct EVENTLOGRECORD e;
1002
1003                 r = evlog_pull_record_tdb(mem_ctx, etdb->tdb, count);
1004                 if (!r) {
1005                         break;
1006                 }
1007
1008                 status = evlog_tdb_entry_to_evt_entry(mem_ctx, r, &e);
1009                 if (!NT_STATUS_IS_OK(status)) {
1010                         goto done;
1011                 }
1012
1013                 endoffset += ndr_size_EVENTLOGRECORD(&e, 0);
1014
1015                 ADD_TO_ARRAY(mem_ctx, struct EVENTLOGRECORD, e, &evt.records, &num_records);
1016                 count++;
1017         }
1018
1019         evt.hdr.StartOffset             = 0x30;
1020         evt.hdr.EndOffset               = evt.hdr.StartOffset + endoffset;
1021         evt.hdr.CurrentRecordNumber     = count;
1022         evt.hdr.OldestRecordNumber      = 1;
1023         evt.hdr.MaxSize                 = tdb_fetch_int32(etdb->tdb, EVT_MAXSIZE);
1024         evt.hdr.Flags                   = 0;
1025         evt.hdr.Retention               = tdb_fetch_int32(etdb->tdb, EVT_RETENTION);
1026
1027         if (DEBUGLEVEL >= 10) {
1028                 NDR_PRINT_DEBUG(EVENTLOGHEADER, &evt.hdr);
1029         }
1030
1031         evt.eof.BeginRecord             = 0x30;
1032         evt.eof.EndRecord               = evt.hdr.StartOffset + endoffset;
1033         evt.eof.CurrentRecordNumber     = evt.hdr.CurrentRecordNumber;
1034         evt.eof.OldestRecordNumber      = evt.hdr.OldestRecordNumber;
1035
1036         if (DEBUGLEVEL >= 10) {
1037                 NDR_PRINT_DEBUG(EVENTLOGEOF, &evt.eof);
1038         }
1039
1040         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, &evt,
1041                    (ndr_push_flags_fn_t)ndr_push_EVENTLOG_EVT_FILE);
1042         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1043                 status = ndr_map_error2ntstatus(ndr_err);
1044                 goto done;
1045         }
1046
1047         *blob_p = blob;
1048         *num_records_p = num_records;
1049
1050  done:
1051         return status;
1052 }