summaryrefslogtreecommitdiff
path: root/source3/rpc_server/srv_eventlog_nt.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/rpc_server/srv_eventlog_nt.c')
-rw-r--r--source3/rpc_server/srv_eventlog_nt.c968
1 files changed, 968 insertions, 0 deletions
diff --git a/source3/rpc_server/srv_eventlog_nt.c b/source3/rpc_server/srv_eventlog_nt.c
new file mode 100644
index 0000000000..0e2bcf4126
--- /dev/null
+++ b/source3/rpc_server/srv_eventlog_nt.c
@@ -0,0 +1,968 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Marcin Krzysztof Porwit 2005,
+ * Copyright (C) Brian Moran 2005,
+ * Copyright (C) Gerald (Jerry) Carter 2005.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_RPC_SRV
+
+typedef struct {
+ char *logname;
+ ELOG_TDB *etdb;
+ uint32 current_record;
+ uint32 num_records;
+ uint32 oldest_entry;
+ uint32 flags;
+ uint32 access_granted;
+} EVENTLOG_INFO;
+
+/********************************************************************
+ ********************************************************************/
+
+static void free_eventlog_info( void *ptr )
+{
+ EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
+
+ if ( elog->etdb )
+ elog_close_tdb( elog->etdb, False );
+
+ TALLOC_FREE( elog );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
+ POLICY_HND * handle )
+{
+ EVENTLOG_INFO *info;
+
+ if ( !find_policy_by_hnd( p, handle, (void **)(void *)&info ) ) {
+ DEBUG( 2,
+ ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
+ return NULL;
+ }
+
+ return info;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
+{
+ char *tdbname = elog_tdbname(talloc_tos(), info->logname );
+ SEC_DESC *sec_desc;
+ bool ret;
+ NTSTATUS ntstatus;
+
+ if ( !tdbname )
+ return False;
+
+ /* get the security descriptor for the file */
+
+ sec_desc = get_nt_acl_no_snum( info, tdbname );
+ SAFE_FREE( tdbname );
+
+ if ( !sec_desc ) {
+ DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n",
+ tdbname));
+ return False;
+ }
+
+ /* root free pass */
+
+ if ( geteuid() == sec_initial_uid() ) {
+ DEBUG(5,("elog_check_access: using root's token\n"));
+ token = get_root_nt_token();
+ }
+
+ /* run the check, try for the max allowed */
+
+ ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
+ &info->access_granted, &ntstatus );
+
+ if ( sec_desc )
+ TALLOC_FREE( sec_desc );
+
+ if ( !ret ) {
+ DEBUG(8,("elog_check_access: se_access_check() return %s\n",
+ nt_errstr( ntstatus)));
+ return False;
+ }
+
+ /* we have to have READ permission for a successful open */
+
+ return ( info->access_granted & SA_RIGHT_FILE_READ_DATA );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static bool elog_validate_logname( const char *name )
+{
+ int i;
+ const char **elogs = lp_eventlog_list();
+
+ if (!elogs) {
+ return False;
+ }
+
+ for ( i=0; elogs[i]; i++ ) {
+ if ( strequal( name, elogs[i] ) )
+ return True;
+ }
+
+ return False;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool get_num_records_hook( EVENTLOG_INFO * info )
+{
+ int next_record;
+ int oldest_record;
+
+ if ( !info->etdb ) {
+ DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
+ return False;
+ }
+
+ /* lock the tdb since we have to get 2 records */
+
+ tdb_lock_bystring_with_timeout( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD, 1 );
+ next_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
+ oldest_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_OLDEST_ENTRY);
+ tdb_unlock_bystring( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
+
+ DEBUG( 8,
+ ( "Oldest Record %d; Next Record %d\n", oldest_record,
+ next_record ) );
+
+ info->num_records = ( next_record - oldest_record );
+ info->oldest_entry = oldest_record;
+
+ return True;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static bool get_oldest_entry_hook( EVENTLOG_INFO * info )
+{
+ /* it's the same thing */
+ return get_num_records_hook( info );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static NTSTATUS elog_open( pipes_struct * p, const char *logname, POLICY_HND *hnd )
+{
+ EVENTLOG_INFO *elog;
+
+ /* first thing is to validate the eventlog name */
+
+ if ( !elog_validate_logname( logname ) )
+ return NT_STATUS_OBJECT_PATH_INVALID;
+
+ if ( !(elog = TALLOC_ZERO_P( NULL, EVENTLOG_INFO )) )
+ return NT_STATUS_NO_MEMORY;
+
+ elog->logname = talloc_strdup( elog, logname );
+
+ /* Open the tdb first (so that we can create any new tdbs if necessary).
+ We have to do this as root and then use an internal access check
+ on the file permissions since you can only have a tdb open once
+ in a single process */
+
+ become_root();
+ elog->etdb = elog_open_tdb( elog->logname, False );
+ unbecome_root();
+
+ if ( !elog->etdb ) {
+ /* according to MSDN, if the logfile cannot be found, we should
+ default to the "Application" log */
+
+ if ( !strequal( logname, ELOG_APPL ) ) {
+
+ TALLOC_FREE( elog->logname );
+
+ elog->logname = talloc_strdup( elog, ELOG_APPL );
+
+ /* do the access check */
+ if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ become_root();
+ elog->etdb = elog_open_tdb( elog->logname, False );
+ unbecome_root();
+ }
+
+ if ( !elog->etdb ) {
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED; /* ??? */
+ }
+ }
+
+ /* now do the access check. Close the tdb if we fail here */
+
+ if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
+ elog_close_tdb( elog->etdb, False );
+ TALLOC_FREE( elog );
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* create the policy handle */
+
+ if ( !create_policy_hnd
+ ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
+ free_eventlog_info( elog );
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* set the initial current_record pointer */
+
+ if ( !get_oldest_entry_hook( elog ) ) {
+ DEBUG(3,("elog_open: Successfully opened eventlog but can't "
+ "get any information on internal records!\n"));
+ }
+
+ elog->current_record = elog->oldest_entry;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static NTSTATUS elog_close( pipes_struct *p, POLICY_HND *hnd )
+{
+ if ( !( close_policy_hnd( p, hnd ) ) ) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ *******************************************************************/
+
+static int elog_size( EVENTLOG_INFO *info )
+{
+ if ( !info || !info->etdb ) {
+ DEBUG(0,("elog_size: Invalid info* structure!\n"));
+ return 0;
+ }
+
+ return elog_tdb_size( ELOG_TDB_CTX(info->etdb), NULL, NULL );
+}
+
+/********************************************************************
+ For the given tdb, get the next eventlog record into the passed
+ Eventlog_entry. returns NULL if it can't get the record for some reason.
+ ********************************************************************/
+
+static Eventlog_entry *get_eventlog_record(prs_struct *ps,
+ TDB_CONTEXT *tdb,
+ int recno)
+{
+ Eventlog_entry *ee = NULL;
+ TDB_DATA ret, key;
+
+ int srecno;
+ int reclen;
+ int len;
+
+ char *wpsource = NULL;
+ char *wpcomputer = NULL;
+ char *wpsid = NULL;
+ char *wpstrs = NULL;
+ char *puserdata = NULL;
+
+ key.dsize = sizeof(int32);
+
+ srecno = recno;
+ key.dptr = ( uint8 * ) &srecno;
+
+ ret = tdb_fetch( tdb, key );
+
+ if ( ret.dsize == 0 ) {
+ DEBUG( 8,
+ ( "Can't find a record for the key, record %d\n",
+ recno ) );
+ return NULL;
+ }
+
+ len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
+
+ DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
+
+ if ( !len )
+ return NULL;
+
+ ee = TALLOC_ARRAY(ps->mem_ctx, Eventlog_entry, 1);
+ if (!ee) {
+ return NULL;
+ }
+ ZERO_STRUCTP(ee);
+
+ len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
+ &ee->record.length, &ee->record.reserved1,
+ &ee->record.record_number,
+ &ee->record.time_generated,
+ &ee->record.time_written, &ee->record.event_id,
+ &ee->record.event_type, &ee->record.num_strings,
+ &ee->record.event_category, &ee->record.reserved2,
+ &ee->record.closing_record_number,
+ &ee->record.string_offset,
+ &ee->record.user_sid_length,
+ &ee->record.user_sid_offset,
+ &ee->record.data_length, &ee->record.data_offset,
+ &ee->data_record.source_name_len, &wpsource,
+ &ee->data_record.computer_name_len, &wpcomputer,
+ &ee->data_record.sid_padding,
+ &ee->record.user_sid_length, &wpsid,
+ &ee->data_record.strings_len, &wpstrs,
+ &ee->data_record.user_data_len, &puserdata,
+ &ee->data_record.data_padding );
+ DEBUG( 10,
+ ( "Read record %d, len in tdb was %d\n",
+ ee->record.record_number, len ) );
+
+ /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
+ into it's 2nd argment for 'B' */
+
+ if (wpcomputer) {
+ ee->data_record.computer_name = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpcomputer, ee->data_record.computer_name_len);
+ if (!ee->data_record.computer_name) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+ if (wpsource) {
+ ee->data_record.source_name = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpsource, ee->data_record.source_name_len);
+ if (!ee->data_record.source_name) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+
+ if (wpsid) {
+ ee->data_record.sid = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpsid, ee->record.user_sid_length);
+ if (!ee->data_record.sid) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+ if (wpstrs) {
+ ee->data_record.strings = (smb_ucs2_t *)TALLOC_MEMDUP(
+ ee, wpstrs, ee->data_record.strings_len);
+ if (!ee->data_record.strings) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+
+ if (puserdata) {
+ ee->data_record.user_data = (char *)TALLOC_MEMDUP(
+ ee, puserdata, ee->data_record.user_data_len);
+ if (!ee->data_record.user_data) {
+ TALLOC_FREE(ee);
+ goto out;
+ }
+ }
+
+ out:
+
+ SAFE_FREE(wpcomputer);
+ SAFE_FREE(wpsource);
+ SAFE_FREE(wpsid);
+ SAFE_FREE(wpstrs);
+ SAFE_FREE(puserdata);
+
+ DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
+ DEBUG( 10,
+ ( "get_eventlog_record: computer_name %d is ",
+ ee->data_record.computer_name_len ) );
+ SAFE_FREE(ret.dptr);
+ return ee;
+}
+
+/********************************************************************
+ note that this can only be called AFTER the table is constructed,
+ since it uses the table to find the tdb handle
+ ********************************************************************/
+
+static bool sync_eventlog_params( EVENTLOG_INFO *info )
+{
+ char *path = NULL;
+ uint32 uiMaxSize;
+ uint32 uiRetention;
+ struct registry_key *key;
+ struct registry_value *value;
+ WERROR wresult;
+ char *elogname = info->logname;
+ TALLOC_CTX *ctx = talloc_tos();
+ bool ret = false;
+
+ DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
+
+ if ( !info->etdb ) {
+ DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
+ return False;
+ }
+ /* set resonable defaults. 512Kb on size and 1 week on time */
+
+ uiMaxSize = 0x80000;
+ uiRetention = 604800;
+
+ /* the general idea is to internally open the registry
+ key and retrieve the values. That way we can continue
+ to use the same fetch/store api that we use in
+ srv_reg_nt.c */
+
+ path = talloc_asprintf(ctx, "%s/%s", KEY_EVENTLOG, elogname );
+ if (!path) {
+ return false;
+ }
+
+ wresult = reg_open_path(ctx, path, REG_KEY_READ, get_root_nt_token(),
+ &key);
+
+ if ( !W_ERROR_IS_OK( wresult ) ) {
+ DEBUG( 4,
+ ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
+ path, dos_errstr( wresult ) ) );
+ return false;
+ }
+
+ wresult = reg_queryvalue(key, key, "Retention", &value);
+ if (!W_ERROR_IS_OK(wresult)) {
+ DEBUG(4, ("Failed to query value \"Retention\": %s\n",
+ dos_errstr(wresult)));
+ ret = false;
+ goto done;
+ }
+ uiRetention = value->v.dword;
+
+ wresult = reg_queryvalue(key, key, "MaxSize", &value);
+ if (!W_ERROR_IS_OK(wresult)) {
+ DEBUG(4, ("Failed to query value \"MaxSize\": %s\n",
+ dos_errstr(wresult)));
+ ret = false;
+ goto done;
+ }
+ uiMaxSize = value->v.dword;
+
+ tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_MAXSIZE, uiMaxSize );
+ tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_RETENTION, uiRetention );
+
+ ret = true;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static Eventlog_entry *read_package_entry( prs_struct * ps,
+ Eventlog_entry * entry )
+{
+ uint8 *offset;
+ Eventlog_entry *ee_new = NULL;
+
+ ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
+ if ( ee_new == NULL ) {
+ return NULL;
+ }
+
+ entry->data_record.sid_padding =
+ ( ( 4 -
+ ( ( entry->data_record.source_name_len +
+ entry->data_record.computer_name_len ) % 4 ) ) % 4 );
+ entry->data_record.data_padding =
+ ( 4 -
+ ( ( entry->data_record.strings_len +
+ entry->data_record.user_data_len ) % 4 ) ) % 4;
+ entry->record.length = sizeof( Eventlog_record );
+ entry->record.length += entry->data_record.source_name_len;
+ entry->record.length += entry->data_record.computer_name_len;
+ if ( entry->record.user_sid_length == 0 ) {
+ /* Should not pad to a DWORD boundary for writing out the sid if there is
+ no SID, so just propagate the padding to pad the data */
+ entry->data_record.data_padding +=
+ entry->data_record.sid_padding;
+ entry->data_record.sid_padding = 0;
+ }
+ DEBUG( 10,
+ ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );
+ DEBUG( 10,
+ ( "data_padding is [%d].\n",
+ entry->data_record.data_padding ) );
+
+ entry->record.length += entry->data_record.sid_padding;
+ entry->record.length += entry->record.user_sid_length;
+ entry->record.length += entry->data_record.strings_len;
+ entry->record.length += entry->data_record.user_data_len;
+ entry->record.length += entry->data_record.data_padding;
+ /* need another copy of length at the end of the data */
+ entry->record.length += sizeof( entry->record.length );
+ DEBUG( 10,
+ ( "entry->record.length is [%d].\n", entry->record.length ) );
+ entry->data =
+ PRS_ALLOC_MEM( ps, uint8,
+ entry->record.length -
+ sizeof( Eventlog_record ) -
+ sizeof( entry->record.length ) );
+ if ( entry->data == NULL ) {
+ return NULL;
+ }
+ offset = entry->data;
+ memcpy( offset, &( entry->data_record.source_name ),
+ entry->data_record.source_name_len );
+ offset += entry->data_record.source_name_len;
+ memcpy( offset, &( entry->data_record.computer_name ),
+ entry->data_record.computer_name_len );
+ offset += entry->data_record.computer_name_len;
+ /* SID needs to be DWORD-aligned */
+ offset += entry->data_record.sid_padding;
+ entry->record.user_sid_offset =
+ sizeof( Eventlog_record ) + ( offset - entry->data );
+ memcpy( offset, &( entry->data_record.sid ),
+ entry->record.user_sid_length );
+ offset += entry->record.user_sid_length;
+ /* Now do the strings */
+ entry->record.string_offset =
+ sizeof( Eventlog_record ) + ( offset - entry->data );
+ memcpy( offset, &( entry->data_record.strings ),
+ entry->data_record.strings_len );
+ offset += entry->data_record.strings_len;
+ /* Now do the data */
+ entry->record.data_length = entry->data_record.user_data_len;
+ entry->record.data_offset =
+ sizeof( Eventlog_record ) + ( offset - entry->data );
+ memcpy( offset, &( entry->data_record.user_data ),
+ entry->data_record.user_data_len );
+ offset += entry->data_record.user_data_len;
+
+ memcpy( &( ee_new->record ), &entry->record,
+ sizeof( Eventlog_record ) );
+ memcpy( &( ee_new->data_record ), &entry->data_record,
+ sizeof( Eventlog_data_record ) );
+ ee_new->data = entry->data;
+
+ return ee_new;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static bool add_record_to_resp( EVENTLOG_R_READ_EVENTLOG * r_u,
+ Eventlog_entry * ee_new )
+{
+ Eventlog_entry *insert_point;
+
+ insert_point = r_u->entry;
+
+ if ( NULL == insert_point ) {
+ r_u->entry = ee_new;
+ ee_new->next = NULL;
+ } else {
+ while ( ( NULL != insert_point->next ) ) {
+ insert_point = insert_point->next;
+ }
+ ee_new->next = NULL;
+ insert_point->next = ee_new;
+ }
+ r_u->num_records++;
+ r_u->num_bytes_in_resp += ee_new->record.length;
+
+ return True;
+}
+
+/********************************************************************
+ _eventlog_OpenEventLogW
+ ********************************************************************/
+
+NTSTATUS _eventlog_OpenEventLogW(pipes_struct *p,
+ struct eventlog_OpenEventLogW *r)
+{
+ const char *servername = "";
+ const char *logname = "";
+ EVENTLOG_INFO *info;
+ NTSTATUS result;
+
+ if (r->in.servername->string) {
+ servername = r->in.servername->string;
+ }
+
+ if (r->in.logname->string) {
+ logname = r->in.logname->string;
+ }
+
+ DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
+ servername, logname ));
+
+ /* according to MSDN, if the logfile cannot be found, we should
+ default to the "Application" log */
+
+ if ( !NT_STATUS_IS_OK( result = elog_open( p, logname, r->out.handle )) )
+ return result;
+
+ if ( !(info = find_eventlog_info_by_hnd( p, r->out.handle )) ) {
+ DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
+ logname ));
+ elog_close( p, r->out.handle );
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info )));
+
+ sync_eventlog_params( info );
+ prune_eventlog( ELOG_TDB_CTX(info->etdb) );
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ _eventlog_ClearEventLogW
+ This call still needs some work
+ ********************************************************************/
+/** The windows client seems to be doing something funny with the file name
+ A call like
+ ClearEventLog(handle, "backup_file")
+ on the client side will result in the backup file name looking like this on the
+ server side:
+ \??\${CWD of client}\backup_file
+ If an absolute path gets specified, such as
+ ClearEventLog(handle, "C:\\temp\\backup_file")
+ then it is still mangled by the client into this:
+ \??\C:\temp\backup_file
+ when it is on the wire.
+ I'm not sure where the \?? is coming from, or why the ${CWD} of the client process
+ would be added in given that the backup file gets written on the server side. */
+
+NTSTATUS _eventlog_ClearEventLogW(pipes_struct *p,
+ struct eventlog_ClearEventLogW *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+ const char *backup_file_name = NULL;
+
+ if ( !info )
+ return NT_STATUS_INVALID_HANDLE;
+
+ if (r->in.backupfile && r->in.backupfile->string) {
+
+ backup_file_name = r->in.backupfile->string;
+
+ DEBUG(8,( "_eventlog_clear_eventlog: Using [%s] as the backup "
+ "file name for log [%s].",
+ backup_file_name, info->logname ) );
+ }
+
+ /* check for WRITE access to the file */
+
+ if ( !(info->access_granted&SA_RIGHT_FILE_WRITE_DATA) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* Force a close and reopen */
+
+ elog_close_tdb( info->etdb, True );
+ become_root();
+ info->etdb = elog_open_tdb( info->logname, True );
+ unbecome_root();
+
+ if ( !info->etdb )
+ return NT_STATUS_ACCESS_DENIED;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+NTSTATUS _eventlog_CloseEventLog( pipes_struct * p, struct eventlog_CloseEventLog *r )
+{
+ return elog_close( p, r->in.handle );
+}
+
+/********************************************************************
+ ********************************************************************/
+
+NTSTATUS _eventlog_read_eventlog( pipes_struct * p,
+ EVENTLOG_Q_READ_EVENTLOG * q_u,
+ EVENTLOG_R_READ_EVENTLOG * r_u )
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
+ Eventlog_entry *entry = NULL, *ee_new = NULL;
+ uint32 num_records_read = 0;
+ prs_struct *ps;
+ int bytes_left, record_number;
+ uint32 elog_read_type, elog_read_dir;
+
+ if (info == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ info->flags = q_u->flags;
+ ps = &p->out_data.rdata;
+
+ bytes_left = q_u->max_read_size;
+
+ if ( !info->etdb )
+ return NT_STATUS_ACCESS_DENIED;
+
+ /* check for valid flags. Can't use the sequential and seek flags together */
+
+ elog_read_type = q_u->flags & (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ);
+ elog_read_dir = q_u->flags & (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ);
+
+ if ( elog_read_type == (EVENTLOG_SEQUENTIAL_READ|EVENTLOG_SEEK_READ)
+ || elog_read_dir == (EVENTLOG_FORWARDS_READ|EVENTLOG_BACKWARDS_READ) )
+ {
+ DEBUG(3,("_eventlog_read_eventlog: Invalid flags [0x%x] for ReadEventLog\n", q_u->flags));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* a sequential read should ignore the offset */
+
+ if ( elog_read_type & EVENTLOG_SEQUENTIAL_READ )
+ record_number = info->current_record;
+ else
+ record_number = q_u->offset;
+
+ while ( bytes_left > 0 ) {
+
+ /* assume that when the record fetch fails, that we are done */
+
+ entry = get_eventlog_record (ps, ELOG_TDB_CTX(info->etdb), record_number);
+ if (!entry) {
+ break;
+ }
+
+ DEBUG( 8, ( "Retrieved record %d\n", record_number ) );
+
+ /* Now see if there is enough room to add */
+
+ if ( !(ee_new = read_package_entry( ps, entry )) )
+ return NT_STATUS_NO_MEMORY;
+
+ if ( r_u->num_bytes_in_resp + ee_new->record.length > q_u->max_read_size ) {
+ r_u->bytes_in_next_record = ee_new->record.length;
+
+ /* response would be too big to fit in client-size buffer */
+
+ bytes_left = 0;
+ break;
+ }
+
+ add_record_to_resp( r_u, ee_new );
+ bytes_left -= ee_new->record.length;
+ TALLOC_FREE(entry);
+ num_records_read = r_u->num_records - num_records_read;
+
+ DEBUG( 10, ( "_eventlog_read_eventlog: read [%d] records for a total "
+ "of [%d] records using [%d] bytes out of a max of [%d].\n",
+ num_records_read, r_u->num_records,
+ r_u->num_bytes_in_resp,
+ q_u->max_read_size ) );
+
+ if ( info->flags & EVENTLOG_FORWARDS_READ )
+ record_number++;
+ else
+ record_number--;
+
+ /* update the eventlog record pointer */
+
+ info->current_record = record_number;
+ }
+
+ /* crazy by WinXP uses NT_STATUS_BUFFER_TOO_SMALL to
+ say when there are no more records */
+
+ return (num_records_read ? NT_STATUS_OK : NT_STATUS_BUFFER_TOO_SMALL);
+}
+
+/********************************************************************
+ _eventlog_GetOldestRecord
+ ********************************************************************/
+
+NTSTATUS _eventlog_GetOldestRecord(pipes_struct *p,
+ struct eventlog_GetOldestRecord *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+
+ if (info == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if ( !( get_oldest_entry_hook( info ) ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ *r->out.oldest_entry = info->oldest_entry;
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+_eventlog_GetNumRecords
+ ********************************************************************/
+
+NTSTATUS _eventlog_GetNumRecords(pipes_struct *p,
+ struct eventlog_GetNumRecords *r)
+{
+ EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, r->in.handle );
+
+ if (info == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if ( !( get_num_records_hook( info ) ) )
+ return NT_STATUS_ACCESS_DENIED;
+
+ *r->out.number = info->num_records;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS _eventlog_BackupEventLogW(pipes_struct *p, struct eventlog_BackupEventLogW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_DeregisterEventSource(pipes_struct *p, struct eventlog_DeregisterEventSource *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ChangeNotify(pipes_struct *p, struct eventlog_ChangeNotify *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterEventSourceW(pipes_struct *p, struct eventlog_RegisterEventSourceW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenBackupEventLogW(pipes_struct *p, struct eventlog_OpenBackupEventLogW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReadEventLogW(pipes_struct *p, struct eventlog_ReadEventLogW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReportEventW(pipes_struct *p, struct eventlog_ReportEventW *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ClearEventLogA(pipes_struct *p, struct eventlog_ClearEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_BackupEventLogA(pipes_struct *p, struct eventlog_BackupEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenEventLogA(pipes_struct *p, struct eventlog_OpenEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterEventSourceA(pipes_struct *p, struct eventlog_RegisterEventSourceA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_OpenBackupEventLogA(pipes_struct *p, struct eventlog_OpenBackupEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReadEventLogA(pipes_struct *p, struct eventlog_ReadEventLogA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_ReportEventA(pipes_struct *p, struct eventlog_ReportEventA *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_RegisterClusterSvc(pipes_struct *p, struct eventlog_RegisterClusterSvc *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_DeregisterClusterSvc(pipes_struct *p, struct eventlog_DeregisterClusterSvc *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_WriteClusterEvents(pipes_struct *p, struct eventlog_WriteClusterEvents *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_GetLogIntormation(pipes_struct *p, struct eventlog_GetLogIntormation *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+
+NTSTATUS _eventlog_FlushEventLog(pipes_struct *p, struct eventlog_FlushEventLog *r)
+{
+ p->rng_fault_state = True;
+ return NT_STATUS_NOT_IMPLEMENTED;
+}
+