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