source: branches/samba-3.0/source/rpc_server/srv_eventlog_nt.c

Last change on this file was 44, checked in by Paul Smedley, 18 years ago

Update source to 3.0.25b

File size: 23.7 KB
Line 
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
28typedef struct {
29 char *logname;
30 ELOG_TDB *etdb;
31 uint32 current_record;
32 uint32 num_records;
33 uint32 oldest_entry;
34 uint32 flags;
35 uint32 access_granted;
36} EVENTLOG_INFO;
37
38/********************************************************************
39 ********************************************************************/
40
41static void free_eventlog_info( void *ptr )
42{
43 EVENTLOG_INFO *elog = (EVENTLOG_INFO *)ptr;
44
45 if ( elog->etdb )
46 elog_close_tdb( elog->etdb, False );
47
48 TALLOC_FREE( elog );
49}
50
51/********************************************************************
52 ********************************************************************/
53
54static EVENTLOG_INFO *find_eventlog_info_by_hnd( pipes_struct * p,
55 POLICY_HND * handle )
56{
57 EVENTLOG_INFO *info;
58
59 if ( !find_policy_by_hnd( p, handle, (void **)(void *)&info ) ) {
60 DEBUG( 2,
61 ( "find_eventlog_info_by_hnd: eventlog not found.\n" ) );
62 return NULL;
63 }
64
65 return info;
66}
67
68/********************************************************************
69********************************************************************/
70
71static BOOL elog_check_access( EVENTLOG_INFO *info, NT_USER_TOKEN *token )
72{
73 char *tdbname = elog_tdbname( info->logname );
74 SEC_DESC *sec_desc;
75 BOOL ret;
76 NTSTATUS ntstatus;
77
78 if ( !tdbname )
79 return False;
80
81 /* get the security descriptor for the file */
82
83 sec_desc = get_nt_acl_no_snum( info, tdbname );
84 SAFE_FREE( tdbname );
85
86 if ( !sec_desc ) {
87 DEBUG(5,("elog_check_access: Unable to get NT ACL for %s\n",
88 tdbname));
89 return False;
90 }
91
92 /* root free pass */
93
94 if ( geteuid() == sec_initial_uid() ) {
95 DEBUG(5,("elog_check_access: using root's token\n"));
96 token = get_root_nt_token();
97 }
98
99 /* run the check, try for the max allowed */
100
101 ret = se_access_check( sec_desc, token, MAXIMUM_ALLOWED_ACCESS,
102 &info->access_granted, &ntstatus );
103
104 if ( sec_desc )
105 TALLOC_FREE( sec_desc );
106
107 if ( !ret ) {
108 DEBUG(8,("elog_check_access: se_access_check() return %s\n",
109 nt_errstr( ntstatus)));
110 return False;
111 }
112
113 /* we have to have READ permission for a successful open */
114
115 return ( info->access_granted & SA_RIGHT_FILE_READ_DATA );
116}
117
118/********************************************************************
119 ********************************************************************/
120
121static BOOL elog_validate_logname( const char *name )
122{
123 int i;
124 const char **elogs = lp_eventlog_list();
125
126 if (!elogs) {
127 return False;
128 }
129
130 for ( i=0; elogs[i]; i++ ) {
131 if ( strequal( name, elogs[i] ) )
132 return True;
133 }
134
135 return False;
136}
137
138/********************************************************************
139********************************************************************/
140
141static BOOL get_num_records_hook( EVENTLOG_INFO * info )
142{
143 int next_record;
144 int oldest_record;
145
146 if ( !info->etdb ) {
147 DEBUG( 10, ( "No open tdb for %s\n", info->logname ) );
148 return False;
149 }
150
151 /* lock the tdb since we have to get 2 records */
152
153 tdb_lock_bystring_with_timeout( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD, 1 );
154 next_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
155 oldest_record = tdb_fetch_int32( ELOG_TDB_CTX(info->etdb), EVT_OLDEST_ENTRY);
156 tdb_unlock_bystring( ELOG_TDB_CTX(info->etdb), EVT_NEXT_RECORD);
157
158 DEBUG( 8,
159 ( "Oldest Record %d; Next Record %d\n", oldest_record,
160 next_record ) );
161
162 info->num_records = ( next_record - oldest_record );
163 info->oldest_entry = oldest_record;
164
165 return True;
166}
167
168/********************************************************************
169 ********************************************************************/
170
171static BOOL get_oldest_entry_hook( EVENTLOG_INFO * info )
172{
173 /* it's the same thing */
174 return get_num_records_hook( info );
175}
176
177/********************************************************************
178 ********************************************************************/
179
180static NTSTATUS elog_open( pipes_struct * p, const char *logname, POLICY_HND *hnd )
181{
182 EVENTLOG_INFO *elog;
183
184 /* first thing is to validate the eventlog name */
185
186 if ( !elog_validate_logname( logname ) )
187 return NT_STATUS_OBJECT_PATH_INVALID;
188
189 if ( !(elog = TALLOC_ZERO_P( NULL, EVENTLOG_INFO )) )
190 return NT_STATUS_NO_MEMORY;
191
192 elog->logname = talloc_strdup( elog, logname );
193
194 /* Open the tdb first (so that we can create any new tdbs if necessary).
195 We have to do this as root and then use an internal access check
196 on the file permissions since you can only have a tdb open once
197 in a single process */
198
199 become_root();
200 elog->etdb = elog_open_tdb( elog->logname, False );
201 unbecome_root();
202
203 if ( !elog->etdb ) {
204 /* according to MSDN, if the logfile cannot be found, we should
205 default to the "Application" log */
206
207 if ( !strequal( logname, ELOG_APPL ) ) {
208
209 TALLOC_FREE( elog->logname );
210
211 elog->logname = talloc_strdup( elog, ELOG_APPL );
212
213 /* do the access check */
214 if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
215 TALLOC_FREE( elog );
216 return NT_STATUS_ACCESS_DENIED;
217 }
218
219 become_root();
220 elog->etdb = elog_open_tdb( elog->logname, False );
221 unbecome_root();
222 }
223
224 if ( !elog->etdb ) {
225 TALLOC_FREE( elog );
226 return NT_STATUS_ACCESS_DENIED; /* ??? */
227 }
228 }
229
230 /* now do the access check. Close the tdb if we fail here */
231
232 if ( !elog_check_access( elog, p->pipe_user.nt_user_token ) ) {
233 elog_close_tdb( elog->etdb, False );
234 TALLOC_FREE( elog );
235 return NT_STATUS_ACCESS_DENIED;
236 }
237
238 /* create the policy handle */
239
240 if ( !create_policy_hnd
241 ( p, hnd, free_eventlog_info, ( void * ) elog ) ) {
242 free_eventlog_info( elog );
243 return NT_STATUS_NO_MEMORY;
244 }
245
246 /* set the initial current_record pointer */
247
248 if ( !get_oldest_entry_hook( elog ) ) {
249 DEBUG(3,("elog_open: Successfully opened eventlog but can't "
250 "get any information on internal records!\n"));
251 }
252
253 elog->current_record = elog->oldest_entry;
254
255 return NT_STATUS_OK;
256}
257
258/********************************************************************
259 ********************************************************************/
260
261static NTSTATUS elog_close( pipes_struct *p, POLICY_HND *hnd )
262{
263 if ( !( close_policy_hnd( p, hnd ) ) ) {
264 return NT_STATUS_INVALID_HANDLE;
265 }
266
267 return NT_STATUS_OK;
268}
269
270/*******************************************************************
271 *******************************************************************/
272
273static int elog_size( EVENTLOG_INFO *info )
274{
275 if ( !info || !info->etdb ) {
276 DEBUG(0,("elog_size: Invalid info* structure!\n"));
277 return 0;
278 }
279
280 return elog_tdb_size( ELOG_TDB_CTX(info->etdb), NULL, NULL );
281}
282
283/********************************************************************
284 For the given tdb, get the next eventlog record into the passed
285 Eventlog_entry. returns NULL if it can't get the record for some reason.
286 ********************************************************************/
287
288static Eventlog_entry *get_eventlog_record( prs_struct * ps, TDB_CONTEXT * tdb,
289 int recno, Eventlog_entry * ee )
290{
291 TDB_DATA ret, key;
292
293 int srecno;
294 int reclen;
295 int len;
296
297 pstring *wpsource, *wpcomputer, *wpsid, *wpstrs, *puserdata;
298
299 key.dsize = sizeof( int32 );
300
301 srecno = recno;
302 key.dptr = ( char * ) &srecno;
303
304 ret = tdb_fetch( tdb, key );
305
306 if ( ret.dsize == 0 ) {
307 DEBUG( 8,
308 ( "Can't find a record for the key, record %d\n",
309 recno ) );
310 return NULL;
311 }
312
313 len = tdb_unpack( ret.dptr, ret.dsize, "d", &reclen );
314
315 DEBUG( 10, ( "Unpacking record %d, size is %d\n", srecno, len ) );
316
317 if ( !len )
318 return NULL;
319
320 /* ee = PRS_ALLOC_MEM(ps, Eventlog_entry, 1); */
321
322 if ( !ee )
323 return NULL;
324
325 len = tdb_unpack( ret.dptr, ret.dsize, "ddddddwwwwddddddBBdBBBd",
326 &ee->record.length, &ee->record.reserved1,
327 &ee->record.record_number,
328 &ee->record.time_generated,
329 &ee->record.time_written, &ee->record.event_id,
330 &ee->record.event_type, &ee->record.num_strings,
331 &ee->record.event_category, &ee->record.reserved2,
332 &ee->record.closing_record_number,
333 &ee->record.string_offset,
334 &ee->record.user_sid_length,
335 &ee->record.user_sid_offset,
336 &ee->record.data_length, &ee->record.data_offset,
337 &ee->data_record.source_name_len, &wpsource,
338 &ee->data_record.computer_name_len, &wpcomputer,
339 &ee->data_record.sid_padding,
340 &ee->record.user_sid_length, &wpsid,
341 &ee->data_record.strings_len, &wpstrs,
342 &ee->data_record.user_data_len, &puserdata,
343 &ee->data_record.data_padding );
344 DEBUG( 10,
345 ( "Read record %d, len in tdb was %d\n",
346 ee->record.record_number, len ) );
347
348 /* have to do the following because the tdb_unpack allocs a buff, stuffs a pointer to the buff
349 into it's 2nd argment for 'B' */
350
351 if ( wpcomputer )
352 memcpy( ee->data_record.computer_name, wpcomputer,
353 ee->data_record.computer_name_len );
354 if ( wpsource )
355 memcpy( ee->data_record.source_name, wpsource,
356 ee->data_record.source_name_len );
357
358 if ( wpsid )
359 memcpy( ee->data_record.sid, wpsid,
360 ee->record.user_sid_length );
361 if ( wpstrs )
362 memcpy( ee->data_record.strings, wpstrs,
363 ee->data_record.strings_len );
364
365 /* note that userdata is a pstring */
366 if ( puserdata )
367 memcpy( ee->data_record.user_data, puserdata,
368 ee->data_record.user_data_len );
369
370 SAFE_FREE( wpcomputer );
371 SAFE_FREE( wpsource );
372 SAFE_FREE( wpsid );
373 SAFE_FREE( wpstrs );
374 SAFE_FREE( puserdata );
375
376 DEBUG( 10, ( "get_eventlog_record: read back %d\n", len ) );
377 DEBUG( 10,
378 ( "get_eventlog_record: computer_name %d is ",
379 ee->data_record.computer_name_len ) );
380 SAFE_FREE( ret.dptr );
381 return ee;
382}
383
384/********************************************************************
385 note that this can only be called AFTER the table is constructed,
386 since it uses the table to find the tdb handle
387 ********************************************************************/
388
389static BOOL sync_eventlog_params( EVENTLOG_INFO *info )
390{
391 pstring path;
392 uint32 uiMaxSize;
393 uint32 uiRetention;
394 REGISTRY_KEY *keyinfo;
395 REGISTRY_VALUE *val;
396 REGVAL_CTR *values;
397 WERROR wresult;
398 char *elogname = info->logname;
399
400 DEBUG( 4, ( "sync_eventlog_params with %s\n", elogname ) );
401
402 if ( !info->etdb ) {
403 DEBUG( 4, ( "No open tdb! (%s)\n", info->logname ) );
404 return False;
405 }
406 /* set resonable defaults. 512Kb on size and 1 week on time */
407
408 uiMaxSize = 0x80000;
409 uiRetention = 604800;
410
411 /* the general idea is to internally open the registry
412 key and retreive the values. That way we can continue
413 to use the same fetch/store api that we use in
414 srv_reg_nt.c */
415
416 pstr_sprintf( path, "%s/%s", KEY_EVENTLOG, elogname );
417
418 wresult =
419 regkey_open_internal( &keyinfo, path, get_root_nt_token( ),
420 REG_KEY_READ );
421
422 if ( !W_ERROR_IS_OK( wresult ) ) {
423 DEBUG( 4,
424 ( "sync_eventlog_params: Failed to open key [%s] (%s)\n",
425 path, dos_errstr( wresult ) ) );
426 return False;
427 }
428
429 if ( !( values = TALLOC_ZERO_P( keyinfo, REGVAL_CTR ) ) ) {
430 TALLOC_FREE( keyinfo );
431 DEBUG( 0, ( "control_eventlog_hook: talloc() failed!\n" ) );
432
433 return False;
434 }
435 fetch_reg_values( keyinfo, values );
436
437 if ( ( val = regval_ctr_getvalue( values, "Retention" ) ) != NULL )
438 uiRetention = IVAL( regval_data_p( val ), 0 );
439
440 if ( ( val = regval_ctr_getvalue( values, "MaxSize" ) ) != NULL )
441 uiMaxSize = IVAL( regval_data_p( val ), 0 );
442
443 regkey_close_internal( keyinfo );
444
445 tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_MAXSIZE, uiMaxSize );
446 tdb_store_int32( ELOG_TDB_CTX(info->etdb), EVT_RETENTION, uiRetention );
447
448 return True;
449}
450
451/********************************************************************
452 ********************************************************************/
453
454static Eventlog_entry *read_package_entry( prs_struct * ps,
455 EVENTLOG_Q_READ_EVENTLOG * q_u,
456 EVENTLOG_R_READ_EVENTLOG * r_u,
457 Eventlog_entry * entry )
458{
459 uint8 *offset;
460 Eventlog_entry *ee_new = NULL;
461
462 ee_new = PRS_ALLOC_MEM( ps, Eventlog_entry, 1 );
463 if ( ee_new == NULL ) {
464 return NULL;
465 }
466
467 entry->data_record.sid_padding =
468 ( ( 4 -
469 ( ( entry->data_record.source_name_len +
470 entry->data_record.computer_name_len ) % 4 ) ) % 4 );
471 entry->data_record.data_padding =
472 ( 4 -
473 ( ( entry->data_record.strings_len +
474 entry->data_record.user_data_len ) % 4 ) ) % 4;
475 entry->record.length = sizeof( Eventlog_record );
476 entry->record.length += entry->data_record.source_name_len;
477 entry->record.length += entry->data_record.computer_name_len;
478 if ( entry->record.user_sid_length == 0 ) {
479 /* Should not pad to a DWORD boundary for writing out the sid if there is
480 no SID, so just propagate the padding to pad the data */
481 entry->data_record.data_padding +=
482 entry->data_record.sid_padding;
483 entry->data_record.sid_padding = 0;
484 }
485 DEBUG( 10,
486 ( "sid_padding is [%d].\n", entry->data_record.sid_padding ) );