1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sandbox/win/src/win_utils.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "sandbox/win/src/internal_types.h"
11 #include "sandbox/win/src/nt_internals.h"
12 #include "sandbox/win/src/sandbox_nt_util.h"
16 // Holds the information about a known registry key.
17 struct KnownReservedKey
{
22 // Contains all the known registry key by name and by handle.
23 const KnownReservedKey kKnownKey
[] = {
24 { L
"HKEY_CLASSES_ROOT", HKEY_CLASSES_ROOT
},
25 { L
"HKEY_CURRENT_USER", HKEY_CURRENT_USER
},
26 { L
"HKEY_LOCAL_MACHINE", HKEY_LOCAL_MACHINE
},
27 { L
"HKEY_USERS", HKEY_USERS
},
28 { L
"HKEY_PERFORMANCE_DATA", HKEY_PERFORMANCE_DATA
},
29 { L
"HKEY_PERFORMANCE_TEXT", HKEY_PERFORMANCE_TEXT
},
30 { L
"HKEY_PERFORMANCE_NLSTEXT", HKEY_PERFORMANCE_NLSTEXT
},
31 { L
"HKEY_CURRENT_CONFIG", HKEY_CURRENT_CONFIG
},
32 { L
"HKEY_DYN_DATA", HKEY_DYN_DATA
}
35 // Returns true if the provided path points to a pipe.
36 bool IsPipe(const std::wstring
& path
) {
38 if (0 == path
.compare(0, sandbox::kNTPrefixLen
, sandbox::kNTPrefix
))
39 start
= sandbox::kNTPrefixLen
;
41 const wchar_t kPipe
[] = L
"pipe\\";
42 return (0 == path
.compare(start
, arraysize(kPipe
) - 1, kPipe
));
49 HKEY
GetReservedKeyFromName(const std::wstring
& name
) {
50 for (size_t i
= 0; i
< arraysize(kKnownKey
); ++i
) {
51 if (name
== kKnownKey
[i
].name
)
52 return kKnownKey
[i
].key
;
58 bool ResolveRegistryName(std::wstring name
, std::wstring
* resolved_name
) {
59 for (size_t i
= 0; i
< arraysize(kKnownKey
); ++i
) {
60 if (name
.find(kKnownKey
[i
].name
) == 0) {
63 if (ERROR_SUCCESS
!= ::RegCreateKeyEx(kKnownKey
[i
].key
, L
"", 0, NULL
, 0,
64 MAXIMUM_ALLOWED
, NULL
, &key
,
68 bool result
= GetPathFromHandle(key
, resolved_name
);
74 *resolved_name
+= name
.substr(wcslen(kKnownKey
[i
].name
));
82 DWORD
IsReparsePoint(const std::wstring
& full_path
, bool* result
) {
83 std::wstring path
= full_path
;
85 // Remove the nt prefix.
86 if (0 == path
.compare(0, kNTPrefixLen
, kNTPrefix
))
87 path
= path
.substr(kNTPrefixLen
);
89 // Check if it's a pipe. We can't query the attributes of a pipe.
95 std::wstring::size_type last_pos
= std::wstring::npos
;
98 path
= path
.substr(0, last_pos
);
100 DWORD attributes
= ::GetFileAttributes(path
.c_str());
101 if (INVALID_FILE_ATTRIBUTES
== attributes
) {
102 DWORD error
= ::GetLastError();
103 if (error
!= ERROR_FILE_NOT_FOUND
&&
104 error
!= ERROR_PATH_NOT_FOUND
&&
105 error
!= ERROR_INVALID_NAME
) {
110 } else if (FILE_ATTRIBUTE_REPARSE_POINT
& attributes
) {
111 // This is a reparse point.
113 return ERROR_SUCCESS
;
116 last_pos
= path
.rfind(L
'\\');
117 } while (last_pos
!= std::wstring::npos
);
120 return ERROR_SUCCESS
;
123 // We get a |full_path| of the form \??\c:\some\foo\bar, and the name that
124 // we'll get from |handle| will be \device\harddiskvolume1\some\foo\bar.
125 bool SameObject(HANDLE handle
, const wchar_t* full_path
) {
126 std::wstring
path(full_path
);
127 DCHECK_NT(!path
.empty());
129 // Check if it's a pipe.
133 std::wstring actual_path
;
134 if (!GetPathFromHandle(handle
, &actual_path
))
137 // This may end with a backslash.
138 const wchar_t kBackslash
= '\\';
139 if (path
[path
.length() - 1] == kBackslash
)
140 path
= path
.substr(0, path
.length() - 1);
142 // Perfect match (case-insesitive check).
143 if (0 == _wcsicmp(actual_path
.c_str(), path
.c_str()))
146 // Look for the drive letter.
147 size_t colon_pos
= path
.find(L
':');
148 if (colon_pos
== 0 || colon_pos
== std::wstring::npos
)
151 // Only one character for the drive.
152 if (colon_pos
> 1 && path
[colon_pos
- 2] != kBackslash
)
155 // We only need 3 chars, but let's alloc a buffer for four.
156 wchar_t drive
[4] = {0};
157 wchar_t vol_name
[MAX_PATH
];
158 memcpy(drive
, &path
[colon_pos
- 1], 2 * sizeof(*drive
));
160 // We'll get a double null terminated string.
161 DWORD vol_length
= ::QueryDosDeviceW(drive
, vol_name
, MAX_PATH
);
162 if (vol_length
< 2 || vol_length
== MAX_PATH
)
165 // Ignore the nulls at the end.
166 vol_length
= static_cast<DWORD
>(wcslen(vol_name
));
168 // The two paths should be the same length.
169 if (vol_length
+ path
.size() - (colon_pos
+ 1) != actual_path
.size())
172 // Check up to the drive letter.
173 if (0 != _wcsnicmp(actual_path
.c_str(), vol_name
, vol_length
))
176 // Check the path after the drive letter.
177 if (0 != _wcsicmp(&actual_path
[vol_length
], &path
[colon_pos
+ 1]))
183 bool ConvertToLongPath(const std::wstring
& short_path
,
184 std::wstring
* long_path
) {
185 // Check if the path is a NT path.
186 bool is_nt_path
= false;
187 std::wstring path
= short_path
;
188 if (0 == path
.compare(0, kNTPrefixLen
, kNTPrefix
)) {
189 path
= path
.substr(kNTPrefixLen
);
193 DWORD size
= MAX_PATH
;
194 scoped_ptr
<wchar_t[]> long_path_buf(new wchar_t[size
]);
196 DWORD return_value
= ::GetLongPathName(path
.c_str(), long_path_buf
.get(),
198 while (return_value
>= size
) {
200 long_path_buf
.reset(new wchar_t[size
]);
201 return_value
= ::GetLongPathName(path
.c_str(), long_path_buf
.get(), size
);
204 DWORD last_error
= ::GetLastError();
205 if (0 == return_value
&& (ERROR_FILE_NOT_FOUND
== last_error
||
206 ERROR_PATH_NOT_FOUND
== last_error
||
207 ERROR_INVALID_NAME
== last_error
)) {
208 // The file does not exist, but maybe a sub path needs to be expanded.
209 std::wstring::size_type last_slash
= path
.rfind(L
'\\');
210 if (std::wstring::npos
== last_slash
)
213 std::wstring begin
= path
.substr(0, last_slash
);
214 std::wstring end
= path
.substr(last_slash
);
215 if (!ConvertToLongPath(begin
, &begin
))
218 // Ok, it worked. Let's reset the return value.
221 } else if (0 != return_value
) {
222 path
= long_path_buf
.get();
225 if (return_value
!= 0) {
227 *long_path
= kNTPrefix
;
239 bool GetPathFromHandle(HANDLE handle
, std::wstring
* path
) {
240 NtQueryObjectFunction NtQueryObject
= NULL
;
241 ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject
);
243 OBJECT_NAME_INFORMATION initial_buffer
;
244 OBJECT_NAME_INFORMATION
* name
= &initial_buffer
;
245 ULONG size
= sizeof(initial_buffer
);
246 // Query the name information a first time to get the size of the name.
247 NTSTATUS status
= NtQueryObject(handle
, ObjectNameInformation
, name
, size
,
250 scoped_ptr
<OBJECT_NAME_INFORMATION
> name_ptr
;
252 name
= reinterpret_cast<OBJECT_NAME_INFORMATION
*>(new BYTE
[size
]);
253 name_ptr
.reset(name
);
255 // Query the name information a second time to get the name of the
256 // object referenced by the handle.
257 status
= NtQueryObject(handle
, ObjectNameInformation
, name
, size
, &size
);
260 if (STATUS_SUCCESS
!= status
)
263 path
->assign(name
->ObjectName
.Buffer
, name
->ObjectName
.Length
/
264 sizeof(name
->ObjectName
.Buffer
[0]));
268 bool GetNtPathFromWin32Path(const std::wstring
& path
, std::wstring
* nt_path
) {
269 HANDLE file
= ::CreateFileW(path
.c_str(), 0,
270 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
, NULL
,
271 OPEN_EXISTING
, FILE_FLAG_BACKUP_SEMANTICS
, NULL
);
272 if (file
== INVALID_HANDLE_VALUE
)
274 bool rv
= GetPathFromHandle(file
, nt_path
);
279 bool WriteProtectedChildMemory(HANDLE child_process
, void* address
,
280 const void* buffer
, size_t length
) {
281 // First, remove the protections.
282 DWORD old_protection
;
283 if (!::VirtualProtectEx(child_process
, address
, length
,
284 PAGE_WRITECOPY
, &old_protection
))
288 bool ok
= ::WriteProcessMemory(child_process
, address
, buffer
, length
,
289 &written
) && (length
== written
);
291 // Always attempt to restore the original protection.
292 if (!::VirtualProtectEx(child_process
, address
, length
,
293 old_protection
, &old_protection
))
299 }; // namespace sandbox
301 // TODO(jschuh): http://crbug.com/11789
302 // I'm guessing we have a race where some "security" software is messing
303 // with ntdll/imports underneath us. So, we retry a few times, and in the
304 // worst case we sleep briefly before a few more attempts. (Normally sleeping
305 // would be very bad, but it's better than crashing in this case.)
306 void ResolveNTFunctionPtr(const char* name
, void* ptr
) {
307 const int max_tries
= 5;
308 const int sleep_threshold
= 2;
310 static HMODULE ntdll
= ::GetModuleHandle(sandbox::kNtdllName
);
312 FARPROC
* function_ptr
= reinterpret_cast<FARPROC
*>(ptr
);
313 *function_ptr
= ::GetProcAddress(ntdll
, name
);
315 for (int tries
= 1; !(*function_ptr
) && tries
< max_tries
; ++tries
) {
316 if (tries
>= sleep_threshold
)
318 ntdll
= ::GetModuleHandle(sandbox::kNtdllName
);
319 *function_ptr
= ::GetProcAddress(ntdll
, name
);
322 CHECK_NT(*function_ptr
);