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.
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.
26 #define DBGC_CLASS DBGC_RPC_SRV
34 uint32 access_granted
;
37 /********************************************************************
38 ********************************************************************/
40 static void free_eventlog_info( void *ptr
)
42 EVENTLOG_INFO
*elog
= (EVENTLOG_INFO
*)ptr
;
45 elog_close_tdb( elog
->tdb
);
50 /********************************************************************
51 ********************************************************************/
53 static EVENTLOG_INFO
*find_eventlog_info_by_hnd( pipes_struct
* p
,
58 if ( !find_policy_by_hnd( p
, handle
, ( void ** ) &info
) ) {
60 ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
67 /********************************************************************
68 ********************************************************************/
70 static BOOL
elog_check_access( EVENTLOG_INFO
*info
, NT_USER_TOKEN
*token
)
72 char *tdbname
= elog_tdbname( info
->logname
);
80 /* get the security descriptor for the file */
82 sec_desc
= get_nt_acl_no_snum( info
, tdbname
);
86 DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n",
93 if ( geteuid() == sec_initial_uid() ) {
94 DEBUG(5,("elog_check_access: using root's token\n"));
95 token
= get_root_nt_token();
98 /* run the check, try for the max allowed */
100 ret
= se_access_check( sec_desc
, token
, MAXIMUM_ALLOWED_ACCESS
,
101 &info
->access_granted
, &ntstatus
);
104 TALLOC_FREE( sec_desc
);
107 DEBUG(8,("elog_check_access: se_access_check() return %s\n",
108 nt_errstr( ntstatus
)));
112 /* we have to have READ permission for a successful open */
114 return ( info
->access_granted
& SA_RIGHT_FILE_READ_DATA
);
117 /********************************************************************
118 ********************************************************************/
120 static BOOL
elog_validate_logname( const char *name
)
123 const char **elogs
= lp_eventlog_list();
125 for ( i
=0; elogs
[i
]; i
++ ) {
126 if ( strequal( name
, elogs
[i
] ) )
133 /********************************************************************
134 ********************************************************************/
136 static NTSTATUS
elog_open( pipes_struct
* p
, const char *logname
, POLICY_HND
*hnd
)
140 /* first thing is to validate the eventlog name */
142 if ( !elog_validate_logname( logname
) )
143 return NT_STATUS_OBJECT_PATH_INVALID
;
145 if ( !(elog
= TALLOC_ZERO_P( NULL
, EVENTLOG_INFO
)) )
146 return NT_STATUS_NO_MEMORY
;
148 elog
->logname
= talloc_strdup( elog
, logname
);
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 */
156 elog
->tdb
= elog_open_tdb( elog
->logname
);
160 /* according to MSDN, if the logfile cannot be found, we should
161 default to the "Application" log */
163 if ( !strequal( logname
, ELOG_APPL
) ) {
165 TALLOC_FREE( elog
->logname
);
167 elog
->logname
= talloc_strdup( elog
, ELOG_APPL
);
169 /* do the access check */
170 if ( !elog_check_access( elog
, p
->pipe_user
.nt_user_token
) ) {
172 return NT_STATUS_ACCESS_DENIED
;
176 elog
->tdb
= elog_open_tdb( elog
->logname
);
182 return NT_STATUS_ACCESS_DENIED
; /* ??? */
186 /* now do the access check. Close the tdb if we fail here */
188 if ( !elog_check_access( elog
, p
->pipe_user
.nt_user_token
) ) {
189 elog_close_tdb( elog
->tdb
);
191 return NT_STATUS_ACCESS_DENIED
;
194 /* create the policy handle */
196 if ( !create_policy_hnd
197 ( p
, hnd
, free_eventlog_info
, ( void * ) elog
) ) {
198 free_eventlog_info( elog
);
199 return NT_STATUS_NO_MEMORY
;
205 /********************************************************************
206 ********************************************************************/
208 static NTSTATUS
elog_close( pipes_struct
*p
, POLICY_HND
*hnd
)
210 if ( !( close_policy_hnd( p
, hnd
) ) ) {
211 return NT_STATUS_INVALID_HANDLE
;
217 /*******************************************************************
218 *******************************************************************/
220 static int elog_size( EVENTLOG_INFO
*info
)
222 if ( !info
|| !info
->tdb
) {
223 DEBUG(0,("elog_size: Invalid info* structure!\n"));
227 return elog_tdb_size( info
->tdb
, NULL
, NULL
);
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 ********************************************************************/
235 Eventlog_entry
*get_eventlog_record( prs_struct
* ps
, TDB_CONTEXT
* tdb
,
236 int recno
, Eventlog_entry
* ee
)
245 pstring
*wpsource
, *wpcomputer
, *wpsid
, *wpstrs
, *puserdata
;
247 key
.dsize
= sizeof( int32
);
251 key
.dptr
= ( char * ) &srecno
;
253 ret
= tdb_fetch( tdb
, key
);
255 if ( ret
.dsize
== 0 ) {
257 ( "Can't find a record for the key, record %d\n",
262 len
= tdb_unpack( ret
.dptr
, ret
.dsize
, "d", &reclen
);
264 DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno
, len
) );
269 /* ee = PRS_ALLOC_MEM(ps, Eventlog_entry, 1); */
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
);
294 ( "Read record %d, len in tdb was %d\n",
295 ee
->record
.record_number
, len
) );
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' */
301 memcpy( ee
->data_record
.computer_name
, wpcomputer
,
302 ee
->data_record
.computer_name_len
);
304 memcpy( ee
->data_record
.source_name
, wpsource
,
305 ee
->data_record
.source_name_len
);
308 memcpy( ee
->data_record
.sid
, wpsid
,
309 ee
->record
.user_sid_length
);
311 memcpy( ee
->data_record
.strings
, wpstrs
,
312 ee
->data_record
.strings_len
);
314 /* note that userdata is a pstring */
316 memcpy( ee
->data_record
.user_data
, puserdata
,
317 ee
->data_record
.user_data_len
);
319 SAFE_FREE( wpcomputer
);
320 SAFE_FREE( wpsource
);
323 SAFE_FREE( puserdata
);
325 DEBUG( 10, ( "get_eventlog_record: read back %d\n", len
) );
327 ( "get_eventlog_record: computer_name %d is ",
328 ee
->data_record
.computer_name_len
) );
329 SAFE_FREE( ret
.dptr
);
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 ********************************************************************/
338 static BOOL
sync_eventlog_params( EVENTLOG_INFO
*info
)
343 REGISTRY_KEY
*keyinfo
;
347 char *elogname
= info
->logname
;
349 DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname
) );
352 DEBUG( 4, ( "No open tdb! (%s)\n", info
->logname
) );
355 /* set resonable defaults. 512Kb on size and 1 week on time */
358 uiRetention
= 604800;
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
365 pstr_sprintf( path
, "%s/%s", KEY_EVENTLOG
, elogname
);
368 regkey_open_internal( &keyinfo
, path
, get_root_nt_token( ),
371 if ( !W_ERROR_IS_OK( wresult
) ) {
373 ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
374 path
, dos_errstr( wresult
) ) );
378 if ( !( values
= TALLOC_ZERO_P( keyinfo
, REGVAL_CTR
) ) ) {
379 TALLOC_FREE( keyinfo
);
380 DEBUG( 0, ( "control_eventlog_hook: talloc() failed!\n" ) );
384 fetch_reg_values( keyinfo
, values
);
386 if ( ( val
= regval_ctr_getvalue( values
, "Retention" ) ) != NULL
)
387 uiRetention
= IVAL( regval_data_p( val
), 0 );
389 if ( ( val
= regval_ctr_getvalue( values
, "MaxSize" ) ) != NULL
)
390 uiMaxSize
= IVAL( regval_data_p( val
), 0 );
392 regkey_close_internal( keyinfo
);
394 tdb_store_int32( info
->tdb
, EVT_MAXSIZE
, uiMaxSize
);
395 tdb_store_int32( info
->tdb
, EVT_RETENTION
, uiRetention
);
400 /********************************************************************
401 ********************************************************************/
403 static BOOL
get_num_records_hook( EVENTLOG_INFO
* info
)
409 DEBUG( 10, ( "No open tdb for %s\n", info
->logname
) );
413 /* lock the tdb since we have to get 2 records */
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
);
421 ( "Oldest Record %d; Next Record %d\n", oldest_record
,
424 info
->num_records
= ( next_record
- oldest_record
);
425 info
->oldest_entry
= oldest_record
;
430 /********************************************************************
431 ********************************************************************/
433 static BOOL
get_oldest_entry_hook( EVENTLOG_INFO
* info
)
435 /* it's the same thing */
436 return get_num_records_hook( info
);
439 /********************************************************************
440 ********************************************************************/
442 static Eventlog_entry
*read_package_entry( prs_struct
* ps
,
443 EVENTLOG_Q_READ_EVENTLOG
* q_u
,
444 EVENTLOG_R_READ_EVENTLOG
* r_u
,
445 Eventlog_entry
* entry
)
448 Eventlog_entry
*ee_new
= NULL
;
450 ee_new
= PRS_ALLOC_MEM( ps
, Eventlog_entry
, 1 );
451 if ( ee_new
== NULL
) {
455 entry
->data_record
.sid_padding
=
457 ( ( entry
->data_record
.source_name_len
+
458 entry
->data_record
.computer_name_len
) % 4 ) ) % 4 );
459 entry
->data_record
.data_padding
=
461 ( ( entry
->data_record
.strings_len
+
462 entry
->data_record
.user_data_len
) % 4 ) ) % 4;
463 entry
->record
.length
= sizeof( Eventlog_record
);
464 entry
->record
.length
+= entry
->data_record
.source_name_len
;
465 entry
->record
.length
+= entry
->data_record
.computer_name_len
;
466 if ( entry
->record
.user_sid_length
== 0 ) {
467 /* Should not pad to a DWORD boundary for writing out the sid if there is
468 no SID, so just propagate the padding to pad the data */
469 entry
->data_record
.data_padding
+=
470 entry
->data_record
.sid_padding
;
471 entry
->data_record
.sid_padding
= 0;
474 ( "sid_padding is [%d].\n", entry
->data_record
.sid_padding
) );
476 ( "data_padding is [%d].\n",
477 entry
->data_record
.data_padding
) );
479 entry
->record
.length
+= entry
->data_record
.sid_padding
;
480 entry
->record
.length
+= entry
->record
.user_sid_length
;
481 entry
->record
.length
+= entry
->data_record
.strings_len
;
482 entry
->record
.length
+= entry
->data_record
.user_data_len
;
483 entry
->record
.length
+= entry
->data_record
.data_padding
;
484 /* need another copy of length at the end of the data */
485 entry
->record
.length
+= sizeof( entry
->record
.length
);
487 ( "entry->record.length is [%d].\n", entry
->record
.length
) );
489 PRS_ALLOC_MEM( ps
, uint8
,
490 entry
->record
.length
-
491 sizeof( Eventlog_record
) -
492 sizeof( entry
->record
.length
) );
493 if ( entry
->data
== NULL
) {
496 offset
= entry
->data
;
497 memcpy( offset
, &( entry
->data_record
.source_name
),
498 entry
->data_record
.source_name_len
);
499 offset
+= entry
->data_record
.source_name_len
;
500 memcpy( offset
, &( entry
->data_record
.computer_name
),
501 entry
->data_record
.computer_name_len
);
502 offset
+= entry
->data_record
.computer_name_len
;
503 /* SID needs to be DWORD-aligned */
504 offset
+= entry
->data_record
.sid_padding
;
505 entry
->record
.user_sid_offset
=
506 sizeof( Eventlog_record
) + ( offset
- entry
->data
);
507 memcpy( offset
, &( entry
->data_record
.sid
),
508 entry
->record
.user_sid_length
);
509 offset
+= entry
->record
.user_sid_length
;
510 /* Now do the strings */
511 entry
->record
.string_offset
=
512 sizeof( Eventlog_record
) + ( offset
- entry
->data
);
513 memcpy( offset
, &( entry
->data_record
.strings
),
514 entry
->data_record
.strings_len
);
515 offset
+= entry
->data_record
.strings_len
;
516 /* Now do the data */
517 entry
->record
.data_length
= entry
->data_record
.user_data_len
;
518 entry
->record
.data_offset
=
519 sizeof( Eventlog_record
) + ( offset
- entry
->data
);
520 memcpy( offset
, &( entry
->data_record
.user_data
),
521 entry
->data_record
.user_data_len
);
522 offset
+= entry
->data_record
.user_data_len
;
524 memcpy( &( ee_new
->record
), &entry
->record
,
525 sizeof( Eventlog_record
) );
526 memcpy( &( ee_new
->data_record
), &entry
->data_record
,
527 sizeof( Eventlog_data_record
) );
528 ee_new
->data
= entry
->data
;
533 /********************************************************************
534 ********************************************************************/
536 static BOOL
add_record_to_resp( EVENTLOG_R_READ_EVENTLOG
* r_u
,
537 Eventlog_entry
* ee_new
)
539 Eventlog_entry
*insert_point
;
541 insert_point
= r_u
->entry
;
543 if ( NULL
== insert_point
) {
547 while ( ( NULL
!= insert_point
->next
) ) {
548 insert_point
= insert_point
->next
;
551 insert_point
->next
= ee_new
;
554 r_u
->num_bytes_in_resp
+= ee_new
->record
.length
;
559 /********************************************************************
560 ********************************************************************/
562 NTSTATUS
_eventlog_open_eventlog( pipes_struct
* p
,
563 EVENTLOG_Q_OPEN_EVENTLOG
* q_u
,
564 EVENTLOG_R_OPEN_EVENTLOG
* r_u
)
566 fstring servername
, logname
;
570 fstrcpy( servername
, "" );
571 if ( q_u
->servername
.string
) {
572 rpcstr_pull( servername
, q_u
->servername
.string
->buffer
,
573 sizeof( servername
),
574 q_u
->servername
.string
->uni_str_len
* 2, 0 );
577 fstrcpy( logname
, "" );
578 if ( q_u
->logname
.string
) {
579 rpcstr_pull( logname
, q_u
->logname
.string
->buffer
,
581 q_u
->logname
.string
->uni_str_len
* 2, 0 );
584 DEBUG( 10,("_eventlog_open_eventlog: Server [%s], Log [%s]\n",
585 servername
, logname
));
587 /* according to MSDN, if the logfile cannot be found, we should
588 default to the "Application" log */
590 if ( !NT_STATUS_IS_OK( result
= elog_open( p
, logname
, &r_u
->handle
)) )
593 if ( !(info
= find_eventlog_info_by_hnd( p
, &r_u
->handle
)) ) {
594 DEBUG(0,("_eventlog_open_eventlog: eventlog (%s) opened but unable to find handle!\n",
596 elog_close( p
, &r_u
->handle
);
597 return NT_STATUS_INVALID_HANDLE
;
600 DEBUG(10,("_eventlog_open_eventlog: Size [%d]\n", elog_size( info
)));
602 sync_eventlog_params( info
);
603 prune_eventlog( info
->tdb
);
608 /********************************************************************
609 This call still needs some work
610 ********************************************************************/
612 NTSTATUS
_eventlog_clear_eventlog( pipes_struct
* p
,
613 EVENTLOG_Q_CLEAR_EVENTLOG
* q_u
,
614 EVENTLOG_R_CLEAR_EVENTLOG
* r_u
)
616 EVENTLOG_INFO
*info
= find_eventlog_info_by_hnd( p
, &q_u
->handle
);
617 pstring backup_file_name
;
620 return NT_STATUS_INVALID_HANDLE
;
622 pstrcpy( backup_file_name
, "" );
623 if ( q_u
->backupfile
.string
) {
624 rpcstr_pull( backup_file_name
, q_u
->backupfile
.string
->buffer
,
625 sizeof( backup_file_name
),
626 q_u
->backupfile
.string
->uni_str_len
* 2, 0 );
630 ( "_eventlog_clear_eventlog: Using [%s] as the backup file name for log [%s].",
631 backup_file_name
, info
->logname
) );
634 /* close the current one, reinit */
636 tdb_close( info
->tdb
);
638 if ( !(info
->tdb
= elog_init_tdb( ttdb
[i
].tdbfname
)) )
639 return NT_STATUS_ACCESS_DENIED
;
645 /********************************************************************
646 ********************************************************************/
648 NTSTATUS
_eventlog_close_eventlog( pipes_struct
* p
,
649 EVENTLOG_Q_CLOSE_EVENTLOG
* q_u
,
650 EVENTLOG_R_CLOSE_EVENTLOG
* r_u
)
652 return elog_close( p
, &q_u
->handle
);
655 /********************************************************************
656 ********************************************************************/
658 NTSTATUS
_eventlog_read_eventlog( pipes_struct
* p
,
659 EVENTLOG_Q_READ_EVENTLOG
* q_u
,
660 EVENTLOG_R_READ_EVENTLOG
* r_u
)
662 EVENTLOG_INFO
*info
= find_eventlog_info_by_hnd( p
, &q_u
->handle
);
663 Eventlog_entry entry
, *ee_new
;
665 uint32 num_records_read
= 0;
667 int bytes_left
, record_number
;
670 info
->flags
= q_u
->flags
;
671 ps
= &p
->out_data
.rdata
;
673 bytes_left
= q_u
->max_read_size
;
676 return NT_STATUS_ACCESS_DENIED
;
679 /* DEBUG(8,("Bytes left is %d\n",bytes_left)); */
681 record_number
= q_u
->offset
;
683 while ( bytes_left
> 0 ) {
684 if ( get_eventlog_record
685 ( ps
, tdb
, record_number
, &entry
) ) {
687 ( "Retrieved record %d\n", record_number
) );
689 /* Now see if there is enough room to add */
690 ee_new
= read_package_entry( ps
, q_u
, r_u
,&entry
);
692 return NT_STATUS_NO_MEMORY
;
694 if ( r_u
->num_bytes_in_resp
+ ee_new
->record
.length
>
695 q_u
->max_read_size
) {
696 r_u
->bytes_in_next_record
=
697 ee_new
->record
.length
;
699 /* response would be too big to fit in client-size buffer */
705 add_record_to_resp( r_u
, ee_new
);
706 bytes_left
-= ee_new
->record
.length
;
707 ZERO_STRUCT( entry
);
709 r_u
->num_records
- num_records_read
;
712 ( "_eventlog_read_eventlog: read [%d] records for a total of [%d] records using [%d] bytes out of a max of [%d].\n",
713 num_records_read
, r_u
->num_records
,
714 r_u
->num_bytes_in_resp
,
715 q_u
->max_read_size
) );
717 DEBUG( 8, ( "get_eventlog_record returned NULL\n" ) );
718 return NT_STATUS_NO_MEMORY
; /* wrong error - but return one anyway */
722 if ( info
->flags
& EVENTLOG_FORWARDS_READ
)
731 /********************************************************************
732 ********************************************************************/
734 NTSTATUS
_eventlog_get_oldest_entry( pipes_struct
* p
,
735 EVENTLOG_Q_GET_OLDEST_ENTRY
* q_u
,
736 EVENTLOG_R_GET_OLDEST_ENTRY
* r_u
)
738 EVENTLOG_INFO
*info
= find_eventlog_info_by_hnd( p
, &q_u
->handle
);
740 if ( !( get_oldest_entry_hook( info
) ) )
741 return NT_STATUS_ACCESS_DENIED
;
743 r_u
->oldest_entry
= info
->oldest_entry
;
748 /********************************************************************
749 ********************************************************************/
751 NTSTATUS
_eventlog_get_num_records( pipes_struct
* p
,
752 EVENTLOG_Q_GET_NUM_RECORDS
* q_u
,
753 EVENTLOG_R_GET_NUM_RECORDS
* r_u
)
755 EVENTLOG_INFO
*info
= find_eventlog_info_by_hnd( p
, &q_u
->handle
);
757 if ( !( get_num_records_hook( info
) ) )
758 return NT_STATUS_ACCESS_DENIED
;
760 r_u
->num_records
= info
->num_records
;