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