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