Home | History | Annotate | Download | only in Common
      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 
     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 
     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 
    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 
    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 
    216 static inline unsigned GetHex(unsigned v)
    217 {
    218   return (v < 10) ? ('0' + v) : ('A' + (v - 10));
    219 }
    220 
    221 #ifndef _SFX
    222 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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