r11060: merging new eventlog code from trunk
[Samba.git] / source / rpc_server / srv_eventlog_nt.c
blob577ec48482a2258b0cbe273ba5d267d2b318d2ef
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.
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.
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.
23 #include "includes.h"
25 #undef DBGC_CLASS
26 #define DBGC_CLASS DBGC_RPC_SRV
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;
37 /********************************************************************
38 ********************************************************************/
40 static void free_eventlog_info( void *ptr )
42 EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
44 if ( elog->tdb )
45 elog_close_tdb( elog->tdb );
47 TALLOC_FREE( elog );
50 /********************************************************************
51 ********************************************************************/
53 static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
54 POLICY_HND * handle )
56 EVENTLOG_INFO *info;
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;
64 return info;
67 /********************************************************************
68 ********************************************************************/
70 static BOOL elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
72 char *tdbname = elog_tdbname( info->logname );
73 SEC_DESC *sec_desc;
74 BOOL ret;
75 NTSTATUS ntstatus;
77 if ( !tdbname )
78 return False;
80 /* get the security descriptor for the file */
82 sec_desc = get_nt_acl_no_snum( info, tdbname );
83 SAFE_FREE( tdbname );
85 if ( !sec_desc ) {
86 DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n",
87 tdbname));
88 return False;
91 /* run the check, try for the max allowed */
93 ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
94 &info->access_granted, &ntstatus );
96 if ( sec_desc )
97 TALLOC_FREE( sec_desc );
99 if ( !ret ) {
100 DEBUG(8,("elog_check_access: se_access_check() return %s\n",
101 nt_errstr( ntstatus)));
102 return False;
105 /* we have to have READ permission for a successful open */
107 return ( info->access_granted & SA_RIGHT_FILE_READ_DATA );
110 /********************************************************************
111 ********************************************************************/
113 static BOOL elog_validate_logname( const char *name )
115 int i;
116 const char **elogs = lp_eventlog_list();
118 for ( i=0; elogs[i]; i++ ) {
119 if ( strequal( name, elogs[i] ) )
120 return True;
123 return False;
126 /********************************************************************
127 ********************************************************************/
129 static WERROR elog_open( pipes_struct * p, const char *logname, POLICY_HND *hnd )
131 EVENTLOG_INFO *elog;
133 /* first thing is to validate the eventlog name */
135 if ( !elog_validate_logname( logname ) )
136 return WERR_OBJECT_PATH_INVALID;
138 if ( !(elog = TALLOC_ZERO_P( NULL, EVENTLOG_INFO )) )
139 return WERR_NOMEM;
141 elog->logname = talloc_strdup( elog, logname );
143 /* Open the tdb first (so that we can create any new tdbs if necessary).
144 We have to do this as root and then use an internal access check
145 on the file permissions since you can only have a tdb open once
146 in a single process */
148 become_root();
149 elog->tdb = elog_open_tdb( elog->logname );
150 unbecome_root();
152 if ( !elog->tdb ) {
153 /* according to MSDN, if the logfile cannot be found, we should
154 default to the "Application" log */
156 if ( !strequal( logname, ELOG_APPL ) ) {
158 TALLOC_FREE( elog->logname );
160 elog->logname = talloc_strdup( elog, ELOG_APPL );
162 /* do the access check */
163 if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
164 TALLOC_FREE( elog );
165 return WERR_ACCESS_DENIED;
168 become_root();
169 elog->tdb = elog_open_tdb( elog->logname );
170 unbecome_root();
173 if ( !elog->tdb ) {
174 TALLOC_FREE( elog );
175 return WERR_ACCESS_DENIED; /* ??? */
179 /* now do the access check. Close the tdb if we fail here */
181 if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
182 elog_close_tdb( elog->tdb );
183 TALLOC_FREE( elog );
184 return WERR_ACCESS_DENIED;
187 /* create the policy handle */
189 if ( !create_policy_hnd
190 ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
191 free_eventlog_info( elog );
192 return WERR_NOMEM;
195 return WERR_OK;
198 /********************************************************************
199 ********************************************************************/
201 static WERROR elog_close( pipes_struct *p, POLICY_HND *hnd )
203 if ( !( close_policy_hnd( p, hnd ) ) ) {
204 return WERR_BADFID;
207 return WERR_OK;
210 /*******************************************************************
211 *******************************************************************/
213 static int elog_size( EVENTLOG_INFO *info )
215 if ( !info || !info->tdb ) {
216 DEBUG(0,("elog_size: Invalid info* structure!\n"));
217 return 0;
220 return elog_tdb_size( info->tdb, NULL, NULL );
223 /********************************************************************
224 For the given tdb, get the next eventlog record into the passed
225 Eventlog_entry. returns NULL if it can't get the record for some reason.
226 ********************************************************************/
228 Eventlog_entry *get_eventlog_record( prs_struct * ps, TDB_CONTEXT * tdb,
229 int recno, Eventlog_entry * ee )
231 TDB_DATA ret, key;
233 int srecno;
234 int reclen;
235 int len;
236 uint8 *rbuff;
238 pstring *wpsource, *wpcomputer, *wpsid, *wpstrs, *puserdata;
240 key.dsize = sizeof( int32 );
241 rbuff = NULL;
243 srecno = recno;
244 key.dptr = ( char * ) &srecno;
246 ret = tdb_fetch( tdb, key );
248 if ( ret.dsize == 0 ) {
249 DEBUG( 8,
250 ( "Can't find a record for the key, record %d\n",
251 recno ) );
252 return NULL;
255 len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
257 DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
259 if ( !len )
260 return NULL;
262 /* ee = PRS_ALLOC_MEM(ps, Eventlog_entry, 1); */
264 if ( !ee )
265 return NULL;
267 len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
268 &ee->record.length, &ee->record.reserved1,
269 &ee->record.record_number,
270 &ee->record.time_generated,
271 &ee->record.time_written, &ee->record.event_id,
272 &ee->record.event_type, &ee->record.num_strings,
273 &ee->record.event_category, &ee->record.reserved2,
274 &ee->record.closing_record_number,
275 &ee->record.string_offset,
276 &ee->record.user_sid_length,
277 &ee->record.user_sid_offset,
278 &ee->record.data_length, &ee->record.data_offset,
279 &ee->data_record.source_name_len, &wpsource,
280 &ee->data_record.computer_name_len, &wpcomputer,
281 &ee->data_record.sid_padding,
282 &ee->record.user_sid_length, &wpsid,
283 &ee->data_record.strings_len, &wpstrs,
284 &ee->data_record.user_data_len, &puserdata,
285 &ee->data_record.data_padding );
286 DEBUG( 10,
287 ( "Read record %d, len in tdb was %d\n",
288 ee->record.record_number, len ) );
290 /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
291 into it's 2nd argment for 'B' */
293 if ( wpcomputer )
294 memcpy( ee->data_record.computer_name, wpcomputer,
295 ee->data_record.computer_name_len );
296 if ( wpsource )
297 memcpy( ee->data_record.source_name, wpsource,
298 ee->data_record.source_name_len );
300 if ( wpsid )
301 memcpy( ee->data_record.sid, wpsid,
302 ee->record.user_sid_length );
303 if ( wpstrs )
304 memcpy( ee->data_record.strings, wpstrs,
305 ee->data_record.strings_len );
307 /* note that userdata is a pstring */
308 if ( puserdata )
309 memcpy( ee->data_record.user_data, puserdata,
310 ee->data_record.user_data_len );
312 SAFE_FREE( wpcomputer );
313 SAFE_FREE( wpsource );
314 SAFE_FREE( wpsid );
315 SAFE_FREE( wpstrs );
316 SAFE_FREE( puserdata );
318 DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
319 DEBUG( 10,
320 ( "get_eventlog_record: computer_name %d is ",
321 ee->data_record.computer_name_len ) );
322 SAFE_FREE( ret.dptr );
323 return ee;
326 /********************************************************************
327 note that this can only be called AFTER the table is constructed,
328 since it uses the table to find the tdb handle
329 ********************************************************************/
331 static BOOL sync_eventlog_params( EVENTLOG_INFO *info )
333 pstring path;
334 uint32 uiMaxSize;
335 uint32 uiRetention;
336 REGISTRY_KEY *keyinfo;
337 REGISTRY_VALUE *val;
338 REGVAL_CTR *values;
339 WERROR wresult;
340 char *elogname = info->logname;
342 DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
344 if ( !info->tdb ) {
345 DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
346 return False;
348 /* set resonable defaults. 512Kb on size and 1 week on time */
350 uiMaxSize = 0x80000;
351 uiRetention = 604800;
353 /* the general idea is to internally open the registry
354 key and retreive the values. That way we can continue
355 to use the same fetch/store api that we use in
356 srv_reg_nt.c */
358 pstr_sprintf( path, "%s/%s", KEY_EVENTLOG, elogname );
360 wresult =
361 regkey_open_internal( &keyinfo, path, get_root_nt_token( ),
362 REG_KEY_READ );
364 if ( !W_ERROR_IS_OK( wresult ) ) {
365 DEBUG( 4,
366 ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
367 path, dos_errstr( wresult ) ) );
368 return False;
371 if ( !( values = TALLOC_ZERO_P( keyinfo, REGVAL_CTR ) ) ) {
372 TALLOC_FREE( keyinfo );
373 DEBUG( 0, ( "control_eventlog_hook: talloc() failed!\n" ) );
375 return False;
377 fetch_reg_values( keyinfo, values );
379 if ( ( val = regval_ctr_getvalue( values, "Retention" ) ) != NULL )
380 uiRetention = IVAL( regval_data_p( val ), 0 );
382 if ( ( val = regval_ctr_getvalue( values, "MaxSize" ) ) != NULL )
383 uiMaxSize = IVAL( regval_data_p( val ), 0 );
385 regkey_close_internal( keyinfo );
387 tdb_store_int32( info->tdb, EVT_MAXSIZE, uiMaxSize );
388 tdb_store_int32( info->tdb, EVT_RETENTION, uiRetention );
390 return True;
393 /********************************************************************
394 ********************************************************************/
396 static BOOL get_num_records_hook( EVENTLOG_INFO * info )
398 int next_record;
399 int oldest_record;
401 if ( !info->tdb ) {
402 DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
403 return False;
406 /* lock the tdb since we have to get 2 records */
408 tdb_lock_bystring( info->tdb, EVT_NEXT_RECORD, 1 );
409 next_record = tdb_fetch_int32( info->tdb, EVT_NEXT_RECORD);
410 oldest_record = tdb_fetch_int32( info->tdb, EVT_OLDEST_ENTRY);
411 tdb_unlock_bystring( info->tdb, EVT_NEXT_RECORD);
413 DEBUG( 8,
414 ( "Oldest Record %d; Next Record %d\n", oldest_record,
415 next_record ) );
417 info->num_records = ( next_record - oldest_record );
418 info->oldest_entry = oldest_record;
420 return True;
423 /********************************************************************
424 ********************************************************************/
426 static BOOL get_oldest_entry_hook( EVENTLOG_INFO * info )
429 /* it's the same thing */
430 return get_num_records_hook( info );
433 /********************************************************************
434 ********************************************************************/
436 static Eventlog_entry *read_package_entry( prs_struct * ps,
437 EVENTLOG_Q_READ_EVENTLOG * q_u,
438 EVENTLOG_R_READ_EVENTLOG * r_u,
439 Eventlog_entry * entry )
441 uint8 *offset;
442 Eventlog_entry *ee_new = NULL;
444 ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
445 if ( ee_new == NULL ) {
446 return NULL;
449 entry->data_record.sid_padding =
450 ( ( 4 -
451 ( ( entry->data_record.source_name_len +
452 entry->data_record.computer_name_len ) % 4 ) ) % 4 );
453 entry->data_record.data_padding =
454 ( 4 -
455 ( ( entry->data_record.strings_len +
456 entry->data_record.user_data_len ) % 4 ) ) % 4;
457 entry->record.length = sizeof( Eventlog_record );
458 entry->record.length += entry->data_record.source_name_len;
459 entry->record.length += entry->data_record.computer_name_len;
460 if ( entry->record.user_sid_length == 0 ) {
461 /* Should not pad to a DWORD boundary for writing out the sid if there is
462 no SID, so just propagate the padding to pad the data */
463 entry->data_record.data_padding +=
464 entry->data_record.sid_padding;
465 entry->data_record.sid_padding = 0;
467 DEBUG( 10,
468 ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );
469 DEBUG( 10,
470 ( "data_padding is [%d].\n",
471 entry->data_record.data_padding ) );
473 entry->record.length += entry->data_record.sid_padding;
474 entry->record.length += entry->record.user_sid_length;
475 entry->record.length += entry->data_record.strings_len;
476 entry->record.length += entry->data_record.user_data_len;
477 entry->record.length += entry->data_record.data_padding;
478 /* need another copy of length at the end of the data */
479 entry->record.length += sizeof( entry->record.length );
480 DEBUG( 10,
481 ( "entry->record.length is [%d].\n", entry->record.length ) );
482 entry->data =
483 PRS_ALLOC_MEM( ps, uint8,
484 entry->record.length -
485 sizeof( Eventlog_record ) -
486 sizeof( entry->record.length ) );
487 if ( entry->data == NULL ) {
488 return NULL;
490 offset = entry->data;
491 memcpy( offset, &( entry->data_record.source_name ),
492 entry->data_record.source_name_len );
493 offset += entry->data_record.source_name_len;
494 memcpy( offset, &( entry->data_record.computer_name ),
495 entry->data_record.computer_name_len );
496 offset += entry->data_record.computer_name_len;
497 /* SID needs to be DWORD-aligned */
498 offset += entry->data_record.sid_padding;
499 entry->record.user_sid_offset =
500 sizeof( Eventlog_record ) + ( offset - entry->data );
501 memcpy( offset, &( entry->data_record.sid ),
502 entry->record.user_sid_length );
503 offset += entry->record.user_sid_length;
504 /* Now do the strings */
505 entry->record.string_offset =
506 sizeof( Eventlog_record ) + ( offset - entry->data );
507 memcpy( offset, &( entry->data_record.strings ),
508 entry->data_record.strings_len );
509 offset += entry->data_record.strings_len;
510 /* Now do the data */
511 entry->record.data_length = entry->data_record.user_data_len;
512 entry->record.data_offset =
513 sizeof( Eventlog_record ) + ( offset - entry->data );
514 memcpy( offset, &( entry->data_record.user_data ),
515 entry->data_record.user_data_len );
516 offset += entry->data_record.user_data_len;
518 memcpy( &( ee_new->record ), &entry->record,
519 sizeof( Eventlog_record ) );
520 memcpy( &( ee_new->data_record ), &entry->data_record,
521 sizeof( Eventlog_data_record ) );
522 ee_new->data = entry->data;
524 return ee_new;
527 /********************************************************************
528 ********************************************************************/
530 static BOOL add_record_to_resp( EVENTLOG_R_READ_EVENTLOG * r_u,
531 Eventlog_entry * ee_new )
533 Eventlog_entry *insert_point;
535 insert_point = r_u->entry;
537 if ( NULL == insert_point ) {
538 r_u->entry = ee_new;
539 ee_new->next = NULL;
540 } else {
541 while ( ( NULL != insert_point->next ) ) {
542 insert_point = insert_point->next;
544 ee_new->next = NULL;
545 insert_point->next = ee_new;
547 r_u->num_records++;
548 r_u->num_bytes_in_resp += ee_new->record.length;
550 return True;
553 /********************************************************************
554 ********************************************************************/
556 WERROR _eventlog_open_eventlog( pipes_struct * p,
557 EVENTLOG_Q_OPEN_EVENTLOG * q_u,
558 EVENTLOG_R_OPEN_EVENTLOG * r_u )
560 fstring servername, logname;
561 EVENTLOG_INFO *info;
562 WERROR wresult;
564 fstrcpy( servername, "" );
565 if ( q_u->servername.string ) {
566 rpcstr_pull( servername, q_u->servername.string->buffer,
567 sizeof( servername ),
568 q_u->servername.string->uni_str_len * 2, 0 );
571 fstrcpy( logname, "" );
572 if ( q_u->logname.string ) {
573 rpcstr_pull( logname, q_u->logname.string->buffer,
574 sizeof( logname ),
575 q_u->logname.string->uni_str_len * 2, 0 );
578 DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
579 servername, logname ));
581 /* according to MSDN, if the logfile cannot be found, we should
582 default to the "Application" log */
584 if ( !W_ERROR_IS_OK( wresult = elog_open( p, logname, &r_u->handle )) )
585 return wresult;
587 if ( !(info = find_eventlog_info_by_hnd( p, &r_u->handle )) ) {
588 DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
589 logname ));
590 elog_close( p, &r_u->handle );
591 return WERR_BADFID;
594 DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info )));
596 sync_eventlog_params( info );
597 prune_eventlog( info->tdb );
599 return WERR_OK;
602 /********************************************************************
603 This call still needs some work
604 ********************************************************************/
606 WERROR _eventlog_clear_eventlog( pipes_struct * p,
607 EVENTLOG_Q_CLEAR_EVENTLOG * q_u,
608 EVENTLOG_R_CLEAR_EVENTLOG * r_u )
610 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
611 pstring backup_file_name;
613 if ( !info )
614 return WERR_BADFID;
616 pstrcpy( backup_file_name, "" );
617 if ( q_u->backupfile.string ) {
618 rpcstr_pull( backup_file_name, q_u->backupfile.string->buffer,
619 sizeof( backup_file_name ),
620 q_u->backupfile.string->uni_str_len * 2, 0 );
623 DEBUG( 8,
624 ( "_eventlog_clear_eventlog: Using [%s] as the backup file name for log [%s].",
625 backup_file_name, info->logname ) );
627 #if 0
628 /* close the current one, reinit */
630 tdb_close( info->tdb );
632 if ( !(info->tdb = elog_init_tdb( ttdb[i].tdbfname )) )
633 return WERR_ACCESS_DENIED;
634 #endif
636 return WERR_OK;
639 /********************************************************************
640 ********************************************************************/
642 WERROR _eventlog_close_eventlog( pipes_struct * p,
643 EVENTLOG_Q_CLOSE_EVENTLOG * q_u,
644 EVENTLOG_R_CLOSE_EVENTLOG * r_u )
646 return elog_close( p, &q_u->handle );
649 /********************************************************************
650 ********************************************************************/
652 WERROR _eventlog_read_eventlog( pipes_struct * p,
653 EVENTLOG_Q_READ_EVENTLOG * q_u,
654 EVENTLOG_R_READ_EVENTLOG * r_u )
656 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
657 Eventlog_entry entry, *ee_new;
659 uint32 num_records_read = 0;
660 prs_struct *ps;
661 int bytes_left, record_number;
662 TDB_CONTEXT *tdb;
664 info->flags = q_u->flags;
665 ps = &p->out_data.rdata;
667 bytes_left = q_u->max_read_size;
668 tdb = info->tdb;
669 if ( !tdb ) {
670 return WERR_EVENTLOG_FILE_CORRUPT;
673 /* DEBUG(8,("Bytes left is %d\n",bytes_left)); */
675 record_number = q_u->offset;
677 while ( bytes_left > 0 ) {
678 if ( get_eventlog_record
679 ( ps, tdb, record_number, &entry ) ) {
680 DEBUG( 8,
681 ( "Retrieved record %d\n", record_number ) );
683 /* Now see if there is enough room to add */
684 ee_new = read_package_entry( ps, q_u, r_u,&entry );
685 if ( !ee_new )
686 return WERR_NOMEM;
688 if ( r_u->num_bytes_in_resp + ee_new->record.length >
689 q_u->max_read_size ) {
690 r_u->bytes_in_next_record =
691 ee_new->record.length;
693 /* response would be too big to fit in client-size buffer */
695 bytes_left = 0;
696 break;
699 add_record_to_resp( r_u, ee_new );
700 bytes_left -= ee_new->record.length;
701 ZERO_STRUCT( entry );
702 num_records_read =
703 r_u->num_records - num_records_read;
705 DEBUG( 10,
706 ( "_eventlog_read_eventlog: read [%d] records for a total of [%d] records using [%d] bytes out of a max of [%d].\n",
707 num_records_read, r_u->num_records,
708 r_u->num_bytes_in_resp,
709 q_u->max_read_size ) );
710 } else {
711 DEBUG( 8, ( "get_eventlog_record returned NULL\n" ) );
712 return WERR_NOMEM; /* wrong error - but return one anyway */
716 if ( info->flags & EVENTLOG_FORWARDS_READ )
717 record_number++;
718 else
719 record_number--;
722 return WERR_OK;
725 /********************************************************************
726 ********************************************************************/
728 WERROR _eventlog_get_oldest_entry( pipes_struct * p,
729 EVENTLOG_Q_GET_OLDEST_ENTRY * q_u,
730 EVENTLOG_R_GET_OLDEST_ENTRY * r_u )
732 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
734 if ( !( get_oldest_entry_hook( info ) ) )
735 return WERR_BADFILE;
737 r_u->oldest_entry = info->oldest_entry;
739 return WERR_OK;
742 /********************************************************************
743 ********************************************************************/
745 WERROR _eventlog_get_num_records( pipes_struct * p,
746 EVENTLOG_Q_GET_NUM_RECORDS * q_u,
747 EVENTLOG_R_GET_NUM_RECORDS * r_u )
749 EVENTLOG_INFO *info = find_eventlog_info_by_hnd( p, &q_u->handle );
751 if ( !( get_num_records_hook( info ) ) )
752 return WERR_BADFILE;
754 r_u->num_records = info->num_records;
756 return WERR_OK;