source: unzip/vendor/current/win32/nt.c@ 200

Last change on this file since 200 was 200, checked in by Yuri Dario, 14 years ago

zip: initial unzip 6.0 import.

File size: 16.3 KB
Line 
1/*
2 Copyright (c) 1990-2007 Info-ZIP. All rights reserved.
3
4 See the accompanying file LICENSE, version 2000-Apr-09 or later
5 (the contents of which are also included in unzip.h) for terms of use.
6 If, for some reason, all these files are missing, the Info-ZIP license
7 also may be found at: ftp://ftp.info-zip.org/pub/infozip/license.html
8*/
9/*
10
11 Copyright (c) 1996 Scott Field (dedicated to Info-Zip group)
12
13 Module Name:
14
15 nt.c
16
17 Abstract:
18
19 This module implements WinNT security descriptor operations for the
20 Win32 Info-ZIP project. Operation such as setting file security,
21 using/querying local and remote privileges, and queuing of operations
22 is performed here. The contents of this module are only relevant
23 when the code is running on Windows NT, and the target volume supports
24 persistent Acl storage.
25
26 User privileges that allow accessing certain privileged aspects of the
27 security descriptor (such as the Sacl) are only used if the user specified
28 to do so.
29
30 Author:
31
32 Scott Field (sfield@microsoft.com)
33
34 Last revised: 18 Jan 97
35
36 */
37
38#define WIN32_LEAN_AND_MEAN
39#define UNZIP_INTERNAL
40#include "../unzip.h"
41#include <windows.h>
42#ifdef __RSXNT__
43# include "../win32/rsxntwin.h"
44#endif
45#include "../win32/nt.h"
46
47
48#ifdef NTSD_EAS /* This file is only needed for NTSD handling */
49
50/* Borland C++ does not define FILE_SHARE_DELETE. Others also? */
51#ifndef FILE_SHARE_DELETE
52# define FILE_SHARE_DELETE 0x00000004
53#endif
54
55/* This macro definition is missing in old versions of MS' winbase.h. */
56#ifndef InterlockedExchangePointer
57# define InterlockedExchangePointer(Target, Value) \
58 (PVOID)InterlockedExchange((PLONG)(Target), (LONG)(Value))
59#endif
60
61
62/* private prototypes */
63
64static BOOL Initialize(VOID);
65static VOID GetRemotePrivilegesSet(CHAR *FileName, PDWORD dwRemotePrivileges);
66static VOID InitLocalPrivileges(VOID);
67
68
69volatile BOOL bInitialized = FALSE; /* module level stuff initialized? */
70HANDLE hInitMutex = NULL; /* prevent multiple initialization */
71
72BOOL g_bRestorePrivilege = FALSE; /* for local set file security override */
73BOOL g_bSaclPrivilege = FALSE; /* for local set sacl operations, only when
74 restore privilege not present */
75
76/* our single cached volume capabilities structure that describes the last
77 volume root we encountered. A single entry like this works well in the
78 zip/unzip scenario for a number of reasons:
79 1. typically one extraction path during unzip.
80 2. typically process one volume at a time during zip, and then move
81 on to the next.
82 3. no cleanup code required and no memory leaks.
83 4. simple code.
84
85 This approach should be reworked to a linked list approach if we expect to
86 be called by many threads which are processing a variety of input/output
87 volumes, since lock contention and stale data may become a bottleneck. */
88
89VOLUMECAPS g_VolumeCaps;
90CRITICAL_SECTION VolumeCapsLock;
91
92
93static BOOL Initialize(VOID)
94{
95 HANDLE hMutex;
96 HANDLE hOldMutex;
97
98 if (bInitialized) return TRUE;
99
100 hMutex = CreateMutex(NULL, TRUE, NULL);
101 if(hMutex == NULL) return FALSE;
102
103 hOldMutex = (HANDLE)InterlockedExchangePointer((void *)&hInitMutex,
104 hMutex);
105
106 if (hOldMutex != NULL) {
107 /* somebody setup the mutex already */
108 InterlockedExchangePointer((void *)&hInitMutex,
109 hOldMutex);
110
111 CloseHandle(hMutex); /* close new, un-needed mutex */
112
113 /* wait for initialization to complete and return status */
114 WaitForSingleObject(hOldMutex, INFINITE);
115 ReleaseMutex(hOldMutex);
116
117 return bInitialized;
118 }
119
120 if (!bInitialized) {
121 /* initialize module level resources */
122
123 InitializeCriticalSection( &VolumeCapsLock );
124 memset(&g_VolumeCaps, 0, sizeof(VOLUMECAPS));
125
126 InitLocalPrivileges();
127
128 bInitialized = TRUE;
129 }
130
131 InterlockedExchangePointer((void *)&hInitMutex,
132 NULL);
133
134 ReleaseMutex(hMutex); /* release correct mutex */
135
136 CloseHandle(hMutex); /* free the no longer needed handle resource */
137
138 return TRUE;
139}
140
141
142BOOL ValidateSecurity(uch *securitydata)
143{
144 PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)securitydata;
145 PACL pAcl;
146 PSID pSid;
147 BOOL bAclPresent;
148 BOOL bDefaulted;
149
150 if(!IsWinNT()) return TRUE; /* don't do anything if not on WinNT */
151
152 if(!IsValidSecurityDescriptor(sd)) return FALSE;
153
154 /* verify Dacl integrity */
155
156 if(!GetSecurityDescriptorDacl(sd, &bAclPresent, &pAcl, &bDefaulted))
157 return FALSE;
158
159 if(bAclPresent && pAcl!=NULL) {
160 if(!IsValidAcl(pAcl)) return FALSE;
161 }
162
163 /* verify Sacl integrity */
164
165 if(!GetSecurityDescriptorSacl(sd, &bAclPresent, &pAcl, &bDefaulted))
166 return FALSE;
167
168 if(bAclPresent && pAcl!=NULL) {
169 if(!IsValidAcl(pAcl)) return FALSE;
170 }
171
172 /* verify owner integrity */
173
174 if(!GetSecurityDescriptorOwner(sd, &pSid, &bDefaulted))
175 return FALSE;
176
177 if(pSid != NULL) {
178 if(!IsValidSid(pSid)) return FALSE;
179 }
180
181 /* verify group integrity */
182
183 if(!GetSecurityDescriptorGroup(sd, &pSid, &bDefaulted))
184 return FALSE;
185
186 if(pSid != NULL) {
187 if(!IsValidSid(pSid)) return FALSE;
188 }
189
190 return TRUE;
191}
192
193static VOID GetRemotePrivilegesSet(char *FileName, PDWORD dwRemotePrivileges)
194{
195 HANDLE hFile;
196
197 *dwRemotePrivileges = 0;
198
199 /* see if we have the SeRestorePrivilege */
200
201 hFile = CreateFileA(
202 FileName,
203 ACCESS_SYSTEM_SECURITY | WRITE_DAC | WRITE_OWNER | READ_CONTROL,
204 FILE_SHARE_READ | FILE_SHARE_DELETE, /* no sd updating allowed here */
205 NULL,
206 OPEN_EXISTING,
207 FILE_FLAG_BACKUP_SEMANTICS,
208 NULL
209 );
210
211 if(hFile != INVALID_HANDLE_VALUE) {
212 /* no remote way to determine SeRestorePrivilege -- just try a
213 read/write to simulate it */
214 SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION |
215 SACL_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION |
216 GROUP_SECURITY_INFORMATION;
217 PSECURITY_DESCRIPTOR sd;
218 DWORD cbBuf = 0;
219
220 GetKernelObjectSecurity(hFile, si, NULL, cbBuf, &cbBuf);
221
222 if(ERROR_INSUFFICIENT_BUFFER == GetLastError()) {
223 if((sd = HeapAlloc(GetProcessHeap(), 0, cbBuf)) != NULL) {
224 if(GetKernelObjectSecurity(hFile, si, sd, cbBuf, &cbBuf)) {
225 if(SetKernelObjectSecurity(hFile, si, sd))
226 *dwRemotePrivileges |= OVERRIDE_RESTORE;
227 }
228 HeapFree(GetProcessHeap(), 0, sd);
229 }
230 }
231
232 CloseHandle(hFile);
233 } else {
234
235 /* see if we have the SeSecurityPrivilege */
236 /* note we don't need this if we have SeRestorePrivilege */
237
238 hFile = CreateFileA(
239 FileName,
240 ACCESS_SYSTEM_SECURITY,
241 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, /* max */
242 NULL,
243 OPEN_EXISTING,
244 0,
245 NULL
246 );
247
248 if(hFile != INVALID_HANDLE_VALUE) {
249 CloseHandle(hFile);
250 *dwRemotePrivileges |= OVERRIDE_SACL;
251 }
252 }
253}
254
255
256BOOL GetVolumeCaps(
257 char *rootpath, /* filepath, or NULL */
258 char *name, /* filename associated with rootpath */
259 PVOLUMECAPS VolumeCaps /* result structure describing capabilities */
260 )
261{
262 char TempRootPath[MAX_PATH + 1];
263 DWORD cchTempRootPath = 0;
264 BOOL bSuccess = TRUE; /* assume success until told otherwise */
265
266 if(!bInitialized) if(!Initialize()) return FALSE;
267
268 /* process the input path to produce a consistent path suitable for
269 compare operations and also suitable for certain picky Win32 API
270 that don't like forward slashes */
271
272 if(rootpath != NULL && rootpath[0] != '\0') {
273 DWORD i;
274
275 cchTempRootPath = lstrlenA(rootpath);
276 if(cchTempRootPath > MAX_PATH) return FALSE;
277
278 /* copy input, converting forward slashes to back slashes as we go */
279
280 for(i = 0 ; i <= cchTempRootPath ; i++) {
281 if(rootpath[i] == '/') TempRootPath[i] = '\\';
282 else TempRootPath[i] = rootpath[i];
283 }
284
285 /* check for UNC and Null terminate or append trailing \ as
286 appropriate */
287
288 /* possible valid UNCs we are passed follow:
289 \\machine\foo\bar (path is \\machine\foo\)
290 \\machine\foo (path is \\machine\foo\)
291 \\machine\foo\
292 \\.\c$\ (FIXFIX: Win32API doesn't like this - GetComputerName())
293 LATERLATER: handling mounted DFS drives in the future will require
294 slightly different logic which isn't available today.
295 This is required because directories can point at
296 different servers which have differing capabilities.
297 */
298
299 if(TempRootPath[0] == '\\' && TempRootPath[1] == '\\') {
300 DWORD slash = 0;
301
302 for(i = 2 ; i < cchTempRootPath ; i++) {
303 if(TempRootPath[i] == '\\') {
304 slash++;
305
306 if(slash == 2) {
307 i++;
308 TempRootPath[i] = '\0';
309 cchTempRootPath = i;
310 break;
311 }
312 }
313 }
314
315 /* if there was only one slash found, just tack another onto the
316 end */
317
318 if(slash == 1 && TempRootPath[cchTempRootPath] != '\\') {
319 TempRootPath[cchTempRootPath] = TempRootPath[0]; /* '\\' */
320 TempRootPath[cchTempRootPath+1] = '\0';
321 cchTempRootPath++;
322 }
323
324 } else {
325
326 if(TempRootPath[1] == ':') {
327
328 /* drive letter specified, truncate to root */
329 TempRootPath[2] = '\\';
330 TempRootPath[3] = '\0';
331 cchTempRootPath = 3;
332 } else {
333
334 /* must be file on current drive */
335 TempRootPath[0] = '\0';
336 cchTempRootPath = 0;
337 }
338
339 }
340
341 } /* if path != NULL */
342
343 /* grab lock protecting cached entry */
344 EnterCriticalSection( &VolumeCapsLock );
345
346 if(!g_VolumeCaps.bValid ||
347 lstrcmpiA(g_VolumeCaps.RootPath, TempRootPath) != 0)
348 {
349
350 /* no match found, build up new entry */
351
352 DWORD dwFileSystemFlags;
353 DWORD dwRemotePrivileges = 0;
354 BOOL bRemote = FALSE;
355
356 /* release lock during expensive operations */
357 LeaveCriticalSection( &VolumeCapsLock );
358
359 bSuccess = GetVolumeInformationA(
360 (TempRootPath[0] == '\0') ? NULL : TempRootPath,
361 NULL, 0,
362 NULL, NULL,
363 &dwFileSystemFlags,
364 NULL, 0);
365
366
367 /* only if target volume supports Acls, and we were told to use
368 privileges do we need to go out and test for the remote case */
369
370 if(bSuccess && (dwFileSystemFlags & FS_PERSISTENT_ACLS) &&
371 VolumeCaps->bUsePrivileges)
372 {
373 if(GetDriveTypeA( (TempRootPath[0] == '\0') ? NULL : TempRootPath )
374 == DRIVE_REMOTE)
375 {
376 bRemote = TRUE;
377
378 /* make a determination about our remote capabilities */
379
380 GetRemotePrivilegesSet(name, &dwRemotePrivileges);
381 }
382 }
383
384 /* always take the lock again, since we release it below */
385 EnterCriticalSection( &VolumeCapsLock );
386
387 /* replace the existing data if successful */
388 if(bSuccess) {
389
390 lstrcpynA(g_VolumeCaps.RootPath, TempRootPath, cchTempRootPath+1);
391 g_VolumeCaps.dwFileSystemFlags = dwFileSystemFlags;
392 g_VolumeCaps.bRemote = bRemote;
393 g_VolumeCaps.dwRemotePrivileges = dwRemotePrivileges;
394 g_VolumeCaps.bValid = TRUE;
395 }
396 }
397
398 if(bSuccess) {
399 /* copy input elements */
400 g_VolumeCaps.bUsePrivileges = VolumeCaps->bUsePrivileges;
401 g_VolumeCaps.dwFileAttributes = VolumeCaps->dwFileAttributes;
402
403 /* give caller results */
404 memcpy(VolumeCaps, &g_VolumeCaps, sizeof(VOLUMECAPS));
405 } else {
406 g_VolumeCaps.bValid = FALSE;
407 }
408
409 LeaveCriticalSection( &VolumeCapsLock ); /* release lock */
410
411 return bSuccess;
412}
413
414
415BOOL SecuritySet(char *resource, PVOLUMECAPS VolumeCaps, uch *securitydata)
416{
417 HANDLE hFile;
418 DWORD dwDesiredAccess = 0;
419 DWORD dwFlags = 0;
420 PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)securitydata;
421 SECURITY_DESCRIPTOR_CONTROL sdc;
422 SECURITY_INFORMATION RequestedInfo = 0;
423 DWORD dwRev;
424 BOOL bRestorePrivilege = FALSE;
425 BOOL bSaclPrivilege = FALSE;
426 BOOL bSuccess;
427
428 if(!bInitialized) if(!Initialize()) return FALSE;
429
430 /* defer directory processing */
431
432 if(VolumeCaps->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
433 /* opening a directory requires FILE_FLAG_BACKUP_SEMANTICS */
434 dwFlags |= FILE_FLAG_BACKUP_SEMANTICS;
435 }
436
437 /* evaluate the input security descriptor and act accordingly */
438
439 if(!IsValidSecurityDescriptor(sd))
440 return FALSE;
441
442 if(!GetSecurityDescriptorControl(sd, &sdc, &dwRev))
443 return FALSE;
444
445 /* setup privilege usage based on if told we can use privileges, and if so,
446 what privileges we have */
447
448 if(VolumeCaps->bUsePrivileges) {
449 if(VolumeCaps->bRemote) {
450 /* use remotely determined privileges */
451 if(VolumeCaps->dwRemotePrivileges & OVERRIDE_RESTORE)
452 bRestorePrivilege = TRUE;
453
454 if(VolumeCaps->dwRemotePrivileges & OVERRIDE_SACL)
455 bSaclPrivilege = TRUE;
456
457 } else {
458 /* use local privileges */
459 bRestorePrivilege = g_bRestorePrivilege;
460 bSaclPrivilege = g_bSaclPrivilege;
461 }
462 }
463
464
465 /* if a Dacl is present write Dacl out */
466 /* if we have SeRestorePrivilege, write owner and group info out */
467
468 if(sdc & SE_DACL_PRESENT) {
469 dwDesiredAccess |= WRITE_DAC;
470 RequestedInfo |= DACL_SECURITY_INFORMATION;
471
472 if(bRestorePrivilege) {
473 dwDesiredAccess |= WRITE_OWNER;
474 RequestedInfo |= (OWNER_SECURITY_INFORMATION |
475 GROUP_SECURITY_INFORMATION);
476 }
477 }
478
479 /* if a Sacl is present and we have either SeRestorePrivilege or
480 SeSystemSecurityPrivilege try to write Sacl out */
481
482 if((sdc & SE_SACL_PRESENT) && (bRestorePrivilege || bSaclPrivilege)) {
483 dwDesiredAccess |= ACCESS_SYSTEM_SECURITY;
484 RequestedInfo |= SACL_SECURITY_INFORMATION;
485 }
486
487 if(RequestedInfo == 0) /* nothing to do */
488 return FALSE;
489
490 if(bRestorePrivilege)
491 dwFlags |= FILE_FLAG_BACKUP_SEMANTICS;
492
493 hFile = CreateFileA(
494 resource,
495 dwDesiredAccess,
496 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,/* max sharing */
497 NULL,
498 OPEN_EXISTING,
499 dwFlags,
500 NULL
501 );
502
503 if(hFile == INVALID_HANDLE_VALUE)
504 return FALSE;
505
506 bSuccess = SetKernelObjectSecurity(hFile, RequestedInfo, sd);
507
508 CloseHandle(hFile);
509
510 return bSuccess;
511}
512
513static VOID InitLocalPrivileges(VOID)
514{
515 HANDLE hToken;
516 TOKEN_PRIVILEGES tp;
517
518 /* try to enable some interesting privileges that give us the ability
519 to get some security information that we normally cannot.
520
521 note that enabling privileges is only relevant on the local machine;
522 when accessing files that are on a remote machine, any privileges
523 that are present on the remote machine get enabled by default. */
524
525 if(!OpenProcessToken(GetCurrentProcess(),
526 TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
527 return;
528
529 tp.PrivilegeCount = 1;
530 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
531
532 if(LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &tp.Privileges[0].Luid)) {
533
534 /* try to enable SeRestorePrivilege; if this succeeds, we can write
535 all aspects of the security descriptor */
536
537 if(AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
538 GetLastError() == ERROR_SUCCESS) g_bRestorePrivilege = TRUE;
539
540 }
541
542 /* try to enable SeSystemSecurityPrivilege, if SeRestorePrivilege not
543 present; if this succeeds, we can write the Sacl */
544
545 if(!g_bRestorePrivilege &&
546 LookupPrivilegeValue(NULL, SE_SECURITY_NAME, &tp.Privileges[0].Luid)) {
547
548 if(AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL) &&
549 GetLastError() == ERROR_SUCCESS) g_bSaclPrivilege = TRUE;
550 }
551
552 CloseHandle(hToken);
553}
554#endif /* NTSD_EAS */
Note: See TracBrowser for help on using the repository browser.