/
Security.cpp
3125 lines (2758 loc) · 86.9 KB
/
Security.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//////////////////////////////////////////////////////////////////////////
//
// Security translation layer for WIN32
//
// Copyright (c) 2007, Algin Technology LLC
// Written by Alan Klietz
// Distributed under GNU General Public License version 2.
//
// $Id: Security.cpp,v 1.19 2010/05/14 02:34:55 cvsalan Exp $
//
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ole2.h>
#include <wincrypt.h> // prereq for winefs.h
#include <winefs.h> // for QueryUsersOnEncryptedFile
#include <aclapi.h> // for Trustee
#include <sddl.h> // for SDDL_REVISION_1
#include <aclui.h> // for EditSecurity dialog
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <mbstring.h>
#include <errno.h>
#include <system.h> // for alloca()
#ifndef LABEL_SECURITY_INFORMATION
# define LABEL_SECURITY_INFORMATION (0x10) // Return S-1-16-xxxx ACEs in SACL
#endif
//
// Stupid MSVC doesn't define __STDC__
//
#ifndef __STDC__
# define __STDC__ 1
#endif
#include "filemode.h"
#include "error.h"
#include "more.h"
#define NEED_DIRENT_H
#define NEED_CSTR_H
#define NEED_HASH_H
#include "windows-support.h"
#include "xalloc.h"
#include "xmbrtowc.h" // for get_codepage()
#include "ls.h" // for sids_format, gids_format
#ifndef SYSTEM_MANDATORY_LABEL_ACE_TYPE
# define SYSTEM_MANDATORY_LABEL_ACE_TYPE 0x11 // Vista Integrity ACE in SACL
#endif
#ifndef SYSTEM_MANDATORY_LABEL_NO_WRITE_UP
# define SYSTEM_MANDATORY_LABEL_NO_WRITE_UP 0x1
#endif
#ifndef SYSTEM_MANDATORY_LABEL_NO_READ_UP
# define SYSTEM_MANDATORY_LABEL_NO_READ_UP 0x2
#endif
#ifndef SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP
# define SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP 0x4
#endif
#undef strrchr
#define strrchr _mbsrchr // use the multibyte version of strrchr
BOOL gbComInitialized; // CoInitialize has been called
static DWORD gdwSdSerial = 1; // next SD serial # to allocate
#pragma warning(disable: 4057) // ignore unsigned char* vs char*
/*
Security on Windows is difficult to understand. The best way
to master it is to follow it historically.
Each layer is backward compatible. Start with Windows NT,
then study Windows 2000, XP, and Vista in that order.
Some of the cruft can be ignored. For example, object ACEs
are used only for Active Directory and are not applicable
to files or registry keys.
The complexity means it is easy to blunder when enforcing
your security policy. You need to understand innumerable rules
and special cases, many of which are barely documented or outright
undocumented.
The whole thing should be scrapped and redesigned.
*/
/*
A SID is a Security Identifier that uniquely identifies a
security principal. A security principal is a person, a computer,
or some other entity that can modify a secured object.
The SID format is S-R-I-S-S...
S identifies the series of digits as a SID (literally 'S'),
R is the revision level (always '1'),
I is the identifier-authority value (48 bits, but see below),
S is subauthority value(s) (32 bits for each).
An SID could be written in this notation as follows:
S-1-5-32-544
In this example,
The SID has a revision level of 1,
an identifier-authority value of 5,
a first subauthority value of 32,
and a second subauthority value of 544.
In this example S-1-5-32-544 is the local Administrators group.
The SID prefix is S-1-5-32 and the Relative Identifier (RID)
is 544. The RID is always the last subauthority value.
When displayed the SID string will take one of two forms. If the
IdentifierAuthority value is not greater than 2^32, then the SID
will be in the form:
S-1-5-21-2127521184-1604012920-1887927527-19009
^ ^ ^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^
| | | | | | |
+-+-+------+----------+----------+--------+--- Decimal
This is true for 99%+ of all SIDs.
In _very_ rare cases a SID will take the form:
S-1-0x206C277C6666-21-2127521184-1604012920-1887927527-19009
^ ^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^
| | | | | | |
| Hexidecimal | | | | |
+----------------+------+----------+----------+--------+--- Decimal
This exposes the fact that the SID authority is technically 48 bits.
The byte order is big-endian, unlike the rest of Windows.
To the best of my knowledge the 'large' format SID has never been used
on a real computer in production use.
99%+ of the time the IdentifierAuthority is 1, 3, 5, 16, or 80.
1 = (S-1-1-0 Everyone), 3 = (S-1-3-0 CREATOR OWNER),
5 = (S-1-5-.. User/Group/Computer), 16 = (S-1-16 Vista mandatory ACE)
80 = (S-1-80 Service SID, mapped from service name text)
*/
/////////////////////////////////////////////////////////////////////////////
//
// SID class that supports assignment (required for hash.h)
//
#define SID AESID // avoid clash with winnt.h's SID struct
class SID {
public:
SID() { m_pSid = NULL; };
SID(PSID pSid) { m_pSid = NULL; _CloneSid(pSid); };
SID(const SID& SidSrc) { m_pSid = NULL; _CloneSid(SidSrc.GetSid()); };
~SID() { _CloneSid(NULL); };
PSID GetSid() const { return m_pSid; };
SID& operator=(const SID& SidSrc) {
PSID pSidSrc = SidSrc.GetSid();
_CloneSid(pSidSrc);
return *this;
};
BOOL Equal(PSID pSid) const {
if (pSid == NULL && m_pSid == NULL) return TRUE;
if (pSid == NULL || m_pSid == NULL) return FALSE;
if (!::IsValidSid(pSid) || !::IsValidSid(m_pSid)) {
return FALSE; // corrupt SIDs are never equal
}
return ::EqualSid(pSid, m_pSid);
};
private:
void _CloneSid(PSID pSid) {
if (m_pSid == pSid) return; // important!
if (m_pSid) { delete [] (PBYTE)m_pSid; m_pSid = NULL; }
if (pSid == NULL || !::IsValidSid(pSid)) return;
DWORD dwLen = ::GetLengthSid(pSid);
m_pSid = (PSID) new BYTE[dwLen];
memcpy(m_pSid, pSid, dwLen);
};
PSID m_pSid; // really PVOID
};
BOOL operator==(const SID& sid1, const SID& sid2) {
return sid1.Equal(sid2.GetSid());
}
BOOL operator!=(const SID& sid1, const SID& sid2) {
return !sid1.Equal(sid2.GetSid());
}
//
// CHData<SID> explicit template specialization of SID as a hashable data type
//
//
// Hash the SID
//
template<> inline LONG CHData<SID>::HashVal(const SID& sid)
{
PSID pSid = sid.GetSid();
if (pSid == NULL) {
return 12345678; // arb fixed hash for NULL or bogus sid
}
if (!::IsValidSid(pSid)) {
return 78901234; // arb fixed hash for corrupt sid
}
int iLen = ::GetLengthSid(pSid);
register const unsigned char* p = (const unsigned char*)pSid;
register LONG x;
x = *p << 7;
for (register int i=iLen; --i >= 0;) {
x = (1000003*x) ^ *p++;
}
x ^= iLen;
if (x == 0 || x == -1) {
x = -2;
}
return x;
}
template<> inline BOOL CHData<SID>::Equal(const SID& sid1, const SID& sid2)
{
return sid1.Equal(sid2.GetSid());
}
#ifdef _DEBUG
template<> inline void CHData<SID>::Trace(const SID& sid)
{
UNREFERENCED_PARAMETER(&sid);
TRACE0(_T("<sid>"));
}
#endif
/////////////////////////////////////////////////////////////////////////////
//
// SECURITY_DESCRIPTOR class that supports assignment (required for hash.h)
//
class SD {
public:
SD() { m_pSd = NULL; };
SD(PSECURITY_DESCRIPTOR pSd) { m_pSd = NULL; _CloneSd(pSd); };
SD(const SD& SdSrc) { m_pSd = NULL; _CloneSd(SdSrc.GetSd()); };
~SD() { _CloneSd(NULL); };
PSECURITY_DESCRIPTOR GetSd() const { return m_pSd; };
void SetSd(PSECURITY_DESCRIPTOR psd) {
_CloneSd(psd);
}
SD& operator=(const SD& SdSrc) {
PSECURITY_DESCRIPTOR pSdSrc = SdSrc.GetSd();
_CloneSd(pSdSrc);
return *this;
};
BOOL Equal(PSECURITY_DESCRIPTOR pSd) const {
if (pSd == NULL && m_pSd == NULL) return TRUE;
if (pSd == NULL || m_pSd == NULL) return FALSE;
if (!::IsValidSecurityDescriptor(pSd) || !::IsValidSecurityDescriptor(m_pSd)) {
return FALSE; // corrupt SDs are never equal
}
DWORD dwLen = ::GetSecurityDescriptorLength(pSd);
if (dwLen != ::GetSecurityDescriptorLength(m_pSd)) {
return FALSE; // different lengths
}
return (memcmp(pSd, m_pSd, dwLen) == 0);
};
private:
void _CloneSd(PSECURITY_DESCRIPTOR pSd) {
if (m_pSd == pSd) return; // important!
if (m_pSd) { delete [] (PBYTE)m_pSd; m_pSd = NULL; }
if (pSd == NULL || !::IsValidSecurityDescriptor(pSd)) return;
DWORD dwLen = ::GetSecurityDescriptorLength(pSd);
SECURITY_DESCRIPTOR_CONTROL sdc;
DWORD dwRevision=0;
if (::GetSecurityDescriptorControl(pSd, &sdc, &dwRevision)) {
if ((sdc & SE_SELF_RELATIVE) == 0) {
error(EXIT_FAILURE, 0, "Tried to encapsulate a non-relative security descriptor.");
/*NOTREACHED*/
}
}
m_pSd = (PSECURITY_DESCRIPTOR) new BYTE[dwLen];
memcpy(m_pSd, pSd, dwLen);
};
PSECURITY_DESCRIPTOR m_pSd; // really PVOID
};
BOOL operator==(const SD& sd1, const SD& sd2) {
return sd1.Equal(sd2.GetSd());
}
BOOL operator!=(const SD& sd1, const SD& sd2) {
return !sd1.Equal(sd2.GetSd());
}
//
// CHData<SD> explicit template specialization of SD as a hashable data type
//
//
// Hash the SD
//
template<> inline LONG CHData<SD>::HashVal(const SD& sd)
{
PSECURITY_DESCRIPTOR pSd = sd.GetSd();
if (pSd == NULL) {
return 12345678; // arb fixed hash for NULL or bogus sd
}
if (!::IsValidSecurityDescriptor(pSd)) {
return 78901234; // arb fixed hash for corrupt sd
}
int iLen = ::GetSecurityDescriptorLength(pSd);
register const unsigned char* p = (const unsigned char*)pSd;
register LONG x;
x = *p << 7;
for (register int i=iLen; --i >= 0;) {
x = (1000003*x) ^ *p++;
}
x ^= iLen;
if (x == 0 || x == -1) {
x = -2;
}
return x;
}
template<> inline BOOL CHData<SD>::Equal(const SD& sd1, const SD& sd2)
{
return sd1.Equal(sd2.GetSd());
}
#ifdef _DEBUG
template<> inline void CHData<SD>::Trace(const SD& sd)
{
UNREFERENCED_PARAMETER(&sd);
TRACE0(_T("<security descriptor>"));
}
#endif
/////////////////////////////////////////////////////////////////////////////
//
// Hash sids -> user names
//
static CHash<CHData<SID>, CHData<CString> > gMapSidToName;
//
// Hash absolute file paths -> security descriptor serial #
//
static CHash<CHData<CString>, CHData<DWORD> > gMapAbsPathToSdSerial;
//
// Hash security descriptor serial # -> security descriptor
// Must be bidirectional to share SDs
//
static CHash<CHData<DWORD>, CHData<SD> > gMapSdSerialToSd;
static CHash<CHData<SD>, CHData<DWORD> > gMapSdToSdSerial;
///////////////////////////////////////////////////////////////////
//
// Get the NETBIOS security domain for this computer. Return
// the local computer name if not joined to a domain
//
// Since we dont care about spoofing, just query %USERDOMAIN%.
// No need to futz with the NetXxx APIs here. (Which is a good
// thing because on Win9x it requires a thunk to a 16-bit DLL).
//
static char *_GetSecurityDomain(void)
{
static BOOL bInit;
static char szDomain[64];
if (bInit) {
return szDomain;
}
bInit = TRUE;
//
// Note: Must use _wgetenv because getenv() returns ANSI not OEM
//
// BUG: GetEnvironmentStrings returns in OEM,
// but GetEnvironmentVariable returns ANSI!
//
wchar_t *wsz = _wgetenv(L"USERDOMAIN");
if (wsz != NULL) {
::WideCharToMultiByte(get_codepage(), 0,
wsz, -1,
szDomain, sizeof(szDomain), NULL, NULL);
}
return szDomain;
}
/////////////////////////////////////////////////////////////////////////////
//
// Translate the pSid to text sid "S-1-5-x-y-z-rid"
//
// Note: ConvertSidToStringSid is not available on NT.
//
static BOOL _GetTextualSid(PSID pSid, char *szSidBuf,
DWORD dwSidBufLen, SIDS_FORMAT eFormat)
{
PSID_IDENTIFIER_AUTHORITY pSia;
DWORD dwSubAuthorities;
DWORD dwSidLen;
if (!::IsValidSid(pSid)) return FALSE;
pSia = ::GetSidIdentifierAuthority(pSid);
dwSubAuthorities = *::GetSidSubAuthorityCount(pSid);
//
// Compute the approximate text buffer length
// S-SID_REVISION- + identifierauthority- + subauthorities- + NULL
//
dwSidLen = (15 + 12 + (12 * dwSubAuthorities) + 1) * sizeof(TCHAR);
if(dwSidBufLen < dwSidLen) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
//
// Prepare S-SID_REVISION-
//
dwSidLen = wsprintf(szSidBuf, _T("S-%lu-"), SID_REVISION );
//
// Format SID_IDENTIFIER_AUTHORITY (BYTE Value[6])
//
// SID_IDENTIFIER_AUTHORITY is 6 bytes *big endian*
//
// In practice it is always a small integer (1,3,5, or 16).
// I yet to see a valid SID with any other authority value.
//
if ( (pSia->Value[0] != 0) || (pSia->Value[1] != 0) ) {
//
// SIA is longer than 4 bytes. Probably indicates a corrupt SID.
//
dwSidLen += wsprintf(szSidBuf + dwSidLen,
_T("0x%02hx%02hx%02hx%02hx%02hx%02hx"),
(USHORT)pSia->Value[0],
(USHORT)pSia->Value[1],
(USHORT)pSia->Value[2],
(USHORT)pSia->Value[3],
(USHORT)pSia->Value[4],
(USHORT)pSia->Value[5]);
} else {
//
// SIA fits in a DWORD. Byte-flip and print it.
//
dwSidLen += wsprintf(szSidBuf + dwSidLen,
_T("%lu"),
(ULONG)(pSia->Value[5]) + // most significant byte=(S-1-*x*)
(ULONG)(pSia->Value[4] << 8) + // usually 0
(ULONG)(pSia->Value[3] << 16) + // usually 0
(ULONG)(pSia->Value[2] << 24) ); // usually 0
}
if (eFormat != sids_long) { // short or none
//
// "S-1-5-...-512"
//
dwSidLen += wsprintf(szSidBuf + dwSidLen, _T("-...-%lu"),
*GetSidSubAuthority(pSid, dwSubAuthorities-1));
} else {
//
// "S-1-5-12345678-98765432-31415926-512"
//
DWORD i;
for(i = 0 ; i < dwSubAuthorities ; i++) {
dwSidLen += wsprintf(szSidBuf + dwSidLen, _T("-%lu"),
*GetSidSubAuthority(pSid, i));
}
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
//
// Map the SID to a domain and name.
//
// If fails, use a table of well-known SIDS
//
static BOOL _LookupAccountSid(PSID pSid,
LPSTR szNameBuf, PDWORD pdwLenName,
LPSTR szDomainBuf, PDWORD pdwLenDomain,
PSID_NAME_USE peSidNameUse/*out*/)
{
//
// First query the system, as we prefer the local language name.
//
if (::LookupAccountSid(NULL, pSid,
szNameBuf, pdwLenName,
szDomainBuf, pdwLenDomain,
peSidNameUse/*out ign*/)) {
return TRUE;
}
///////////////////////////////////////////////////////////////////
//
// Lookup failed. Use our built-in table of well-known SIDs
//
struct _aWellKnownSids {
LPCSTR m_szSid;
LPCSTR m_szDomain;
LPCSTR m_szName;
enum _SID_NAME_USE m_eSidNameUse;
} aWellKnownSids[] = {
{
//
// UNDOCUMENTED:
// ::LookupAccountSids() on S-1-5-18 works on XP/Vista,
// does not work on NT, and possibly works on W2K.
//
"S-1-5-18",
"NT AUTHORITY", "SYSTEM",
SidTypeUser
},
{
"S-1-5-19",
"NT AUTHORITY", "LOCAL SERVICE",
SidTypeUser
},
{
"S-1-5-20",
"NT AUTHORITY", "NETWORK SERVICE",
SidTypeUser
},
{
//
// Used in IO inherited ACEs. Substituted with actual SID
// upon object creation.
//
"S-1-3-0",
"", "CREATOR OWNER",
SidTypeUser
},
{
//
// Vista hack for an ACE to take away WRITE_DACL|READ_CONTROL
// rights from the object owner. Usually indicates READ_CONTROL,
// which therefore denies WRITE_DACL.
//
// Changing ownership implicitly strips this ACE from the ACL.
// Therefore to bypass this ACE, change ownership to another
// user then claim it back. Fiddle as needed. Finally recreate
// the ACE.
//
"S-1-3-4",
"", "OWNER RIGHTS",
SidTypeUser
},
{
//
// S-1-5-80-w-x-y-z is a per-service SID,
// constructed textually (w-x-y-z) from the service name.
//
// Used for display when multi-booting XP
// and looking at Vista files from XP.
//
"S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464",
"NT SERVICE", "TrustedInstaller",
SidTypeUser
},
//
// SACL label ACEs
//
// Uses same text as icacls.exe
//
{
"S-1-16-0", // Defined in WinNT.h but never seen
"Mandatory Label","Untrusted Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{
"S-1-16-4096",
"Mandatory Label","Low Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{
"S-1-16-8192",
"Mandatory Label","Medium Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{
"S-1-16-8448", // Defined in WinNT for Win7 SDK, never seen
"Mandatory Label","MediumPlus Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{
"S-1-16-12288",
"Mandatory Label","High Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{
"S-1-16-16834",
"Mandatory Label","System Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{
"S-1-16-20480",
"Mandatory Label","ProtectedProcess Mandatory Level",
(SID_NAME_USE)10 // SidTypeLabel
},
{ NULL, NULL, NULL, SidTypeUser }
};
char szSidBuf[128];
if (!_GetTextualSid(pSid, szSidBuf, 128, sids_long)) {
return FALSE;
}
for (int i=0; aWellKnownSids[i].m_szName != NULL; ++i) {
if (stricmp(aWellKnownSids[i].m_szSid, szSidBuf) == 0) {
lstrcpyn(szDomainBuf, aWellKnownSids[i].m_szDomain, *pdwLenDomain);
*pdwLenDomain = strlen(szDomainBuf);
lstrcpyn(szNameBuf, aWellKnownSids[i].m_szName, *pdwLenName);
*pdwLenName = strlen(szNameBuf);
*peSidNameUse = aWellKnownSids[i].m_eSidNameUse;
return TRUE;
}
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
//
// Map the SID to a user name. Fall back to a text SID if necessary.
//
// Will always succeed unless insufficient buffer or corrupt sid
//
// Declared also in Token.cpp
//
BOOL LookupSidName(PSID pSid, LPTSTR szBuf, DWORD dwBufLen,
SIDS_FORMAT eFormat)
{
char szName[128], szDomain[128];
DWORD dwLenName, dwLenDomain;
SID_NAME_USE eSidNameUse;
//SIDS_FORMAT eFormat = (bGroup ? gids_format : sids_format);
if (!::IsValidSid(pSid)) return FALSE;
//
// Look up the SID in the hash table first
//
SID sid(pSid);
CString strName;
if (gMapSidToName.Lookup(sid, strName)) {
//
// Found a hit
//
lstrcpyn(szBuf, (LPCTSTR)strName, dwBufLen);
return TRUE;
}
dwLenDomain = sizeof(szDomain);
dwLenName = sizeof(szName);
//
// Query the LSA for the account name. Will check the local SAM first.
// If not found it will pass it up to the domain controller.
//
// BUG: No way to specify a shorter timeout if the
// domain controller is unavailable. (The default timeout
// is too long for our purposes.)
//
// WORKAROUND (not implemented): If the first lookup fails for
// a given SID prefix, cache this fact and skip checks on future
// SIDs with the same prefix.
//
if (numeric_ids || !_LookupAccountSid(pSid,
szName, &dwLenName,
szDomain, &dwLenDomain,
&eSidNameUse/*out ign*/)) {
//
// Fall back to the textual SID
//
if (!_GetTextualSid(pSid, szBuf, dwBufLen, eFormat)) {
//
// The SID is corrupt
//
return FALSE;
} else {
//
// Add to hash table
//
SID sid(pSid);
strName = szBuf;
gMapSidToName.SetAt(sid, strName);
return TRUE; // found
}
}
if (eFormat != sids_long) {
//
// Do not include szDomain if it matches the local computer name
// (not in a domain) or the default domain (in a domain)
//
// Ditto BUILTIN or NT AUTHORITY
//
if (_mbsicmp((unsigned char*)szDomain, (unsigned char*)_GetSecurityDomain()) == 0) {
szDomain[0] = '\0';
} else if (_stricmp(szDomain,"BUILTIN") == 0) {
szDomain[0] = '\0';
} else if (_stricmp(szDomain, "NT AUTHORITY") == 0) {
szDomain[0] = '\0';
} else if (_stricmp(szDomain, "Mandatory Label") == 0) {
szDomain[0] = '\0';
} else if (_stricmp(szDomain, "NT SERVICE") == 0) {
// e.g., NT SERVICE\Trusted Installer
szDomain[0] = '\0';
}
}
//
// Build "domain\name"
//
if (szDomain[0] == '\0') {
lstrcpyn(szBuf, szName, dwBufLen);
} else {
_snprintf(szBuf, dwBufLen, "%s\\%s", szDomain, szName);
}
//
// Add to hash table
//
strName = szBuf;
gMapSidToName.SetAt(sid, strName);
return TRUE; // found
}
////////////////////////////////////////////////////////////////////////
BOOL
_LoadSecurityDescriptor(struct cache_entry *ce, SD& rsd)
{
if (ce->ce_abspath == NULL) {
//
// Path is unknown
//
return FALSE;
}
//
// Return hardcoded value if --fast and not a fixed disk
//
if (run_fast &&
(ce->dwFileAttributes & FILE_ATTRIBUTE_FIXED_DISK) == 0) {
//
// --fast and not a fixed disk
//
return FALSE;
}
DWORD dwSdSerial=0;
if (gMapAbsPathToSdSerial.Lookup(ce->ce_abspath, dwSdSerial/*out*/)) {
if (gMapSdSerialToSd.Lookup(dwSdSerial, rsd)) {
//
// Found cache hit
//
return TRUE; // Use psd = sd.GetSd() to extract the psd
}
}
DWORD dwSdLen = 1024; // initial size
DWORD dwNeededSdLen;
DWORD dwFlags;
PSECURITY_DESCRIPTOR psd;
BOOL bSuccess;
dwFlags = OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION;
if (_EnableSecurityPrivilege()) { // if we have SeSecurityPrivilege
dwFlags |= SACL_SECURITY_INFORMATION; // get SACL too
if (IsVista) {
//
// UNDOCUMENTED: Required to get S-1-16-xxxx SACL ACEs for
// mandatory integrity labels
//
dwFlags |= LABEL_SECURITY_INFORMATION; // get integrity labels too
}
}
do {
dwNeededSdLen = 0;
psd = (PSECURITY_DESCRIPTOR) alloca(dwSdLen);
///////////////////////////////////////////////////////////////////
//
// Get the file's security description
//
// WARNING: This call is incredibly *slow* over a network, especially
// when querying all the files in a directory (ls -l)
//
// Thus the reason for the --fast switch, to avoid this call
// on network files.
//
// DESIGN BUG: Microsoft really should provide a way to batch
// these calls over the wire to avoid the excessive rounds-trips.
// Perhaps start pre-fetching if there are repeated calls within
// the same folder?
//
///////////////////////////////////////////////////////////////////
if (gbReg) {
bSuccess = _GetRegSecurity(ce->ce_abspath, ce,
dwFlags, psd, dwSdLen, &dwNeededSdLen);
} else {
PVOID pOldState = _push_64bitfs();
bSuccess = ::GetFileSecurity(ce->ce_abspath,
dwFlags, psd, dwSdLen, &dwNeededSdLen);
_pop_64bitfs(pOldState);
}
if (bSuccess) {
//
// Cache the hit so we will never again
// GetFileSecurity on this file.
//
rsd.SetSd(psd); // return the descriptor in sd (does heap copy)
//
// Map abspath -> serial # -> psd
//
// This technique saves memory by sharing SDs that are identical
//
dwSdSerial=0;
if (gMapSdToSdSerial.Lookup(rsd, dwSdSerial/*out*/)) {
gMapAbsPathToSdSerial.SetAt(ce->ce_abspath, dwSdSerial);
} else {
gMapAbsPathToSdSerial.SetAt(ce->ce_abspath, gdwSdSerial);
gMapSdSerialToSd.SetAt(gdwSdSerial, rsd);
gMapSdToSdSerial.SetAt(rsd, gdwSdSerial);
++gdwSdSerial;
}
} else {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && dwNeededSdLen < 65536 && dwSdLen < 65536) {
//
// Grow size and try again
//
if (dwNeededSdLen) {
dwSdLen = dwNeededSdLen + 32;
} else {
dwSdLen += 1024;
}
} else {
return FALSE; // Fail; possibly due to Win9x stub
}
}
} while (!bSuccess);
//
// Got psd ok
//
return TRUE;
}
////////////////////////////////////////////////////////////////////////
//
// Get the name of the owner (or group) for the file
//
static BOOL
_GetOwnerName(struct cache_entry *ce, char *szUserBuf, DWORD dwUserBufLen,
BOOL bGroup)
{
SIDS_FORMAT eFormat = (bGroup ? gids_format : sids_format);
SD sd;
BOOL bDefaulted = FALSE;
BOOL bSuccess;
if (eFormat == sids_none || !_LoadSecurityDescriptor(ce, sd)) {
//
// We return "0" to keep the number of columns the same.
//
// Needed for perl scripts that expect exactly 9 columns in the output
// Ditto Emacs.
//
lstrcpyn(szUserBuf, "0", dwUserBufLen);
return TRUE;
}
//
// Got psd
//
PSECURITY_DESCRIPTOR psd = sd.GetSd();
//
// Dig out the owner or group SID
//
PSID pSid;
if (!bGroup) {
bSuccess = ::GetSecurityDescriptorOwner(psd, &pSid, &bDefaulted);
} else {
bSuccess = ::GetSecurityDescriptorGroup(psd, &pSid, &bDefaulted);
}
if (!bSuccess) {
return FALSE;
}
//
// Convert SID to name
//
bSuccess = LookupSidName(pSid, szUserBuf, dwUserBufLen, eFormat);
return bSuccess;
}
///////////////////////////////////////////////////////
//
// Return the user name or POSIX group name of the file.
//
extern "C" char *
xgetuser(struct cache_entry *ce, int bGroup)
{
static char szOwnerBuf[128]; // not thread safe
if (!_GetOwnerName(ce, szOwnerBuf, 128, bGroup/*gid*/)) {
return NULL;
}
return szOwnerBuf;
}
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
//
// Define a COM object to implement the callbacks
// ISecurityInformation and IEffectivePermission.
//
// EditSecurity() interrogates these interfaces
// in order to display the file's security using
// a fancy property sheet.
//
class CComDialogInfo : // COM object
public ISecurityInformation,
public IEffectivePermission
#ifdef UNDEFINED
, public ISecurityInformation2
#endif
{
public:
CComDialogInfo(struct cache_entry *ce, PSECURITY_DESCRIPTOR psd) :
m_sd(psd), m_sdOrig(psd) {
m_cRef = 0;
m_ce = ce;
m_psd = m_sd.GetSd();
m_psdOrig = m_sdOrig.GetSd();
if (m_psd == NULL || m_psdOrig == NULL) {
error(EXIT_FAILURE, 0, "Bad security descriptor.");
/*NOTREACHED*/
}
}
//
// DESIGN BUG: A quirk of C++ is that the destructor in a derived class
// should *always* be virtual.
//
// Otherwise the derived class destructor is never called if the object
// is deleted through its base pointer, meaning that the derived member
// objects are never deleted!
//
// (Assignment operators in a derived class should also be virtual for
// similar reasons.)
//
virtual ~CComDialogInfo() { } // implicitly destructs member objects m_sd, etc.
////////////////////////////////////////////////////////////////////////