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
20using namespace NWindows;
21
22static const char g_WinAttribChars[16 + 1] = "RHS8DAdNTsLCOnE_";
23/*
240 READONLY
251 HIDDEN
262 SYSTEM
27
284 DIRECTORY
295 ARCHIVE
306 DEVICE
317 NORMAL
328 TEMPORARY
339 SPARSE_FILE
3410 REPARSE_POINT
3511 COMPRESSED
3612 OFFLINE
3713 NOT_CONTENT_INDEXED
3814 ENCRYPTED
39
4016 VIRTUAL
41*/
42
43static const char kPosixTypes[16] = { '0', 'p', 'c', '3', 'd', '5', 'b', '7', '-', '9', 'l', 'B', 's', 'D', 'E', 'F' };
44#define MY_ATTR_CHAR(a, n, c) ((a) & (1 << (n))) ? c : '-';
45
46static void ConvertPosixAttribToString(char *s, UInt32 a) throw()
47{
48  s[0] = kPosixTypes[(a >> 12) & 0xF];
49  for (int i = 6; i >= 0; i -= 3)
50  {
51    s[7 - i] = MY_ATTR_CHAR(a, i + 2, 'r');
52    s[8 - i] = MY_ATTR_CHAR(a, i + 1, 'w');
53    s[9 - i] = MY_ATTR_CHAR(a, i + 0, 'x');
54  }
55  if ((a & 0x800) != 0) s[3] = ((a & (1 << 6)) ? 's' : 'S');
56  if ((a & 0x400) != 0) s[6] = ((a & (1 << 3)) ? 's' : 'S');
57  if ((a & 0x200) != 0) s[9] = ((a & (1 << 0)) ? 't' : 'T');
58  s[10] = 0;
59
60  a &= ~(UInt32)0xFFFF;
61  if (a != 0)
62  {
63    s[10] = ' ';
64    ConvertUInt32ToHex8Digits(a, s + 11);
65  }
66}
67
68void ConvertWinAttribToString(char *s, UInt32 wa) throw()
69{
70  for (int i = 0; i < 16; i++)
71    if ((wa & (1 << i)) && i != 7)
72      *s++ = g_WinAttribChars[i];
73  *s = 0;
74
75  // we support p7zip trick that stores posix attributes in high 16 bits, and 0x8000 flag
76  // we also support ZIP archives created in Unix, that store posix attributes in high 16 bits without 0x8000 flag
77
78  // if (wa & 0x8000)
79  if ((wa >> 16) != 0)
80  {
81    *s++ = ' ';
82    ConvertPosixAttribToString(s, wa >> 16);
83  }
84}
85
86void ConvertPropertyToShortString(char *dest, const PROPVARIANT &prop, PROPID propID, bool full) throw()
87{
88  *dest = 0;
89
90  if (prop.vt == VT_FILETIME)
91  {
92    FILETIME localFileTime;
93    if ((prop.filetime.dwHighDateTime == 0 &&
94        prop.filetime.dwLowDateTime == 0) ||
95        !::FileTimeToLocalFileTime(&prop.filetime, &localFileTime))
96      return;
97    ConvertFileTimeToString(localFileTime, dest, true, full);
98    return;
99  }
100
101  switch (propID)
102  {
103    case kpidCRC:
104    {
105      if (prop.vt != VT_UI4)
106        break;
107      ConvertUInt32ToHex8Digits(prop.ulVal, dest);
108      return;
109    }
110    case kpidAttrib:
111    {
112      if (prop.vt != VT_UI4)
113        break;
114      UInt32 a = prop.ulVal;
115
116      /*
117      if ((a & 0x8000) && (a & 0x7FFF) == 0)
118        ConvertPosixAttribToString(dest, a >> 16);
119      else
120      */
121      ConvertWinAttribToString(dest, a);
122      return;
123    }
124    case kpidPosixAttrib:
125    {
126      if (prop.vt != VT_UI4)
127        break;
128      ConvertPosixAttribToString(dest, prop.ulVal);
129      return;
130    }
131    case kpidINode:
132    {
133      if (prop.vt != VT_UI8)
134        break;
135      ConvertUInt32ToString((UInt32)(prop.uhVal.QuadPart >> 48), dest);
136      dest += strlen(dest);
137      *dest++ = '-';
138      UInt64 low = prop.uhVal.QuadPart & (((UInt64)1 << 48) - 1);
139      ConvertUInt64ToString(low, dest);
140      return;
141    }
142    case kpidVa:
143    {
144      UInt64 v = 0;
145      if (prop.vt == VT_UI4)
146        v = prop.ulVal;
147      else if (prop.vt == VT_UI8)
148        v = (UInt64)prop.uhVal.QuadPart;
149      else
150        break;
151      dest[0] = '0';
152      dest[1] = 'x';
153      ConvertUInt64ToHex(v, dest + 2);
154      return;
155    }
156  }
157
158  ConvertPropVariantToShortString(prop, dest);
159}
160
161void ConvertPropertyToString(UString &dest, const PROPVARIANT &prop, PROPID propID, bool full)
162{
163  if (prop.vt == VT_BSTR)
164  {
165    dest.SetFromBstr(prop.bstrVal);
166    return;
167  }
168  char temp[64];
169  ConvertPropertyToShortString(temp, prop, propID, full);
170  dest.SetFromAscii(temp);
171}
172
173static inline unsigned GetHex(unsigned v)
174{
175  return (v < 10) ? ('0' + v) : ('A' + (v - 10));
176}
177
178#ifndef _SFX
179
180static inline void AddHexToString(AString &res, unsigned v)
181{
182  res += (char)GetHex(v >> 4);
183  res += (char)GetHex(v & 0xF);
184  res += ' ';
185}
186
187/*
188static AString Data_To_Hex(const Byte *data, size_t size)
189{
190  AString s;
191  for (size_t i = 0; i < size; i++)
192    AddHexToString(s, data[i]);
193  return s;
194}
195*/
196
197static const char * const sidNames[] =
198{
199    "0"
200  , "Dialup"
201  , "Network"
202  , "Batch"
203  , "Interactive"
204  , "Logon"  // S-1-5-5-X-Y
205  , "Service"
206  , "Anonymous"
207  , "Proxy"
208  , "EnterpriseDC"
209  , "Self"
210  , "AuthenticatedUsers"
211  , "RestrictedCode"
212  , "TerminalServer"
213  , "RemoteInteractiveLogon"
214  , "ThisOrganization"
215  , "16"
216  , "IUserIIS"
217  , "LocalSystem"
218  , "LocalService"
219  , "NetworkService"
220  , "Domains"
221};
222
223struct CSecID2Name
224{
225  UInt32 n;
226  const char *sz;
227};
228
229static const CSecID2Name sid_32_Names[] =
230{
231  { 544, "Administrators" },
232  { 545, "Users" },
233  { 546, "Guests" },
234  { 547, "PowerUsers" },
235  { 548, "AccountOperators" },
236  { 549, "ServerOperators" },
237  { 550, "PrintOperators" },
238  { 551, "BackupOperators" },
239  { 552, "Replicators" },
240  { 553, "Backup Operators" },
241  { 554, "PreWindows2000CompatibleAccess" },
242  { 555, "RemoteDesktopUsers" },
243  { 556, "NetworkConfigurationOperators" },
244  { 557, "IncomingForestTrustBuilders" },
245  { 558, "PerformanceMonitorUsers" },
246  { 559, "PerformanceLogUsers" },
247  { 560, "WindowsAuthorizationAccessGroup" },
248  { 561, "TerminalServerLicenseServers" },
249  { 562, "DistributedCOMUsers" },
250  { 569, "CryptographicOperators" },
251  { 573, "EventLogReaders" },
252  { 574, "CertificateServiceDCOMAccess" }
253};
254
255static const CSecID2Name sid_21_Names[] =
256{
257  { 500, "Administrator" },
258  { 501, "Guest" },
259  { 502, "KRBTGT" },
260  { 512, "DomainAdmins" },
261  { 513, "DomainUsers" },
262  { 515, "DomainComputers" },
263  { 516, "DomainControllers" },
264  { 517, "CertPublishers" },
265  { 518, "SchemaAdmins" },
266  { 519, "EnterpriseAdmins" },
267  { 520, "GroupPolicyCreatorOwners" },
268  { 553, "RASandIASServers" },
269  { 553, "RASandIASServers" },
270  { 571, "AllowedRODCPasswordReplicationGroup" },
271  { 572, "DeniedRODCPasswordReplicationGroup" }
272};
273
274struct CServicesToName
275{
276  UInt32 n[5];
277  const char *sz;
278};
279
280static const CServicesToName services_to_name[] =
281{
282  { { 0x38FB89B5, 0xCBC28419, 0x6D236C5C, 0x6E770057, 0x876402C0 } , "TrustedInstaller" }
283};
284
285static void ParseSid(AString &s, const Byte *p, UInt32 lim, UInt32 &sidSize)
286{
287  sidSize = 0;
288  if (lim < 8)
289  {
290    s += "ERROR";
291    return;
292  }
293  UInt32 rev = p[0];
294  if (rev != 1)
295  {
296    s += "UNSUPPORTED";
297    return;
298  }
299  UInt32 num = p[1];
300  if (8 + num * 4 > lim)
301  {
302    s += "ERROR";
303    return;
304  }
305  sidSize = 8 + num * 4;
306  UInt32 authority = GetBe32(p + 4);
307
308  if (p[2] == 0 && p[3] == 0 && authority == 5 && num >= 1)
309  {
310    UInt32 v0 = Get32(p + 8);
311    if (v0 < ARRAY_SIZE(sidNames))
312    {
313      s += sidNames[v0];
314      return;
315    }
316    if (v0 == 32 && num == 2)
317    {
318      UInt32 v1 = Get32(p + 12);
319      for (unsigned i = 0; i < ARRAY_SIZE(sid_32_Names); i++)
320        if (sid_32_Names[i].n == v1)
321        {
322          s += sid_32_Names[i].sz;
323          return;
324        }
325    }
326    if (v0 == 21 && num == 5)
327    {
328      UInt32 v4 = Get32(p + 8 + 4 * 4);
329      for (unsigned i = 0; i < ARRAY_SIZE(sid_21_Names); i++)
330        if (sid_21_Names[i].n == v4)
331        {
332          s += sid_21_Names[i].sz;
333          return;
334        }
335    }
336    if (v0 == 80 && num == 6)
337    {
338      for (unsigned i = 0; i < ARRAY_SIZE(services_to_name); i++)
339      {
340        const CServicesToName &sn = services_to_name[i];
341        int j;
342        for (j = 0; j < 5 && sn.n[j] == Get32(p + 8 + 4 + j * 4); j++);
343        if (j == 5)
344        {
345          s += sn.sz;
346          return;
347        }
348      }
349    }
350  }
351
352  char sz[16];
353  s += "S-1-";
354  if (p[2] == 0 && p[3] == 0)
355  {
356    ConvertUInt32ToString(authority, sz);
357    s += sz;
358  }
359  else
360  {
361    s += "0x";
362    for (int i = 2; i < 8; i++)
363      AddHexToString(s, p[i]);
364  }
365  for (UInt32 i = 0; i < num; i++)
366  {
367    s += '-';
368    ConvertUInt32ToString(Get32(p + 8 + i * 4), sz);
369    s += sz;
370  }
371}
372
373static void ParseOwner(AString &s, const Byte *p, UInt32 size, UInt32 pos)
374{
375  if (pos > size)
376  {
377    s += "ERROR";
378    return;
379  }
380  UInt32 sidSize = 0;
381  ParseSid(s, p + pos, size - pos, sidSize);
382}
383
384static void AddUInt32ToString(AString &s, UInt32 val)
385{
386  char sz[16];
387  ConvertUInt32ToString(val, sz);
388  s += sz;
389}
390
391static void ParseAcl(AString &s, const Byte *p, UInt32 size, const char *strName, UInt32 flags, UInt32 offset)
392{
393  UInt32 control = Get16(p + 2);
394  if ((flags & control) == 0)
395    return;
396  UInt32 pos = Get32(p + offset);
397  s += ' ';
398  s += strName;
399  if (pos >= size)
400    return;
401  p += pos;
402  size -= pos;
403  if (size < 8)
404    return;
405  if (Get16(p) != 2) // revision
406    return;
407  UInt32 num = Get32(p + 4);
408  AddUInt32ToString(s, num);
409
410  /*
411  UInt32 aclSize = Get16(p + 2);
412  if (num >= (1 << 16))
413    return;
414  if (aclSize > size)
415    return;
416  size = aclSize;
417  size -= 8;
418  p += 8;
419  for (UInt32 i = 0 ; i < num; i++)
420  {
421    if (size <= 8)
422      return;
423    // Byte type = p[0];
424    // Byte flags = p[1];
425    // UInt32 aceSize = Get16(p + 2);
426    // UInt32 mask = Get32(p + 4);
427    p += 8;
428    size -= 8;
429
430    UInt32 sidSize = 0;
431    s += ' ';
432    ParseSid(s, p, size, sidSize);
433    if (sidSize == 0)
434      return;
435    p += sidSize;
436    size -= sidSize;
437  }
438
439  // the tail can contain zeros. So (size != 0) is not ERROR
440  // if (size != 0) s += " ERROR";
441  */
442}
443
444#define MY_SE_OWNER_DEFAULTED       (0x0001)
445#define MY_SE_GROUP_DEFAULTED       (0x0002)
446#define MY_SE_DACL_PRESENT          (0x0004)
447#define MY_SE_DACL_DEFAULTED        (0x0008)
448#define MY_SE_SACL_PRESENT          (0x0010)
449#define MY_SE_SACL_DEFAULTED        (0x0020)
450#define MY_SE_DACL_AUTO_INHERIT_REQ (0x0100)
451#define MY_SE_SACL_AUTO_INHERIT_REQ (0x0200)
452#define MY_SE_DACL_AUTO_INHERITED   (0x0400)
453#define MY_SE_SACL_AUTO_INHERITED   (0x0800)
454#define MY_SE_DACL_PROTECTED        (0x1000)
455#define MY_SE_SACL_PROTECTED        (0x2000)
456#define MY_SE_RM_CONTROL_VALID      (0x4000)
457#define MY_SE_SELF_RELATIVE         (0x8000)
458
459void ConvertNtSecureToString(const Byte *data, UInt32 size, AString &s)
460{
461  s.Empty();
462  if (size < 20 || size > (1 << 18))
463  {
464    s += "ERROR";
465    return;
466  }
467  if (Get16(data) != 1) // revision
468  {
469    s += "UNSUPPORTED";
470    return;
471  }
472  ParseOwner(s, data, size, Get32(data + 4));
473  s += ' ';
474  ParseOwner(s, data, size, Get32(data + 8));
475  ParseAcl(s, data, size, "s:", MY_SE_SACL_PRESENT, 12);
476  ParseAcl(s, data, size, "d:", MY_SE_DACL_PRESENT, 16);
477  s += ' ';
478  AddUInt32ToString(s, size);
479  // s += '\n';
480  // s += Data_To_Hex(data, size);
481}
482
483#ifdef _WIN32
484
485static bool CheckSid(const Byte *data, UInt32 size, UInt32 pos) throw()
486{
487  if (pos >= size)
488    return false;
489  size -= pos;
490  if (size < 8)
491    return false;
492  UInt32 rev = data[pos];
493  if (rev != 1)
494    return false;
495  UInt32 num = data[pos + 1];
496  return (8 + num * 4 <= size);
497}
498
499static bool CheckAcl(const Byte *p, UInt32 size, UInt32 flags, UInt32 offset) throw()
500{
501  UInt32 control = Get16(p + 2);
502  if ((flags & control) == 0)
503    return true;
504  UInt32 pos = Get32(p + offset);
505  if (pos >= size)
506    return false;
507  p += pos;
508  size -= pos;
509  if (size < 8)
510    return false;
511  UInt32 aclSize = Get16(p + 2);
512  return (aclSize <= size);
513}
514
515bool CheckNtSecure(const Byte *data, UInt32 size) throw()
516{
517  if (size < 20)
518    return false;
519  if (Get16(data) != 1) // revision
520    return true; // windows function can handle such error, so we allow it
521  if (size > (1 << 18))
522    return false;
523  if (!CheckSid(data, size, Get32(data + 4))) return false;
524  if (!CheckSid(data, size, Get32(data + 8))) return false;
525  if (!CheckAcl(data, size, MY_SE_SACL_PRESENT, 12)) return false;
526  if (!CheckAcl(data, size, MY_SE_DACL_PRESENT, 16)) return false;
527  return true;
528}
529
530#endif
531
532bool ConvertNtReparseToString(const Byte *data, UInt32 size, UString &s)
533{
534  s.Empty();
535  NFile::CReparseAttr attr;
536  if (attr.Parse(data, size))
537  {
538    if (!attr.IsSymLink())
539      s.AddAscii("Junction: ");
540    s += attr.GetPath();
541    if (!attr.IsOkNamePair())
542    {
543      s.AddAscii(" : ");
544      s += attr.PrintName;
545    }
546    return true;
547  }
548
549  if (size < 8)
550    return false;
551  UInt32 tag = Get32(data);
552  UInt32 len = Get16(data + 4);
553  if (len + 8 > size)
554    return false;
555  if (Get16(data + 6) != 0) // padding
556    return false;
557
558  char hex[16];
559  ConvertUInt32ToHex8Digits(tag, hex);
560  s.AddAscii(hex);
561  s.Add_Space();
562
563  data += 8;
564
565  for (UInt32 i = 0; i < len; i++)
566  {
567    unsigned b = ((const Byte *)data)[i];
568    s += (wchar_t)GetHex((b >> 4) & 0xF);
569    s += (wchar_t)GetHex(b & 0xF);
570  }
571  return true;
572}
573
574#endif
575