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