r19778: Make regkey_open_internal take a talloc ctx
[kai/samba.git] / source3 / rpc_server / srv_eventlog_nt.c
1 /* 
2  *  Unix SMB/CIFS implementation.
3  *  RPC Pipe client / server routines
4  *  Copyright (C) Marcin Krzysztof Porwit    2005,
5  *  Copyright (C) Brian Moran                2005,
6  *  Copyright (C) Gerald (Jerry) Carter      2005.
7  *  
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.
12  *  
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.
17  *  
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.
21  */
22
23 #include "includes.h"
24
25 #undef  DBGC_CLASS
26 #define DBGC_CLASS DBGC_RPC_SRV
27
28 typedef struct {
29         char *logname;
30         ELOG_TDB *etdb;
31         uint32 current_record;
32         uint32 num_records;
33         uint32 oldest_entry;
34         uint32 flags;
35         uint32 access_granted;
36 } EVENTLOG_INFO;
37
38 /********************************************************************
39  ********************************************************************/
40
41 static void free_eventlog_info( void *ptr )
42 {
43         EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
44         
45         if ( elog->etdb )
46                 elog_close_tdb( elog->etdb, False );
47         
48         TALLOC_FREE( elog );
49 }
50
51 /********************************************************************
52  ********************************************************************/
53
54 static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
55                                                 POLICY_HND * handle )
56 {
57         EVENTLOG_INFO *info;
58
59         if ( !find_policy_by_hnd( p, handle, (void **)(void *)&info ) ) {
60                 DEBUG( 2,
61                        ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
62                 return NULL;
63         }
64
65         return info;
66 }
67
68 /********************************************************************
69 ********************************************************************/
70
71 static BOOL elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
72 {
73         char *tdbname = elog_tdbname( info->logname );
74         SEC_DESC *sec_desc;
75         BOOL ret;
76         NTSTATUS ntstatus;
77         
78         if ( !tdbname ) 
79                 return False;
80         
81         /* get the security descriptor for the file */
82         
83         sec_desc = get_nt_acl_no_snum( info, tdbname );
84         SAFE_FREE( tdbname );
85         
86         if ( !sec_desc ) {
87                 DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n", 
88                         tdbname));
89                 return False;
90         }
91         
92         /* root free pass */
93
94         if ( geteuid() == sec_initial_uid() ) {
95                 DEBUG(5,("elog_check_access: using root's token\n"));
96                 token = get_root_nt_token();
97         }
98
99         /* run the check, try for the max allowed */
100         
101         ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
102                 &info->access_granted, &ntstatus );
103                 
104         if ( sec_desc )
105                 TALLOC_FREE( sec_desc );
106                 
107         if ( !ret ) {
108                 DEBUG(8,("elog_check_access: se_access_check() return %s\n",
109                         nt_errstr( ntstatus)));
110                 return False;
111         }
112         
113         /* we have to have READ permission for a successful open */
114         
115         return ( info->access_granted & SA_RIGHT_FILE_READ_DATA );
116 }
117
118 /********************************************************************
119  ********************************************************************/
120
121 static BOOL elog_validate_logname( const char *name )
122 {
123         int i;
124         const char **elogs = lp_eventlog_list();
125         
126         for ( i=0; elogs[i]; i++ ) {
127                 if ( strequal( name, elogs[i] ) )
128                         return True;
129         }
130         
131         return False;
132 }
133
134 /********************************************************************
135 ********************************************************************/
136
137 static BOOL get_num_records_hook( EVENTLOG_INFO * info )
138 {
139         int next_record;
140         int oldest_record;
141
142         if ( !info->etdb ) {
143                 DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
144                 return False;
145         }
146
147         /* lock the tdb since we have to get 2 records */
148
149         tdb_lock_bystring_with_timeout( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD, 1 );
150         next_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
151         oldest_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_OLDEST_ENTRY);
152         tdb_unlock_bystring( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
153
154         DEBUG( 8,
155                ( "Oldest Record %d; Next Record %d\n", oldest_record,
156                  next_record ) );
157
158         info->num_records = ( next_record - oldest_record );
159         info->oldest_entry = oldest_record;
160
161         return True;
162 }
163
164 /********************************************************************
165  ********************************************************************/
166
167 static BOOL get_oldest_entry_hook( EVENTLOG_INFO * info )
168 {
169         /* it's the same thing */
170         return get_num_records_hook( info );
171 }
172
173 /********************************************************************
174  ********************************************************************/
175
176 static NTSTATUS elog_open( pipes_struct * p, const char *logname, POLICY_HND *hnd )
177 {
178         EVENTLOG_INFO *elog;
179         
180         /* first thing is to validate the eventlog name */
181         
182         if ( !elog_validate_logname( logname ) )
183                 return NT_STATUS_OBJECT_PATH_INVALID;
184         
185         if ( !(elog = TALLOC_ZERO_P( NULL, EVENTLOG_INFO )) )
186                 return NT_STATUS_NO_MEMORY;
187                 
188         elog->logname = talloc_strdup( elog, logname );
189         
190         /* Open the tdb first (so that we can create any new tdbs if necessary).
191            We have to do this as root and then use an internal access check 
192            on the file permissions since you can only have a tdb open once
193            in a single process */
194
195         become_root();
196         elog->etdb = elog_open_tdb( elog->logname, False );
197         unbecome_root();
198
199         if ( !elog->etdb ) {
200                 /* according to MSDN, if the logfile cannot be found, we should
201                   default to the "Application" log */
202         
203                 if ( !strequal( logname, ELOG_APPL ) ) {
204                 
205                         TALLOC_FREE( elog->logname );
206                         
207                         elog->logname = talloc_strdup( elog, ELOG_APPL );                       
208
209                         /* do the access check */
210                         if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
211                                 TALLOC_FREE( elog );
212                                 return NT_STATUS_ACCESS_DENIED;
213                         }
214         
215                         become_root();
216                         elog->etdb = elog_open_tdb( elog->logname, False );
217                         unbecome_root();
218                 }       
219                 
220                 if ( !elog->etdb ) {
221                         TALLOC_FREE( elog );
222                         return NT_STATUS_ACCESS_DENIED; /* ??? */               
223                 }
224         }
225         
226         /* now do the access check.  Close the tdb if we fail here */
227
228         if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
229                 elog_close_tdb( elog->etdb, False );
230                 TALLOC_FREE( elog );
231                 return NT_STATUS_ACCESS_DENIED;
232         }
233         
234         /* create the policy handle */
235         
236         if ( !create_policy_hnd
237              ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
238                 free_eventlog_info( elog );
239                 return NT_STATUS_NO_MEMORY;
240         }
241
242         /* set the initial current_record pointer */
243
244         if ( !get_oldest_entry_hook( elog ) ) {
245                 DEBUG(3,("elog_open: Successfully opened eventlog but can't "
246                         "get any information on internal records!\n"));
247         }       
248
249         elog->current_record = elog->oldest_entry;
250
251         return NT_STATUS_OK;
252 }
253
254 /********************************************************************
255  ********************************************************************/
256
257 static NTSTATUS elog_close( pipes_struct *p, POLICY_HND *hnd )
258 {
259         if ( !( close_policy_hnd( p, hnd ) ) ) {
260                 return NT_STATUS_INVALID_HANDLE;
261         }
262
263         return NT_STATUS_OK;
264 }
265
266 /*******************************************************************
267  *******************************************************************/
268
269 static int elog_size( EVENTLOG_INFO *info )
270 {
271         if ( !info || !info->etdb ) {
272                 DEBUG(0,("elog_size: Invalid info* structure!\n"));
273                 return 0;
274         }
275
276         return elog_tdb_size( ELOG_TDB_CTX(info->etdb), NULL, NULL );
277 }
278
279 /********************************************************************
280   For the given tdb, get the next eventlog record into the passed 
281   Eventlog_entry.  returns NULL if it can't get the record for some reason.
282  ********************************************************************/
283
284 Eventlog_entry *get_eventlog_record( prs_struct * ps, TDB_CONTEXT * tdb,
285                                      int recno, Eventlog_entry * ee )
286 {
287         TDB_DATA ret, key;
288
289         int srecno;
290         int reclen;
291         int len;
292
293         pstring *wpsource, *wpcomputer, *wpsid, *wpstrs, *puserdata;
294
295         key.dsize = sizeof( int32 );
296
297         srecno = recno;
298         key.dptr = ( char * ) &srecno;
299
300         ret = tdb_fetch( tdb, key );
301
302         if ( ret.dsize == 0 ) {
303                 DEBUG( 8,
304                        ( "Can't find a record for the key, record %d\n",
305                          recno ) );
306                 return NULL;
307         }
308
309         len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
310
311         DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
312
313         if ( !len )
314                 return NULL;
315
316         /* ee = PRS_ALLOC_MEM(ps, Eventlog_entry, 1); */
317
318         if ( !ee )
319                 return NULL;
320
321         len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
322                           &ee->record.length, &ee->record.reserved1,
323                           &ee->record.record_number,
324                           &ee->record.time_generated,
325                           &ee->record.time_written, &ee->record.event_id,
326                           &ee->record.event_type, &ee->record.num_strings,
327                           &ee->record.event_category, &ee->record.reserved2,
328                           &ee->record.closing_record_number,
329                           &ee->record.string_offset,
330                           &ee->record.user_sid_length,
331                           &ee->record.user_sid_offset,
332                           &ee->record.data_length, &ee->record.data_offset,
333                           &ee->data_record.source_name_len, &wpsource,
334                           &ee->data_record.computer_name_len, &wpcomputer,
335                           &ee->data_record.sid_padding,
336                           &ee->record.user_sid_length, &wpsid,
337                           &ee->data_record.strings_len, &wpstrs,
338                           &ee->data_record.user_data_len, &puserdata,
339                           &ee->data_record.data_padding );
340         DEBUG( 10,
341                ( "Read record %d, len in tdb was %d\n",
342                  ee->record.record_number, len ) );
343
344         /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
345            into it's 2nd argment for 'B' */
346
347         if ( wpcomputer )
348                 memcpy( ee->data_record.computer_name, wpcomputer,
349                         ee->data_record.computer_name_len );
350         if ( wpsource )
351                 memcpy( ee->data_record.source_name, wpsource,
352                         ee->data_record.source_name_len );
353
354         if ( wpsid )
355                 memcpy( ee->data_record.sid, wpsid,
356                         ee->record.user_sid_length );
357         if ( wpstrs )
358                 memcpy( ee->data_record.strings, wpstrs,
359                         ee->data_record.strings_len );
360
361         /* note that userdata is a pstring */
362         if ( puserdata )
363                 memcpy( ee->data_record.user_data, puserdata,
364                         ee->data_record.user_data_len );
365
366         SAFE_FREE( wpcomputer );
367         SAFE_FREE( wpsource );
368         SAFE_FREE( wpsid );
369         SAFE_FREE( wpstrs );
370         SAFE_FREE( puserdata );
371
372         DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
373         DEBUG( 10,
374                ( "get_eventlog_record: computer_name %d is ",
375                  ee->data_record.computer_name_len ) );
376         SAFE_FREE( ret.dptr );
377         return ee;
378 }
379
380 /********************************************************************
381  note that this can only be called AFTER the table is constructed, 
382  since it uses the table to find the tdb handle
383  ********************************************************************/
384
385 static BOOL sync_eventlog_params( EVENTLOG_INFO *info )
386 {
387         pstring path;
388         uint32 uiMaxSize;
389         uint32 uiRetention;
390         REGISTRY_KEY *keyinfo;
391         REGISTRY_VALUE *val;
392         REGVAL_CTR *values;
393         WERROR wresult;
394         char *elogname = info->logname;
395
396         DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
397
398         if ( !info->etdb ) {
399                 DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
400                 return False;
401         }
402         /* set resonable defaults.  512Kb on size and 1 week on time */
403
404         uiMaxSize = 0x80000;
405         uiRetention = 604800;
406
407         /* the general idea is to internally open the registry 
408            key and retrieve the values.  That way we can continue 
409            to use the same fetch/store api that we use in 
410            srv_reg_nt.c */
411
412         pstr_sprintf( path, "%s/%s", KEY_EVENTLOG, elogname );
413
414         wresult = regkey_open_internal(
415                 NULL, &keyinfo, path, get_root_nt_token(),  REG_KEY_READ );
416
417         if ( !W_ERROR_IS_OK( wresult ) ) {
418                 DEBUG( 4,
419                        ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
420                          path, dos_errstr( wresult ) ) );
421                 return False;
422         }
423
424         if ( !( values = TALLOC_ZERO_P( keyinfo, REGVAL_CTR ) ) ) {
425                 TALLOC_FREE( keyinfo );
426                 DEBUG( 0, ( "control_eventlog_hook: talloc() failed!\n" ) );
427
428                 return False;
429         }
430         fetch_reg_values( keyinfo, values );
431
432         if ( ( val = regval_ctr_getvalue( values, "Retention" ) ) != NULL )
433                 uiRetention = IVAL( regval_data_p( val ), 0 );
434
435         if ( ( val = regval_ctr_getvalue( values, "MaxSize" ) ) != NULL )
436                 uiMaxSize = IVAL( regval_data_p( val ), 0 );
437
438         regkey_close_internal( keyinfo );
439
440         tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_MAXSIZE, uiMaxSize );
441         tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_RETENTION, uiRetention );
442
443         return True;
444 }
445
446 /********************************************************************
447  ********************************************************************/
448
449 static Eventlog_entry *read_package_entry( prs_struct * ps,
450                                            EVENTLOG_Q_READ_EVENTLOG * q_u,
451                                            EVENTLOG_R_READ_EVENTLOG * r_u,
452                                            Eventlog_entry * entry )
453 {
454         uint8 *offset;
455         Eventlog_entry *ee_new = NULL;
456
457         ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
458         if ( ee_new == NULL ) {
459                 return NULL;
460         }
461
462         entry->data_record.sid_padding =
463                 ( ( 4 -
464                     ( ( entry->data_record.source_name_len +
465                         entry->data_record.computer_name_len ) % 4 ) ) % 4 );
466         entry->data_record.data_padding =
467                 ( 4 -
468                   ( ( entry->data_record.strings_len +
469                       entry->data_record.user_data_len ) % 4 ) ) % 4;
470         entry->record.length = sizeof( Eventlog_record );
471         entry->record.length += entry->data_record.source_name_len;
472         entry->record.length += entry->data_record.computer_name_len;
473         if ( entry->record.user_sid_length == 0 ) {
474                 /* Should not pad to a DWORD boundary for writing out the sid if there is
475                    no SID, so just propagate the padding to pad the data */
476                 entry->data_record.data_padding +=
477                         entry->data_record.sid_padding;
478                 entry->data_record.sid_padding = 0;
479         }
480         DEBUG( 10,
481                ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );
482         DEBUG( 10,
483                ( "data_padding is [%d].\n",
484                  entry->data_record.data_padding ) );
485
486         entry->record.length += entry->data_record.sid_padding;
487         entry->record.length += entry->record.user_sid_length;
488         entry->record.length += entry->data_record.strings_len;
489         entry->record.length += entry->data_record.user_data_len;
490         entry->record.length += entry->data_record.data_padding;
491         /* need another copy of length at the end of the data */
492         entry->record.length += sizeof( entry->record.length );
493         DEBUG( 10,
494                ( "entry->record.length is [%d].\n", entry->record.length ) );
495         entry->data =
496                 PRS_ALLOC_MEM( ps, uint8,
497                                entry->record.length -
498                                sizeof( Eventlog_record ) -
499                                sizeof( entry->record.length ) );
500         if ( entry->data == NULL ) {
501                 return NULL;
502         }
503         offset = entry->data;
504         memcpy( offset, &( entry->data_record.source_name ),
505                 entry->data_record.source_name_len );
506         offset += entry->data_record.source_name_len;
507         memcpy( offset, &( entry->data_record.computer_name ),
508                 entry->data_record.computer_name_len );
509         offset += entry->data_record.computer_name_len;
510         /* SID needs to be DWORD-aligned */
511         offset += entry->data_record.sid_padding;
512         entry->record.user_sid_offset =
513                 sizeof( Eventlog_record ) + ( offset - entry->data );
514         memcpy( offset, &( entry->data_record.sid ),
515                 entry->record.user_sid_length );
516         offset += entry->record.user_sid_length;
517         /* Now do the strings */
518         entry->record.string_offset =
519                 sizeof( Eventlog_record ) + ( offset - entry->data );
520         memcpy( offset, &( entry->data_record.strings ),
521                 entry->data_record.strings_len );
522         offset += entry->data_record.strings_len;
523         /* Now do the data */
524         entry->record.data_length = entry->data_record.user_data_len;
525         entry->record.data_offset =
526                 sizeof( Eventlog_record ) + ( offset - entry->data );
527         memcpy( offset, &( entry->data_record.user_data ),
528                 entry->data_record.user_data_len );
529         offset += entry->data_record.user_data_len;
530
531         memcpy( &( ee_new->record ), &entry->record,
532                 sizeof( Eventlog_record ) );
533         memcpy( &( ee_new->data_record ), &entry->data_record,
534                 sizeof( Eventlog_data_record ) );
535         ee_new->data = entry->data;
536
537         return ee_new;
538 }
539
540 /********************************************************************
541  ********************************************************************/
542
543 static BOOL add_record_to_resp( EVENTLOG_R_READ_EVENTLOG * r_u,
544                                 Eventlog_entry * ee_new )
545 {
546         Eventlog_entry *insert_point;
547
548         insert_point = r_u->entry;
549
550         if ( NULL == insert_point ) {
551                 r_u->entry = ee_new;
552                 ee_new->next = NULL;
553         } else {
554                 while ( ( NULL != insert_point->next ) ) {
555                         insert_point = insert_point->next;
556                 }
557                 ee_new->next = NULL;
558                 insert_point->next = ee_new;
559         }
560         r_u->num_records++;
561         r_u->num_bytes_in_resp += ee_new->record.length;
562
563         return True;
564 }
565
566 /********************************************************************
567  ********************************************************************/
568
569 NTSTATUS _eventlog_open_eventlog( pipes_struct * p,
570                                 EVENTLOG_Q_OPEN_EVENTLOG * q_u,
571                                 EVENTLOG_R_OPEN_EVENTLOG * r_u )
572 {
573         fstring servername, logname;
574         EVENTLOG_INFO *info;
575         NTSTATUS result;
576
577         fstrcpy( servername, "" );
578         if ( q_u->servername.string ) {
579                 rpcstr_pull( servername, q_u->servername.string->buffer,
580                              sizeof( servername ),
581                              q_u->servername.string->uni_str_len * 2, 0 );
582         }
583
584         fstrcpy( logname, "" );
585         if ( q_u->logname.string ) {
586                 rpcstr_pull( logname, q_u->logname.string->buffer,
587                              sizeof( logname ),
588                              q_u->logname.string->uni_str_len * 2, 0 );
589         }
590         
591         DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
592                 servername, logname ));
593                 
594         /* according to MSDN, if the logfile cannot be found, we should
595           default to the "Application" log */
596           
597         if ( !NT_STATUS_IS_OK( result = elog_open( p, logname, &r_u->handle )) )
598                 return result;
599
600         if ( !(info = find_eventlog_info_by_hnd( p, &r_u->handle )) ) {
601                 DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
602                         logname ));
603                 elog_close( p, &r_u->handle );
604                 return NT_STATUS_INVALID_HANDLE;
605         }
606
607         DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info )));
608
609         sync_eventlog_params( info );
610         prune_eventlog( ELOG_TDB_CTX(info->etdb) );
611
612         return NT_STATUS_OK;
613 }
614
615 /********************************************************************
616  This call still needs some work
617  ********************************************************************/
618
619 NTSTATUS _eventlog_clear_eventlog( pipes_struct * p,
620                                  EVENTLOG_Q_CLEAR_EVENTLOG * q_u,
621                                  EVENTLOG_R_CLEAR_EVENTLOG * r_u )
622 {
623         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
624         pstring backup_file_name;
625
626         if ( !info )
627                 return NT_STATUS_INVALID_HANDLE;
628
629         pstrcpy( backup_file_name, "" );
630         if ( q_u->backupfile.string ) {
631                 rpcstr_pull( backup_file_name, q_u->backupfile.string->buffer,
632                              sizeof( backup_file_name ),
633                              q_u->backupfile.string->uni_str_len * 2, 0 );
634
635                 DEBUG(8,( "_eventlog_clear_eventlog: Using [%s] as the backup "
636                         "file name for log [%s].",
637                          backup_file_name, info->logname ) );
638         }
639
640         /* check for WRITE access to the file */
641
642         if ( !(info->access_granted&SA_RIGHT_FILE_WRITE_DATA) )
643                 return NT_STATUS_ACCESS_DENIED;
644
645         /* Force a close and reopen */
646
647         elog_close_tdb( info->etdb, True ); 
648         become_root();
649         info->etdb = elog_open_tdb( info->logname, True );
650         unbecome_root();
651
652         if ( !info->etdb )
653                 return NT_STATUS_ACCESS_DENIED;
654
655         return NT_STATUS_OK;
656 }
657
658 /********************************************************************
659  ********************************************************************/
660
661 NTSTATUS _eventlog_close_eventlog( pipes_struct * p,
662                                  EVENTLOG_Q_CLOSE_EVENTLOG * q_u,
663                                  EVENTLOG_R_CLOSE_EVENTLOG * r_u )
664 {
665         return elog_close( p, &q_u->handle );
666 }
667
668 /********************************************************************
669  ********************************************************************/
670
671 NTSTATUS _eventlog_read_eventlog( pipes_struct * p,
672                                 EVENTLOG_Q_READ_EVENTLOG * q_u,
673                                 EVENTLOG_R_READ_EVENTLOG * r_u )
674 {
675         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
676         Eventlog_entry entry, *ee_new;
677         uint32 num_records_read = 0;
678         prs_struct *ps;
679         int bytes_left, record_number;
680         uint32 elog_read_type, elog_read_dir;
681
682         if (info == NULL) {
683                 return NT_STATUS_INVALID_HANDLE;
684         }
685
686         info->flags = q_u->flags;
687         ps = &p->out_data.rdata;
688
689         bytes_left = q_u->max_read_size;
690
691         if ( !info->etdb ) 
692                 return NT_STATUS_ACCESS_DENIED;
693                 
694         /* check for valid flags.  Can't use the sequential and seek flags together */
695
696         elog_read_type = q_u->flags & (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ);
697         elog_read_dir = q_u->flags & (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ);
698
699         if ( elog_read_type == (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ) 
700                 ||  elog_read_dir == (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ) )
701         {
702                 DEBUG(3,("_eventlog_read_eventlog: Invalid flags [0x%x] for ReadEventLog\n", q_u->flags));
703                 return NT_STATUS_INVALID_PARAMETER;
704         }
705
706         /* a sequential read should ignore the offset */
707
708         if ( elog_read_type & EVENTLOG_SEQUENTIAL_READ )
709                 record_number = info->current_record;
710         else 
711                 record_number = q_u->offset;
712
713         while ( bytes_left > 0 ) {
714
715                 /* assume that when the record fetch fails, that we are done */
716
717                 if ( !get_eventlog_record ( ps, ELOG_TDB_CTX(info->etdb), record_number, &entry ) ) 
718                         break;
719
720                 DEBUG( 8, ( "Retrieved record %d\n", record_number ) );
721                                
722                 /* Now see if there is enough room to add */
723
724                 if ( !(ee_new = read_package_entry( ps, q_u, r_u,&entry )) )
725                         return NT_STATUS_NO_MEMORY;
726
727                 if ( r_u->num_bytes_in_resp + ee_new->record.length > q_u->max_read_size ) {
728                         r_u->bytes_in_next_record = ee_new->record.length;
729
730                         /* response would be too big to fit in client-size buffer */
731                                 
732                         bytes_left = 0;
733                         break;
734                 }
735                         
736                 add_record_to_resp( r_u, ee_new );
737                 bytes_left -= ee_new->record.length;
738                 ZERO_STRUCT( entry );
739                 num_records_read = r_u->num_records - num_records_read;
740                                 
741                 DEBUG( 10, ( "_eventlog_read_eventlog: read [%d] records for a total "
742                         "of [%d] records using [%d] bytes out of a max of [%d].\n",
743                          num_records_read, r_u->num_records,
744                          r_u->num_bytes_in_resp,
745                          q_u->max_read_size ) );
746
747                 if ( info->flags & EVENTLOG_FORWARDS_READ )
748                         record_number++;
749                 else
750                         record_number--;
751                 
752                 /* update the eventlog record pointer */
753                 
754                 info->current_record = record_number;
755         }
756
757         /* crazy by WinXP uses NT_STATUS_BUFFER_TOO_SMALL to 
758            say when there are no more records */
759
760         return (num_records_read ? NT_STATUS_OK : NT_STATUS_BUFFER_TOO_SMALL);
761 }
762
763 /********************************************************************
764  ********************************************************************/
765
766 NTSTATUS _eventlog_get_oldest_entry( pipes_struct * p,
767                                    EVENTLOG_Q_GET_OLDEST_ENTRY * q_u,
768                                    EVENTLOG_R_GET_OLDEST_ENTRY * r_u )
769 {
770         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
771
772         if (info == NULL) {
773                 return NT_STATUS_INVALID_HANDLE;
774         }
775
776         if ( !( get_oldest_entry_hook( info ) ) )
777                 return NT_STATUS_ACCESS_DENIED;
778
779         r_u->oldest_entry = info->oldest_entry;
780
781         return NT_STATUS_OK;
782 }
783
784 /********************************************************************
785  ********************************************************************/
786
787 NTSTATUS _eventlog_get_num_records( pipes_struct * p,
788                                   EVENTLOG_Q_GET_NUM_RECORDS * q_u,
789                                   EVENTLOG_R_GET_NUM_RECORDS * r_u )
790 {
791         EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
792
793         if (info == NULL) {
794                 return NT_STATUS_INVALID_HANDLE;
795         }
796
797         if ( !( get_num_records_hook( info ) ) )
798                 return NT_STATUS_ACCESS_DENIED;
799
800         r_u->num_records = info->num_records;
801
802         return NT_STATUS_OK;
803 }