1 // PropIDUtils.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/CpuArch.h"
6
7 #include "../../../Common/IntToString.h"
8 #include "../../../Common/StringConvert.h"
9
10 #include "../../../Windows/FileIO.h"
11 #include "../../../Windows/PropVariantConv.h"
12
13 #include "../../PropID.h"
14
15 #include "PropIDUtils.h"
16
17 #define Get16(x) GetUi16(x)
18 #define Get32(x) GetUi32(x)
19
20 using namespace NWindows;
21
22 static const unsigned kNumWinAtrribFlags = 21;
23 static const char g_WinAttribChars[kNumWinAtrribFlags + 1] = "RHS8DAdNTsLCOIEV.X.PU";
24
25 /*
26 FILE_ATTRIBUTE_
27
28 0 READONLY
29 1 HIDDEN
30 2 SYSTEM
31 3 (Volume label - obsolete)
32 4 DIRECTORY
33 5 ARCHIVE
34 6 DEVICE
35 7 NORMAL
36 8 TEMPORARY
37 9 SPARSE_FILE
38 10 REPARSE_POINT
39 11 COMPRESSED
40 12 OFFLINE
41 13 NOT_CONTENT_INDEXED (I - Win10 attrib/Explorer)
42 14 ENCRYPTED
43 15 INTEGRITY_STREAM (V - ReFS Win8/Win2012)
44 16 VIRTUAL (reserved)
45 17 NO_SCRUB_DATA (X - ReFS Win8/Win2012 attrib)
46 18 RECALL_ON_OPEN or EA
47 19 PINNED
48 20 UNPINNED
49 21 STRICTLY_SEQUENTIAL
50 22 RECALL_ON_DATA_ACCESS
51 */
52
53
54 static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' };
55 #define MY_ATTR_CHAR(a, n, c) ((a) & (1 << (n))) ? c : '-';
56
ConvertPosixAttribToString(char * s,UInt32 a)57 static void ConvertPosixAttribToString(char *s, UInt32 a) throw()
58 {
59 s[0] = kPosixTypes[(a >> 12) & 0xF];
60 for (int i = 6; i >= 0; i -= 3)
61 {
62 s[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r');
63 s[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w');
64 s[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x');
65 }
66 if ((a & 0x800) != 0) s[3] = ((a & (1 << 6)) ? 's' : 'S');
67 if ((a & 0x400) != 0) s[6] = ((a & (1 << 3)) ? 's' : 'S');
68 if ((a & 0x200) != 0) s[9] = ((a & (1 << 0)) ? 't' : 'T');
69 s[10] = 0;
70
71 a &= ~(UInt32)0xFFFF;
72 if (a != 0)
73 {
74 s[10] = ' ';
75 ConvertUInt32ToHex8Digits(a, s + 11);
76 }
77 }
78
79
ConvertWinAttribToString(char * s,UInt32 wa)80 void ConvertWinAttribToString(char *s, UInt32 wa) throw()
81 {
82 /*
83 some programs store posix attributes in high 16 bits.
84 p7zip - stores additional 0x8000 flag marker.
85 macos - stores additional 0x4000 flag marker.
86 info-zip - no additional marker.
87 */
88
89 bool isPosix = ((wa & 0xF0000000) != 0);
90
91 UInt32 posix = 0;
92 if (isPosix)
93 {
94 posix = wa >> 16;
95 wa &= (UInt32)0x3FFF;
96 }
97
98 for (unsigned i = 0; i < kNumWinAtrribFlags; i++)
99 {
100 UInt32 flag = (1 << i);
101 if ((wa & flag) != 0)
102 {
103 char c = g_WinAttribChars[i];
104 if (c != '.')
105 {
106 wa &= ~flag;
107 // if (i != 7) // we can disable N (NORMAL) printing
108 *s++ = c;
109 }
110 }
111 }
112
113 if (wa != 0)
114 {
115 *s++ = ' ';
116 ConvertUInt32ToHex8Digits(wa, s);
117 s += strlen(s);
118 }
119
120 *s = 0;
121
122 if (isPosix)
123 {
124 *s++ = ' ';
125 ConvertPosixAttribToString(s, posix);
126 }
127 }
128
129
ConvertPropertyToShortString2(char * dest,const PROPVARIANT & prop,PROPID propID,int level)130 void ConvertPropertyToShortString2(char *dest, const PROPVARIANT &prop, PROPID propID, int level) throw()
131 {
132 *dest = 0;
133
134 if (prop.vt == VT_FILETIME)
135 {
136 const FILETIME &ft = prop.filetime;
137 if ((ft.dwHighDateTime == 0 &&
138 ft.dwLowDateTime == 0))
139 return;
140 ConvertUtcFileTimeToString(prop.filetime, dest, level);
141 return;
142 }
143
144 switch (propID)
145 {
146 case kpidCRC:
147 {
148 if (prop.vt != VT_UI4)
149 break;
150 ConvertUInt32ToHex8Digits(prop.ulVal, dest);
151 return;
152 }
153 case kpidAttrib:
154 {
155 if (prop.vt != VT_UI4)
156 break;
157 UInt32 a = prop.ulVal;
158
159 /*
160 if ((a & 0x8000) && (a & 0x7FFF) == 0)
161 ConvertPosixAttribToString(dest, a >> 16);
162 else
163 */
164 ConvertWinAttribToString(dest, a);
165 return;
166 }
167 case kpidPosixAttrib:
168 {
169 if (prop.vt != VT_UI4)
170 break;
171 ConvertPosixAttribToString(dest, prop.ulVal);
172 return;
173 }
174 case kpidINode:
175 {
176 if (prop.vt != VT_UI8)
177 break;
178 ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest);
179 dest += strlen(dest);
180 *dest++ = '-';
181 UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1);
182 ConvertUInt64ToString(low, dest);
183 return;
184 }
185 case kpidVa:
186 {
187 UInt64 v = 0;
188 if (prop.vt == VT_UI4)
189 v = prop.ulVal;
190 else if (prop.vt == VT_UI8)
191 v = (UInt64)prop.uhVal.QuadPart;
192 else
193 break;
194 dest[0] = '0';
195 dest[1] = 'x';
196 ConvertUInt64ToHex(v, dest + 2);
197 return;
198 }
199 }
200
201 ConvertPropVariantToShortString(prop, dest);
202 }
203
ConvertPropertyToString2(UString & dest,const PROPVARIANT & prop,PROPID propID,int level)204 void ConvertPropertyToString2(UString &dest, const PROPVARIANT &prop, PROPID propID, int level)
205 {
206 if (prop.vt == VT_BSTR)
207 {
208 dest.SetFromBstr(prop.bstrVal);
209 return;
210 }
211 char temp[64];
212 ConvertPropertyToShortString2(temp, prop, propID, level);
213 dest = temp;
214 }
215
GetHex(unsigned v)216 static inline unsigned GetHex(unsigned v)
217 {
218 return (v < 10) ? ('0' + v) : ('A' + (v - 10));
219 }
220
221 #ifndef _SFX
222
AddHexToString(AString & res,unsigned v)223 static inline void AddHexToString(AString &res, unsigned v)
224 {
225 res += (char)GetHex(v >> 4);
226 res += (char)GetHex(v & 0xF);
227 }
228
229 /*
230 static AString Data_To_Hex(const Byte *data, size_t size)
231 {
232 AString s;
233 for (size_t i = 0; i < size; i++)
234 AddHexToString(s, data[i]);
235 return s;
236 }
237 */
238
239 static const char * const sidNames[] =
240 {
241 "0"
242 , "Dialup"
243 , "Network"
244 , "Batch"
245 , "Interactive"
246 , "Logon" // S-1-5-5-X-Y
247 , "Service"
248 , "Anonymous"
249 , "Proxy"
250 , "EnterpriseDC"
251 , "Self"
252 , "AuthenticatedUsers"
253 , "RestrictedCode"
254 , "TerminalServer"
255 , "RemoteInteractiveLogon"
256 , "ThisOrganization"
257 , "16"
258 , "IUserIIS"
259 , "LocalSystem"
260 , "LocalService"
261 , "NetworkService"
262 , "Domains"
263 };
264
265 struct CSecID2Name
266 {
267 UInt32 n;
268 const char *sz;
269 };
270
FindPairIndex(const CSecID2Name * pairs,unsigned num,UInt32 id)271 static int FindPairIndex(const CSecID2Name * pairs, unsigned num, UInt32 id)
272 {
273 for (unsigned i = 0; i < num; i++)
274 if (pairs[i].n == id)
275 return i;
276 return -1;
277 }
278
279 static const CSecID2Name sid_32_Names[] =
280 {
281 { 544, "Administrators" },
282 { 545, "Users" },
283 { 546, "Guests" },
284 { 547, "PowerUsers" },
285 { 548, "AccountOperators" },
286 { 549, "ServerOperators" },
287 { 550, "PrintOperators" },
288 { 551, "BackupOperators" },
289 { 552, "Replicators" },
290 { 553, "Backup Operators" },
291 { 554, "PreWindows2000CompatibleAccess" },
292 { 555, "RemoteDesktopUsers" },
293 { 556, "NetworkConfigurationOperators" },
294 { 557, "IncomingForestTrustBuilders" },
295 { 558, "PerformanceMonitorUsers" },
296 { 559, "PerformanceLogUsers" },
297 { 560, "WindowsAuthorizationAccessGroup" },
298 { 561, "TerminalServerLicenseServers" },
299 { 562, "DistributedCOMUsers" },
300 { 569, "CryptographicOperators" },
301 { 573, "EventLogReaders" },
302 { 574, "CertificateServiceDCOMAccess" }
303 };
304
305 static const CSecID2Name sid_21_Names[] =
306 {
307 { 500, "Administrator" },
308 { 501, "Guest" },
309 { 502, "KRBTGT" },
310 { 512, "DomainAdmins" },
311 { 513, "DomainUsers" },
312 { 515, "DomainComputers" },
313 { 516, "DomainControllers" },
314 { 517, "CertPublishers" },
315 { 518, "SchemaAdmins" },
316 { 519, "EnterpriseAdmins" },
317 { 520, "GroupPolicyCreatorOwners" },
318 { 553, "RASandIASServers" },
319 { 553, "RASandIASServers" },
320 { 571, "AllowedRODCPasswordReplicationGroup" },
321 { 572, "DeniedRODCPasswordReplicationGroup" }
322 };
323
324 struct CServicesToName
325 {
326 UInt32 n[5];
327 const char *sz;
328 };
329
330 static const CServicesToName services_to_name[] =
331 {
332 { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" }
333 };
334
ParseSid(AString & s,const Byte * p,UInt32 lim,UInt32 & sidSize)335 static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize)
336 {
337 sidSize = 0;
338 if (lim < 8)
339 {
340 s += "ERROR";
341 return;
342 }
343 UInt32 rev = p[0];
344 if (rev != 1)
345 {
346 s += "UNSUPPORTED";
347 return;
348 }
349 UInt32 num = p[1];
350 if (8 + num * 4 > lim)
351 {
352 s += "ERROR";
353 return;
354 }
355 sidSize = 8 + num * 4;
356 UInt32 authority = GetBe32(p + 4);
357
358 if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1)
359 {
360 UInt32 v0 = Get32(p + 8);
361 if (v0 < ARRAY_SIZE(sidNames))
362 {
363 s += sidNames[v0];
364 return;
365 }
366 if (v0 == 32 && num == 2)
367 {
368 UInt32 v1 = Get32(p + 12);
369 int index = FindPairIndex(sid_32_Names, ARRAY_SIZE(sid_32_Names), v1);
370 if (index >= 0)
371 {
372 s += sid_32_Names[(unsigned)index].sz;
373 return;
374 }
375 }
376 if (v0 == 21 && num == 5)
377 {
378 UInt32 v4 = Get32(p + 8 + 4 * 4);
379 int index = FindPairIndex(sid_21_Names, ARRAY_SIZE(sid_21_Names), v4);
380 if (index >= 0)
381 {
382 s += sid_21_Names[(unsigned)index].sz;
383 return;
384 }
385 }
386 if (v0 == 80 && num == 6)
387 {
388 for (unsigned i = 0; i < ARRAY_SIZE(services_to_name); i++)
389 {
390 const CServicesToName &sn = services_to_name[i];
391 int j;
392 for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++);
393 if (j == 5)
394 {
395 s += sn.sz;
396 return;
397 }
398 }
399 }
400 }
401
402 s += "S-1-";
403 if (p[2] == 0 && p[3] == 0)
404 s.Add_UInt32(authority);
405 else
406 {
407 s += "0x";
408 for (int i = 2; i < 8; i++)
409 AddHexToString(s, p[i]);
410 }
411 for (UInt32 i = 0; i < num; i++)
412 {
413 s += '-';
414 s.Add_UInt32(Get32(p + 8 + i * 4));
415 }
416 }
417
ParseOwner(AString & s,const Byte * p,UInt32 size,UInt32 pos)418 static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos)
419 {
420 if (pos > size)
421 {
422 s += "ERROR";
423 return;
424 }
425 UInt32 sidSize = 0;
426 ParseSid(s, p + pos, size - pos, sidSize);
427 }
428
ParseAcl(AString & s,const Byte * p,UInt32 size,const char * strName,UInt32 flags,UInt32 offset)429 static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset)
430 {
431 UInt32 control = Get16(p + 2);
432 if ((flags & control) == 0)
433 return;
434 UInt32 pos = Get32(p + offset);
435 s.Add_Space();
436 s += strName;
437 if (pos >= size)
438 return;
439 p += pos;
440 size -= pos;
441 if (size < 8)
442 return;
443 if (Get16(p) != 2) // revision
444 return;
445 UInt32 num = Get32(p + 4);
446 s.Add_UInt32(num);
447
448 /*
449 UInt32 aclSize = Get16(p + 2);
450 if (num >= (1 << 16))
451 return;
452 if (aclSize > size)
453 return;
454 size = aclSize;
455 size -= 8;
456 p += 8;
457 for (UInt32 i = 0 ; i < num; i++)
458 {
459 if (size <= 8)
460 return;
461 // Byte type = p[0];
462 // Byte flags = p[1];
463 // UInt32 aceSize = Get16(p + 2);
464 // UInt32 mask = Get32(p + 4);
465 p += 8;
466 size -= 8;
467
468 UInt32 sidSize = 0;
469 s.Add_Space();
470 ParseSid(s, p, size, sidSize);
471 if (sidSize == 0)
472 return;
473 p += sidSize;
474 size -= sidSize;
475 }
476
477 // the tail can contain zeros. So (size != 0) is not ERROR
478 // if (size != 0) s += " ERROR";
479 */
480 }
481
482 #define MY_SE_OWNER_DEFAULTED (0x0001)
483 #define MY_SE_GROUP_DEFAULTED (0x0002)
484 #define MY_SE_DACL_PRESENT (0x0004)
485 #define MY_SE_DACL_DEFAULTED (0x0008)
486 #define MY_SE_SACL_PRESENT (0x0010)
487 #define MY_SE_SACL_DEFAULTED (0x0020)
488 #define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100)
489 #define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200)
490 #define MY_SE_DACL_AUTO_INHERITED (0x0400)
491 #define MY_SE_SACL_AUTO_INHERITED (0x0800)
492 #define MY_SE_DACL_PROTECTED (0x1000)
493 #define MY_SE_SACL_PROTECTED (0x2000)
494 #define MY_SE_RM_CONTROL_VALID (0x4000)
495 #define MY_SE_SELF_RELATIVE (0x8000)
496
ConvertNtSecureToString(const Byte * data,UInt32 size,AString & s)497 void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s)
498 {
499 s.Empty();
500 if (size < 20 || size > (1 << 18))
501 {
502 s += "ERROR";
503 return;
504 }
505 if (Get16(data) != 1) // revision
506 {
507 s += "UNSUPPORTED";
508 return;
509 }
510 ParseOwner(s, data, size, Get32(data + 4));
511 s.Add_Space();
512 ParseOwner(s, data, size, Get32(data + 8));
513 ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12);
514 ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16);
515 s.Add_Space();
516 s.Add_UInt32(size);
517 // s += '\n';
518 // s += Data_To_Hex(data, size);
519 }
520
521 #ifdef _WIN32
522
CheckSid(const Byte * data,UInt32 size,UInt32 pos)523 static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) throw()
524 {
525 if (pos >= size)
526 return false;
527 size -= pos;
528 if (size < 8)
529 return false;
530 UInt32 rev = data[pos];
531 if (rev != 1)
532 return false;
533 UInt32 num = data[pos + 1];
534 return (8 + num * 4 <= size);
535 }
536
CheckAcl(const Byte * p,UInt32 size,UInt32 flags,UInt32 offset)537 static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) throw()
538 {
539 UInt32 control = Get16(p + 2);
540 if ((flags & control) == 0)
541 return true;
542 UInt32 pos = Get32(p + offset);
543 if (pos >= size)
544 return false;
545 p += pos;
546 size -= pos;
547 if (size < 8)
548 return false;
549 UInt32 aclSize = Get16(p + 2);
550 return (aclSize <= size);
551 }
552
CheckNtSecure(const Byte * data,UInt32 size)553 bool CheckNtSecure(const Byte *data, UInt32 size) throw()
554 {
555 if (size < 20)
556 return false;
557 if (Get16(data) != 1) // revision
558 return true; // windows function can handle such error, so we allow it
559 if (size > (1 << 18))
560 return false;
561 if (!CheckSid(data, size, Get32(data + 4))) return false;
562 if (!CheckSid(data, size, Get32(data + 8))) return false;
563 if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false;
564 if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false;
565 return true;
566 }
567
568 #endif
569
570
571
572 // IO_REPARSE_TAG_*
573
574 static const CSecID2Name k_ReparseTags[] =
575 {
576 { 0xA0000003, "MOUNT_POINT" },
577 { 0xC0000004, "HSM" },
578 { 0x80000005, "DRIVE_EXTENDER" },
579 { 0x80000006, "HSM2" },
580 { 0x80000007, "SIS" },
581 { 0x80000008, "WIM" },
582 { 0x80000009, "CSV" },
583 { 0x8000000A, "DFS" },
584 { 0x8000000B, "FILTER_MANAGER" },
585 { 0xA000000C, "SYMLINK" },
586 { 0xA0000010, "IIS_CACHE" },
587 { 0x80000012, "DFSR" },
588 { 0x80000013, "DEDUP" },
589 { 0xC0000014, "APPXSTRM" },
590 { 0x80000014, "NFS" },
591 { 0x80000015, "FILE_PLACEHOLDER" },
592 { 0x80000016, "DFM" },
593 { 0x80000017, "WOF" }
594 };
595
ConvertNtReparseToString(const Byte * data,UInt32 size,UString & s)596 bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s)
597 {
598 s.Empty();
599 NFile::CReparseAttr attr;
600 DWORD errorCode = 0;
601 if (attr.Parse(data, size, errorCode))
602 {
603 if (!attr.IsSymLink())
604 s += "Junction: ";
605 s += attr.GetPath();
606 if (!attr.IsOkNamePair())
607 {
608 s += " : ";
609 s += attr.PrintName;
610 }
611 return true;
612 }
613
614 if (size < 8)
615 return false;
616 UInt32 tag = Get32(data);
617 UInt32 len = Get16(data + 4);
618 if (len + 8 > size)
619 return false;
620 if (Get16(data + 6) != 0) // padding
621 return false;
622
623 /*
624 #define _my_IO_REPARSE_TAG_DEDUP (0x80000013L)
625 if (tag == _my_IO_REPARSE_TAG_DEDUP)
626 {
627 }
628 */
629
630 {
631 int index = FindPairIndex(k_ReparseTags, ARRAY_SIZE(k_ReparseTags), tag);
632 if (index >= 0)
633 s += k_ReparseTags[(unsigned)index].sz;
634 else
635 {
636 s += "REPARSE:";
637 char hex[16];
638 ConvertUInt32ToHex8Digits(tag, hex);
639 s += hex;
640 }
641 }
642
643 s += ":";
644 s.Add_UInt32(len);
645
646 if (len != 0)
647 {
648 s.Add_Space();
649
650 data += 8;
651
652 for (UInt32 i = 0; i < len; i++)
653 {
654 if (i >= 8)
655 {
656 s += "...";
657 break;
658 }
659 unsigned b = data[i];
660 s += (char)GetHex((b >> 4) & 0xF);
661 s += (char)GetHex(b & 0xF);
662 }
663 }
664
665 return true;
666 }
667
668 #endif
669