Ticket #4827: native_nt_permanent_section.cpp

File native_nt_permanent_section.cpp, 18.5 KB (added by Domagoj Šarić, 12 years ago)
Line 
1////////////////////////////////////////////////////////////////////////////////
2///
3/// native_nt_permanent_section.cpp
4/// -------------------------------
5///
6/// Copyright (c) Domagoj Saric 2010
7///
8/// Use, modification and distribution is subject to the Boost Software
9/// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
10/// http://www.boost.org/LICENSE_1_0.txt)
11///
12/// For more information, see http://www.boost.org
13///
14////////////////////////////////////////////////////////////////////////////////
15//------------------------------------------------------------------------------
16#ifdef _WIN32_WINNT
17 #if _WIN32_WINNT < 0x0500
18 #error target Windows version too low
19 #endif
20#else // _WIN32_WINNT
21 #define _WIN32_WINNT 0x0500
22#endif // _WIN32_WINNT
23
24#include "boost/assert.hpp"
25#include "boost/noncopyable.hpp"
26#include "boost/optional.hpp"
27#include "boost/static_assert.hpp"
28
29#include "accctrl.h"
30#include "aclapi.h"
31#include "windows.h"
32// Make sure to use the latest Platform SDK (the one from VS2008, 6.0, misses
33// __stdcall qualifiers)...
34#include "winternl.h"
35
36#ifndef STATUS_SUCCESS
37 #define STATUS_SUCCESS 0x0
38#endif // STATUS_SUCCESS
39
40#ifndef STATUS_INFO_LENGTH_MISMATCH
41 #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004L
42#endif // STATUS_INFO_LENGTH_MISMATCH
43
44#ifndef STATUS_ACCESS_DENIED
45 #define STATUS_ACCESS_DENIED 0xC0000022L
46#endif // STATUS_ACCESS_DENIED
47
48#ifndef STATUS_PRIVILEGE_NOT_HELD
49 #define STATUS_PRIVILEGE_NOT_HELD 0xC0000061L
50#endif // STATUS_PRIVILEGE_NOT_HELD
51
52#ifndef STATUS_BUFFER_TOO_SMALL
53 #define STATUS_BUFFER_TOO_SMALL 0xC0000023L
54#endif // STATUS_BUFFER_TOO_SMALL
55
56#ifndef STATUS_OBJECT_NAME_EXISTS
57 #define STATUS_OBJECT_NAME_EXISTS 0x40000000L
58#endif // STATUS_OBJECT_NAME_EXISTS
59
60#include <cstring>
61//------------------------------------------------------------------------------
62struct CLIENT_ID
63{
64 UINT_PTR UniqueProcess;
65 UINT_PTR UniqueThread;
66};
67typedef CLIENT_ID * PCLIENT_ID;
68//------------------------------------------------------------------------------
69namespace boost
70{
71//------------------------------------------------------------------------------
72namespace detail
73{
74//------------------------------------------------------------------------------
75 void * get_nt_proc( char const * const proc_name )
76 {
77 return ::GetProcAddress( ::GetModuleHandleA( "ntdll.dll" ), proc_name );
78 }
79
80 template <typename Proc>
81 Proc * get_nt_proc( char const * const proc_name )
82 {
83 return reinterpret_cast<Proc *>( get_nt_proc( proc_name ) );
84 }
85
86 typedef NTSYSAPI NTSTATUS (NTAPI NtClose_t)(IN HANDLE Handle);
87
88 typedef NTSYSAPI NTSTATUS (NTAPI NtAllocateLocallyUniqueId_t)( OUT PLUID LocallyUniqueId );
89
90 typedef NTSYSAPI
91 NTSTATUS
92 (NTAPI
93 NtCreateSection)(
94 OUT PHANDLE SectionHandle,
95 IN ULONG DesiredAccess,
96 IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
97 IN PLARGE_INTEGER MaximumSize OPTIONAL,
98 IN ULONG PageAttributess,
99 IN ULONG SectionAttributes,
100 IN HANDLE FileHandle OPTIONAL
101 );
102
103 typedef NTSYSAPI NTSTATUS (NTAPI NtMakeTemporaryObject)( IN HANDLE ObjectHandle );
104
105 typedef NTSYSAPI
106 NTSTATUS
107 (NTAPI
108 NtCreateToken_t)(
109 OUT PHANDLE TokenHandle,
110 IN ACCESS_MASK DesiredAccess,
111 IN POBJECT_ATTRIBUTES ObjectAttributes,
112 IN TOKEN_TYPE TokenType,
113 IN PLUID AuthenticationId,
114 IN PLARGE_INTEGER ExpirationTime,
115 IN PTOKEN_USER TokenUser,
116 IN PTOKEN_GROUPS TokenGroups,
117 IN PTOKEN_PRIVILEGES TokenPrivileges,
118 IN PTOKEN_OWNER TokenOwner,
119 IN PTOKEN_PRIMARY_GROUP TokenPrimaryGroup,
120 IN PTOKEN_DEFAULT_DACL TokenDefaultDacl,
121 IN PTOKEN_SOURCE TokenSource
122 );
123
124 typedef NTSYSAPI NTSTATUS (NTAPI NtOpenProcessToken_t)(
125 IN HANDLE ProcessHandle,
126 IN ACCESS_MASK DesiredAccess,
127 OUT PHANDLE TokenHandle
128 );
129
130 typedef NTSYSAPI NTSTATUS (NTAPI NtOpenProcess_t)(
131 OUT PHANDLE ProcessHandle,
132 IN ACCESS_MASK AccessMask,
133 IN POBJECT_ATTRIBUTES ObjectAttributes,
134 IN PCLIENT_ID ClientId
135 );
136
137 typedef NTSYSAPI NTSTATUS (NTAPI NtQuerySecurityObject_t)(
138 IN HANDLE ObjectHandle,
139 IN SECURITY_INFORMATION SecurityInformationClass,
140 OUT PSECURITY_DESCRIPTOR DescriptorBuffer,
141 IN ULONG DescriptorBufferLength,
142 OUT PULONG RequiredLength
143 );
144
145 typedef NTSYSAPI NTSTATUS (NTAPI NtSetSecurityObject_t)(
146 IN HANDLE ObjectHandle,
147 IN SECURITY_INFORMATION SecurityInformationClass,
148 IN PSECURITY_DESCRIPTOR DescriptorBuffer
149 );
150
151 typedef NTSYSAPI NTSTATUS (NTAPI NtQuerySystemInformation_t) (
152 IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
153 OUT PVOID SystemInformation,
154 IN ULONG SystemInformationLength,
155 OUT PULONG ReturnLength OPTIONAL
156 );
157
158
159 template <typename RawHandle, typename CloserReturnType, CloserReturnType ( __stdcall * CloseHandleFunction )( RawHandle ), CloserReturnType successValue = ERROR_SUCCESS>
160 class handle_guard : noncopyable
161 {
162 public:
163 typedef RawHandle raw_handle_t;
164
165 public:
166 explicit handle_guard( raw_handle_t const handle = 0 ) : handle_( handle ) {}
167 ~handle_guard() { close(); }
168
169 void close()
170 {
171 BOOST_VERIFY( ( CloseHandleFunction( handle_ ) == successValue ) || !handle_ );
172 handle_ = raw_handle_t( 0 );
173 }
174
175 raw_handle_t operator*( ) const { BOOST_ASSERT( handle_ ); return handle_; }
176 raw_handle_t * operator&( ) { BOOST_ASSERT( !handle_ ); return &handle_; }
177 handle_guard & operator=( RawHandle const handle ) { BOOST_ASSERT( !handle_ ); handle_ = handle; return *this; }
178
179 bool is_null() const { return handle_ == 0; }
180
181 private:
182 raw_handle_t handle_;
183 };
184
185 typedef handle_guard<HANDLE, BOOL , &::CloseHandle, TRUE > win32_handle;
186 typedef handle_guard<HANDLE, NTSTATUS, &::NtClose , STATUS_SUCCESS> nt_handle ;
187
188 DWORD const impersionation_required_access( TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY );
189
190 bool enable_privilege( char const * const privilege_name )
191 {
192 TOKEN_PRIVILEGES tp;
193 tp.PrivilegeCount = 1;
194
195 BOOST_VERIFY( ::LookupPrivilegeValueA( NULL, privilege_name, &tp.Privileges[ 0 ].Luid ) );
196
197 tp.Privileges[ 0 ].Attributes = SE_PRIVILEGE_ENABLED;
198
199 nt_handle current_process_token;
200 NTSTATUS const status( get_nt_proc<NtOpenProcessToken_t>( "NtOpenProcessToken" )( HANDLE( -1 ), TOKEN_ALL_ACCESS, &current_process_token ) );
201 BOOST_VERIFY( status == STATUS_SUCCESS );
202
203 bool const succeeded
204 (
205 ::AdjustTokenPrivileges( *current_process_token, false, &tp, sizeof( tp ), NULL, NULL ) &&
206 ( ::GetLastError() != ERROR_NOT_ALL_ASSIGNED )
207 );
208 BOOST_ASSERT( ( ::GetLastError() != ERROR_NOT_ALL_ASSIGNED ) && "The token does not have the specified privilege." );
209 return succeeded;
210 }
211
212 optional<EXPLICIT_ACCESS> build_administrators_explicit_access()
213 {
214 SID_IDENTIFIER_AUTHORITY /*const*/ system_sid_authority = SECURITY_NT_AUTHORITY;
215 SID * p_sid;
216
217 // Create "Administrators" SID
218 BOOL const result
219 (
220 ::AllocateAndInitializeSid
221 (
222 &system_sid_authority,
223 2,
224 SECURITY_BUILTIN_DOMAIN_RID,
225 DOMAIN_ALIAS_RID_ADMINS,
226 0,
227 0,
228 0,
229 0,
230 0,
231 0,
232 &reinterpret_cast<PSID &>( p_sid )
233 )
234 );
235
236 if ( result )
237 {
238 EXPLICIT_ACCESS explicit_access;
239 explicit_access.grfAccessMode = GRANT_ACCESS;
240 explicit_access.grfAccessPermissions = impersionation_required_access;
241 explicit_access.grfInheritance = 0;
242
243 // Build the Trustee with the SID
244 ::BuildTrusteeWithSid( &explicit_access.Trustee, p_sid );
245
246 return explicit_access;
247 }
248
249 return none;
250 }
251
252 HANDLE get_local_system_token_2kxp()
253 {
254 NTSTATUS status;
255 // See the SuperUsr example in the 2k3 PSDK...
256 OBJECT_ATTRIBUTES poa;
257 InitializeObjectAttributes( &poa, 0, 0, NULL, NULL );
258
259 CLIENT_ID /*const*/ local_system_pid = { 4, 0 };
260 nt_handle system_process;
261 status = get_nt_proc<NtOpenProcess_t>( "NtOpenProcess" )( &system_process, PROCESS_ALL_ACCESS, &poa, &local_system_pid );
262 if ( status == STATUS_ACCESS_DENIED )
263 return NULL;
264
265 BOOST_ASSERT( status == STATUS_SUCCESS );
266
267 nt_handle system_token;
268 status = get_nt_proc<NtOpenProcessToken_t>( "NtOpenProcessToken" )( *system_process, READ_CONTROL | WRITE_DAC, &system_token );
269 BOOST_ASSERT( status == STATUS_SUCCESS );
270
271 DWORD required_size;
272 status = get_nt_proc<NtQuerySecurityObject_t>( "NtQuerySecurityObject" )( *system_token, DACL_SECURITY_INFORMATION, NULL, 0, &required_size );
273 BOOST_ASSERT( status == STATUS_BUFFER_TOO_SMALL );
274 SECURITY_DESCRIPTOR * const p_sd( static_cast<SECURITY_DESCRIPTOR *>( ::_alloca( required_size ) ) );
275 status = get_nt_proc<NtQuerySecurityObject_t>( "NtQuerySecurityObject" )( *system_token, DACL_SECURITY_INFORMATION, p_sd, required_size, &required_size );
276 BOOST_ASSERT( status == STATUS_SUCCESS );
277
278 BOOL dacl_present;
279 BOOL dacl_defaulted;
280 ACL * p_acl;
281 BOOST_VERIFY( ::GetSecurityDescriptorDacl( p_sd, &dacl_present, &p_acl, &dacl_defaulted ) );
282
283 optional<EXPLICIT_ACCESS> /*const*/ admin_access( build_administrators_explicit_access() );
284
285 ACL * p_new_acl( NULL );
286 status = ::SetEntriesInAcl( 1, admin_access.get_ptr(), p_acl, &p_new_acl );
287 BOOST_ASSERT( status == ERROR_SUCCESS );
288
289 BOOL result;
290 DWORD abs_sd_length( 0 ), acl_size( 0 ), dummy_size( 0 );
291 result = ::MakeAbsoluteSD
292 (
293 p_sd,
294 NULL, &abs_sd_length,
295 NULL, &acl_size ,
296 NULL, &dummy_size ,
297 NULL, &dummy_size ,
298 NULL, &dummy_size
299 );
300 BOOST_ASSERT( ( result == false ) && ( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER ) );
301 SECURITY_DESCRIPTOR * const p_abs_sd( static_cast<SECURITY_DESCRIPTOR *>( ::_alloca( abs_sd_length ) ) );
302 ACL * const p_dacl ( static_cast<ACL *>( ::_alloca( acl_size ) ) );
303 result = ::MakeAbsoluteSD
304 (
305 p_sd,
306 p_abs_sd, &abs_sd_length ,
307 p_dacl , &acl_size ,
308 NULL , &dummy_size,
309 NULL , &dummy_size,
310 NULL , &dummy_size
311 );
312 BOOST_ASSERT( result );
313
314 result = ::SetSecurityDescriptorDacl
315 (
316 p_abs_sd,
317 dacl_present,
318 p_new_acl,
319 dacl_defaulted
320 );
321 BOOST_ASSERT( result );
322
323 status = get_nt_proc<NtSetSecurityObject_t>( "NtSetSecurityObject" )( *system_token, DACL_SECURITY_INFORMATION, p_abs_sd );
324
325 if ( admin_access.is_initialized() )
326 BOOST_VERIFY( ::FreeSid( admin_access->Trustee.ptstrName ) == NULL );
327
328 BOOST_VERIFY( ::LocalFree( p_new_acl ) == NULL );
329
330 HANDLE modified_system_token;
331 status = get_nt_proc<NtOpenProcessToken_t>( "NtOpenProcessToken" )( *system_process, impersionation_required_access, &modified_system_token );
332 BOOST_ASSERT( status == STATUS_SUCCESS );
333 return modified_system_token;
334 }
335
336 HANDLE get_local_system_token_vista7()
337 {
338 struct SYSTEM_PROCESS_INFORMATION
339 {
340 ULONG NextEntryOffset;
341 ULONG NumberOfThreads;
342 LARGE_INTEGER Reserved[3];
343 LARGE_INTEGER CreateTime;
344 LARGE_INTEGER UserTime;
345 LARGE_INTEGER KernelTime;
346 UNICODE_STRING ImageName;
347 LONG BasePriority;
348 DWORD_PTR ProcessId;
349 PVOID Reserved3;
350 ULONG HandleCount;
351 BYTE Reserved4[4];
352 PVOID Reserved5[11];
353 SIZE_T PeakPagefileUsage;
354 SIZE_T PrivatePageCount;
355 LARGE_INTEGER Reserved6[6];
356 };
357 BOOST_STATIC_ASSERT( sizeof( SYSTEM_PROCESS_INFORMATION ) == sizeof( ::SYSTEM_PROCESS_INFORMATION ) );
358
359 NTSTATUS status;
360 ULONG required_size;
361 status = get_nt_proc<NtQuerySystemInformation_t>( "NtQuerySystemInformation" )( SystemProcessInformation, NULL, 0, &required_size );
362 BOOST_ASSERT( status == STATUS_INFO_LENGTH_MISMATCH );
363 SYSTEM_PROCESS_INFORMATION * p_process_info( static_cast<SYSTEM_PROCESS_INFORMATION *>( ::alloca( required_size ) ) );
364 status = get_nt_proc<NtQuerySystemInformation_t>( "NtQuerySystemInformation" )( SystemProcessInformation, p_process_info, required_size, &required_size );
365 BOOST_ASSERT( status == STATUS_SUCCESS );
366
367 // http://support.microsoft.com/kb/243330
368 // LocalSystem SID = "S-1-5-18"
369 static SID /*const*/ local_system_sid = { 1, 1, { 0, 0, 0, 0, 0, 5 }, 18 };
370
371 OBJECT_ATTRIBUTES poa;
372 InitializeObjectAttributes( &poa, 0, 0, NULL, NULL );
373
374 CLIENT_ID pid; pid.UniqueThread = 0;
375 for
376 (
377 ; p_process_info->NextEntryOffset != 0
378 ; reinterpret_cast<char * &>( p_process_info ) += p_process_info->NextEntryOffset
379 )
380 {
381 nt_handle local_system_process;
382 pid.UniqueProcess = p_process_info->ProcessId;
383 status = get_nt_proc<NtOpenProcess_t>( "NtOpenProcess" )( &local_system_process, PROCESS_QUERY_INFORMATION, &poa, &pid );
384 if ( status != STATUS_SUCCESS ) continue;
385
386 HANDLE system_token;
387 status = get_nt_proc<NtOpenProcessToken_t>( "NtOpenProcessToken" )( *local_system_process, impersionation_required_access, &system_token );
388 BOOST_ASSERT( status == STATUS_SUCCESS );
389 TOKEN_USER token_user[ 3 ];
390 DWORD requiredSize;
391 BOOST_VERIFY( ::GetTokenInformation( system_token, TokenUser, &token_user, sizeof( token_user ), &requiredSize ) );
392
393 SID const & token_sid( *static_cast<SID const *>( token_user->User.Sid ) );
394 if ( std::memcmp( &token_sid, &local_system_sid, sizeof( token_sid ) ) == 0 )
395 return system_token;
396 else
397 BOOST_VERIFY( ( ::NtClose( system_token ) == STATUS_SUCCESS ) || ( !system_token ) );
398 }
399
400 BOOST_ASSERT( false );
401 return NULL;
402 }
403//------------------------------------------------------------------------------
404} // namespace detail
405
406
407detail::nt_handle create_permanent_section( wchar_t const * const name, unsigned long long /*const*/ size )
408{
409 using namespace boost::detail;
410
411 BOOST_STATIC_ASSERT( sizeof( size ) == sizeof( LARGE_INTEGER ) );
412
413 NTSTATUS status;
414
415 OBJECT_ATTRIBUTES section_oa;
416 UNICODE_STRING uname;
417 ::RtlInitUnicodeString( &uname, name );
418 InitializeObjectAttributes( &section_oa, &uname, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_PERMANENT, NULL, NULL );
419
420 // Try right away (if we are running as a service under LocalSystem or
421 // already have the required rights...maybe it works for administrators
422 // straight away on W2k...).
423 HANDLE section_handle;
424 status = get_nt_proc<NtCreateSection>( "NtCreateSection" )
425 (
426 &section_handle,
427 SECTION_ALL_ACCESS,
428 &section_oa,
429 reinterpret_cast<LARGE_INTEGER /*const*/ *>( &size ),
430 PAGE_EXECUTE_READWRITE,
431 SEC_RESERVE,
432 NULL
433 );
434 if ( ( status == STATUS_SUCCESS ) || ( status == STATUS_OBJECT_NAME_EXISTS ) )
435 {
436 BOOST_ASSERT( section_handle );
437 return nt_handle( section_handle );
438 }
439
440 BOOST_ASSERT( status == STATUS_PRIVILEGE_NOT_HELD );
441
442 BOOST_VERIFY( enable_privilege( SE_DEBUG_NAME ) );
443
444 #if _WIN32_WINNT < 0x0600
445 nt_handle local_system_token( get_local_system_token_2kxp() );
446 if ( local_system_token.is_null() )
447 #else
448 nt_handle
449 #endif
450 local_system_token = get_local_system_token_vista7();
451
452 BOOST_VERIFY( ::ImpersonateLoggedOnUser( *local_system_token ) || local_system_token.is_null() );
453
454 status = get_nt_proc<NtCreateSection>( "NtCreateSection" )
455 (
456 &section_handle,
457 SECTION_ALL_ACCESS,
458 &section_oa,
459 reinterpret_cast<LARGE_INTEGER /*const*/ *>( &size ),
460 PAGE_EXECUTE_READWRITE,
461 SEC_RESERVE,
462 NULL
463 );
464
465 BOOST_VERIFY( ::RevertToSelf() );
466
467 if ( ( status != STATUS_SUCCESS ) && ( status != STATUS_OBJECT_NAME_EXISTS ) )
468 {
469 /* ... */
470 }
471
472 // ...status = get_nt_proc<NtMakeTemporaryObject>( "NtMakeTemporaryObject" )( section_handle );
473
474 return nt_handle( section_handle );
475}
476
477//------------------------------------------------------------------------------
478} // namespace boost
479//------------------------------------------------------------------------------
480
481extern "C" int main( int /*argc*/, char * * /*argv*/ )
482{
483 return boost::create_permanent_section
484 (
485 L"\\BaseNamedObjects\\Native NT Boost Interprocess Test Section",
486 1000000
487 ).is_null() ? EXIT_FAILURE : EXIT_SUCCESS;
488}
489
490
491VOID NTAPI RtlInitUnicodeString( PUNICODE_STRING const DestinationString, PCWSTR const SourceString )
492{
493 DestinationString->Buffer = const_cast<PWSTR>( SourceString );
494 DestinationString->MaximumLength = DestinationString->Length = static_cast<USHORT>( /*std*/::wcslen( SourceString ) * sizeof( WCHAR ) );
495}
496
497NTSTATUS NTAPI NtClose( HANDLE const handle )
498{
499 using namespace boost::detail;
500 return get_nt_proc<NtClose_t>( "NtClose" )( handle );
501}