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