|
|
1.1 root 1: /*
2: Copyright (c) 2008 TrueCrypt Foundation. All rights reserved.
3:
4: Governed by the TrueCrypt License 2.4 the full text of which is contained
5: in the file License.txt included in TrueCrypt binary and source code
6: distribution packages.
7: */
8:
9: #include "Tcdefs.h"
10: #include "Platform/Finally.h"
11: #include "Platform/ForEach.h"
12: #include <Setupapi.h>
13: #include <devguid.h>
14: #include <io.h>
15: #include <shlobj.h>
16: #include <atlbase.h>
17: #include "BootEncryption.h"
18: #include "Boot/Windows/BootCommon.h"
19: #include "Common/Resource.h"
20: #include "Crc.h"
21: #include "Crypto.h"
22: #include "Dlgcode.h"
23: #include "Endian.h"
24: #include "Random.h"
25: #include "Registry.h"
26: #include "Volumes.h"
27:
28: #ifdef VOLFORMAT
29: #include "Format/FormatCom.h"
30: #elif defined (TCMOUNT)
31: #include "Mount/MainCom.h"
32: #endif
33:
34: namespace TrueCrypt
35: {
36: #if !defined (SETUP)
37:
38: class Elevator
39: {
40: public:
41: static void CallDriver (DWORD ioctl, void *input, DWORD inputSize, void *output, DWORD outputSize)
42: {
43: Elevate();
44:
45: CComBSTR inputBstr;
46: if (input && inputBstr.AppendBytes ((const char *) input, inputSize) != S_OK)
47: throw ParameterIncorrect (SRC_POS);
48:
49: CComBSTR outputBstr;
50: if (output && outputBstr.AppendBytes ((const char *) output, outputSize) != S_OK)
51: throw ParameterIncorrect (SRC_POS);
52:
53: DWORD result = ElevatedComInstance->CallDriver (ioctl, inputBstr, &outputBstr);
54: if (result != ERROR_SUCCESS)
55: {
56: SetLastError (result);
57: throw SystemException();
58: }
59: }
60:
61: static void ReadWriteFile (BOOL write, BOOL device, const string &filePath, byte *buffer, uint64 offset, uint32 size, DWORD *sizeDone)
62: {
63: Elevate();
64:
65: CComBSTR bufferBstr;
66: if (bufferBstr.AppendBytes ((const char *) buffer, size) != S_OK)
67: throw ParameterIncorrect (SRC_POS);
68: DWORD result = ElevatedComInstance->ReadWriteFile (write, device, CComBSTR (filePath.c_str()), &bufferBstr, offset, size, sizeDone);
69:
70: if (result != ERROR_SUCCESS)
71: {
72: SetLastError (result);
73: throw SystemException();
74: }
75:
76: if (!write)
77: memcpy (buffer, (BYTE *) bufferBstr.m_str, size);
78: }
79:
80: static void RegisterFilterDriver (bool registerDriver)
81: {
82: Elevate();
83:
84: DWORD result = ElevatedComInstance->RegisterFilterDriver (registerDriver ? TRUE : FALSE);
85: if (result != ERROR_SUCCESS)
86: {
87: SetLastError (result);
88: throw SystemException();
89: }
90: }
91:
92: static void Release ()
93: {
94: if (ElevatedComInstance)
95: {
96: ElevatedComInstance->Release();
97: ElevatedComInstance = nullptr;
98: CoUninitialize ();
99: }
100: }
101:
102: static void SetDriverServiceStartType (DWORD startType)
103: {
104: Elevate();
105:
106: DWORD result = ElevatedComInstance->SetDriverServiceStartType (startType);
107: if (result != ERROR_SUCCESS)
108: {
109: SetLastError (result);
110: throw SystemException();
111: }
112: }
113:
114: protected:
115: static void Elevate ()
116: {
117: if (IsAdmin())
118: {
119: SetLastError (ERROR_ACCESS_DENIED);
120: throw SystemException();
121: }
122:
123: if (!ElevatedComInstance)
124: {
125: CoInitialize (NULL);
126: ElevatedComInstance = GetElevatedInstance (GetActiveWindow() ? GetActiveWindow() : MainDlg);
127: }
128: }
129:
130: #if defined (TCMOUNT)
131: static ITrueCryptMainCom *ElevatedComInstance;
132: #elif defined (VOLFORMAT)
133: static ITrueCryptFormatCom *ElevatedComInstance;
134: #endif
135: };
136:
137: #if defined (TCMOUNT)
138: ITrueCryptMainCom *Elevator::ElevatedComInstance;
139: #elif defined (VOLFORMAT)
140: ITrueCryptFormatCom *Elevator::ElevatedComInstance;
141: #endif
142:
143: #else // SETUP
144:
145: class Elevator
146: {
147: public:
148: static void CallDriver (DWORD ioctl, void *input, DWORD inputSize, void *output, DWORD outputSize) { throw ParameterIncorrect (SRC_POS); }
149: static void ReadWriteFile (BOOL write, BOOL device, const string &filePath, byte *buffer, uint64 offset, uint32 size, DWORD *sizeDone) { throw ParameterIncorrect (SRC_POS); }
150: static void RegisterFilterDriver (bool registerDriver) { throw ParameterIncorrect (SRC_POS); }
151: static void Release () { }
152: static void SetDriverServiceStartType (DWORD startType) { throw ParameterIncorrect (SRC_POS); }
153: };
154:
155: #endif // SETUP
156:
157:
158: File::File (string path, bool readOnly, bool create) : Elevated (false), FileOpen (false)
159: {
160: Handle = CreateFile (path.c_str(),
161: readOnly ? FILE_READ_DATA : FILE_READ_DATA | FILE_WRITE_DATA,
162: FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, create ? CREATE_ALWAYS : OPEN_EXISTING,
163: FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_WRITE_THROUGH, NULL);
164:
165: try
166: {
167: throw_sys_if (Handle == INVALID_HANDLE_VALUE);
168: }
169: catch (SystemException &)
170: {
171: if (GetLastError() == ERROR_ACCESS_DENIED && IsUacSupported())
172: Elevated = true;
173: else
174: throw;
175: }
176:
177: FileOpen = true;
178: FilePointerPosition = 0;
179: IsDevice = false;
180: Path = path;
181: }
182:
183: void File::Close ()
184: {
185: if (FileOpen)
186: {
187: if (!Elevated)
188: CloseHandle (Handle);
189:
190: FileOpen = false;
191: }
192: }
193:
194: DWORD File::Read (byte *buffer, DWORD size)
195: {
196: DWORD bytesRead;
197:
198: if (Elevated)
199: {
200: DWORD bytesRead;
201:
202: Elevator::ReadWriteFile (false, IsDevice, Path, buffer, FilePointerPosition, size, &bytesRead);
203: FilePointerPosition += bytesRead;
204: return bytesRead;
205: }
206:
207: throw_sys_if (!ReadFile (Handle, buffer, size, &bytesRead, NULL));
208: return bytesRead;
209: }
210:
211: void File::SeekAt (int64 position)
212: {
213: if (Elevated)
214: {
215: FilePointerPosition = position;
216: }
217: else
218: {
219: LARGE_INTEGER pos;
220: pos.QuadPart = position;
221: throw_sys_if (!SetFilePointerEx (Handle, pos, NULL, FILE_BEGIN));
222: }
223: }
224:
225: void File::Write (byte *buffer, DWORD size)
226: {
227: DWORD bytesWritten;
228:
229: if (Elevated)
230: {
231: Elevator::ReadWriteFile (true, IsDevice, Path, buffer, FilePointerPosition, size, &bytesWritten);
232: FilePointerPosition += bytesWritten;
233: throw_sys_if (bytesWritten != size);
234: }
235: else
236: {
237: throw_sys_if (!WriteFile (Handle, buffer, size, &bytesWritten, NULL) || bytesWritten != size);
238: }
239: }
240:
241: void Show (HWND parent, const string &str)
242: {
243: MessageBox (parent, str.c_str(), NULL, 0);
244: }
245:
246:
247: Device::Device (string path, bool readOnly)
248: {
249: FileOpen = false;
250: Elevated = false;
251:
252: Handle = CreateFile ((string ("\\\\.\\") + path).c_str(),
253: readOnly ? FILE_READ_DATA : FILE_READ_DATA | FILE_WRITE_DATA,
254: FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
255: FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_WRITE_THROUGH, NULL);
256:
257: try
258: {
259: throw_sys_if (Handle == INVALID_HANDLE_VALUE);
260: }
261: catch (SystemException &)
262: {
263: if (GetLastError() == ERROR_ACCESS_DENIED && IsUacSupported())
264: Elevated = true;
265: else
266: throw;
267: }
268:
269: FileOpen = true;
270: FilePointerPosition = 0;
271: IsDevice = true;
272: Path = path;
273: }
274:
275:
276: BootEncryption::~BootEncryption ()
277: {
278: if (RescueIsoImage)
279: delete RescueIsoImage;
280:
281: Elevator::Release();
282: }
283:
284:
285: void BootEncryption::CallDriver (DWORD ioctl, void *input, DWORD inputSize, void *output, DWORD outputSize)
286: {
287: try
288: {
289: DWORD bytesReturned;
290: throw_sys_if (!DeviceIoControl (hDriver, ioctl, input, inputSize, output, outputSize, &bytesReturned, NULL));
291: }
292: catch (SystemException &)
293: {
294: if (GetLastError() == ERROR_ACCESS_DENIED && IsUacSupported())
295: Elevator::CallDriver (ioctl, input, inputSize, output, outputSize);
296: else
297: throw;
298: }
299: }
300:
301:
302: DWORD BootEncryption::GetDriverServiceStartType ()
303: {
304: DWORD startType;
305: throw_sys_if (!ReadLocalMachineRegistryDword ("SYSTEM\\CurrentControlSet\\Services\\truecrypt", "Start", &startType));
306: return startType;
307: }
308:
309:
310: void BootEncryption::SetDriverServiceStartType (DWORD startType)
311: {
312: if (!IsAdmin() && IsUacSupported())
313: {
314: Elevator::SetDriverServiceStartType (startType);
315: return;
316: }
317:
318: BOOL startOnBoot = (startType == SERVICE_BOOT_START);
319:
320: SC_HANDLE serviceManager = OpenSCManager (NULL, NULL, SC_MANAGER_ALL_ACCESS);
321: throw_sys_if (!serviceManager);
322:
323: finally_do_arg (SC_HANDLE, serviceManager, { CloseServiceHandle (finally_arg); });
324:
325: SC_HANDLE service = OpenService (serviceManager, "truecrypt", SERVICE_CHANGE_CONFIG);
326: throw_sys_if (!service);
327:
328: finally_do_arg (SC_HANDLE, service, { CloseServiceHandle (finally_arg); });
329:
330: throw_sys_if (!ChangeServiceConfig (service, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE,
331: startOnBoot ? SERVICE_ERROR_SEVERE : SERVICE_ERROR_NORMAL, NULL,
332: startOnBoot ? "Filter" : NULL,
333: NULL, NULL, NULL, NULL, NULL));
334:
335: // ChangeServiceConfig() rejects SERVICE_BOOT_START with ERROR_INVALID_PARAMETER
336: throw_sys_if (!WriteLocalMachineRegistryDword ("SYSTEM\\CurrentControlSet\\Services\\truecrypt", "Start", startType));
337: }
338:
339:
340: void BootEncryption::ProbeRealSystemDriveSize ()
341: {
342: if (RealSystemDriveSizeValid)
343: return;
344:
345: GetSystemDriveConfiguration();
346:
347: ProbeRealDriveSizeRequest request;
348: _snwprintf (request.DeviceName, array_capacity (request.DeviceName), L"%hs", DriveConfig.DrivePartition.DevicePath.c_str());
349:
350: CallDriver (TC_IOCTL_PROBE_REAL_DRIVE_SIZE, &request, sizeof (request), &request, sizeof (request));
351: DriveConfig.DrivePartition.Info.PartitionLength = request.RealDriveSize;
352:
353: RealSystemDriveSizeValid = TRUE;
354: }
355:
356:
357: PartitionList BootEncryption::GetDrivePartitions (int driveNumber)
358: {
359: PartitionList partList;
360:
361: for (int partNumber = 0; partNumber < 64; ++partNumber)
362: {
363: stringstream partPath;
364: partPath << "\\Device\\Harddisk" << driveNumber << "\\Partition" << partNumber;
365:
366: DISK_PARTITION_INFO_STRUCT diskPartInfo;
367: _snwprintf (diskPartInfo.deviceName, array_capacity (diskPartInfo.deviceName), L"%hs", partPath.str().c_str());
368:
369: try
370: {
371: CallDriver (TC_IOCTL_GET_DRIVE_PARTITION_INFO, &diskPartInfo, sizeof (diskPartInfo), &diskPartInfo, sizeof (diskPartInfo));
372: }
373: catch (...)
374: {
375: continue;
376: }
377:
378: Partition part;
379: part.DevicePath = partPath.str();
380: part.Number = partNumber;
381: part.Info = diskPartInfo.partInfo;
382: part.IsGPT = diskPartInfo.IsGPT;
383:
384: // Mount point
385: wstringstream ws;
386: ws << partPath.str().c_str();
387: int driveNumber = GetDiskDeviceDriveLetter ((wchar_t *) ws.str().c_str());
388:
389: if (driveNumber >= 0)
390: {
391: part.MountPoint += (char) (driveNumber + 'A');
392: part.MountPoint += ":";
393: }
394: partList.push_back (part);
395: }
396:
397: return partList;
398: }
399:
400:
401: DISK_GEOMETRY BootEncryption::GetDriveGeometry (int driveNumber)
402: {
403: stringstream devName;
404: devName << "\\Device\\Harddisk" << driveNumber << "\\Partition0";
405:
406: DISK_GEOMETRY geometry;
407: throw_sys_if (!::GetDriveGeometry ((char *) devName.str().c_str(), &geometry));
408: return geometry;
409: }
410:
411:
412: string BootEncryption::GetWindowsDirectory ()
413: {
414: char buf[MAX_PATH];
415: if (GetSystemDirectory (buf, sizeof (buf)) > 0)
416: return string (buf);
417:
418: return string();
419: }
420:
421:
422: uint16 BootEncryption::GetInstalledBootLoaderVersion ()
423: {
424: uint16 version;
425: CallDriver (TC_IOCTL_GET_BOOT_LOADER_VERSION, NULL, 0, &version, sizeof (version));
426: return version;
427: }
428:
429:
430: BootEncryptionStatus BootEncryption::GetStatus ()
431: {
432: /* IMPORTANT: Do NOT add any potentially time-consuming operations to this function. */
433:
434: BootEncryptionStatus status;
435: CallDriver (TC_IOCTL_GET_BOOT_ENCRYPTION_STATUS, NULL, 0, &status, sizeof (status));
436: return status;
437: }
438:
439:
440: void BootEncryption::GetVolumeProperties (VOLUME_PROPERTIES_STRUCT *properties)
441: {
442: if (properties == NULL)
443: throw ParameterIncorrect (SRC_POS);
444:
445: CallDriver (TC_IOCTL_GET_BOOT_DRIVE_VOLUME_PROPERTIES, NULL, 0, properties, sizeof (*properties));
446: }
447:
448:
1.1.1.2 ! root 449: bool BootEncryption::SystemDriveContainsPartitionType (byte type)
! 450: {
! 451: Device device (GetSystemDriveConfiguration().DevicePath, true);
! 452:
! 453: byte mbrBuf[SECTOR_SIZE];
! 454: device.SeekAt (0);
! 455: device.Read (mbrBuf, sizeof (mbrBuf));
! 456:
! 457: MBR *mbr = reinterpret_cast <MBR *> (mbrBuf);
! 458: if (mbr->Signature != 0xaa55)
! 459: throw ParameterIncorrect (SRC_POS);
! 460:
! 461: for (size_t i = 0; i < array_capacity (mbr->Partitions); ++i)
! 462: {
! 463: if (mbr->Partitions[i].Type == type)
! 464: return true;
! 465: }
! 466:
! 467: return false;
! 468: }
! 469:
! 470:
! 471: bool BootEncryption::SystemDriveContainsExtendedPartition ()
! 472: {
! 473: return SystemDriveContainsPartitionType (PARTITION_EXTENDED) || SystemDriveContainsPartitionType (PARTITION_XINT13_EXTENDED);
! 474: }
! 475:
! 476:
! 477: bool BootEncryption::SystemDriveIsDynamic ()
! 478: {
! 479: return SystemDriveContainsPartitionType (PARTITION_LDM);
! 480: }
! 481:
! 482:
1.1 root 483: SystemDriveConfiguration BootEncryption::GetSystemDriveConfiguration ()
484: {
485: if (DriveConfigValid)
486: return DriveConfig;
487:
488: SystemDriveConfiguration config;
489:
490: string winDir = GetWindowsDirectory();
491:
492: // Scan all drives
493: for (int driveNumber = 0; driveNumber < 32; ++driveNumber)
494: {
495: bool windowsFound = false;
496: config.SystemLoaderPresent = false;
497:
498: PartitionList partitions = GetDrivePartitions (driveNumber);
499: foreach (const Partition &part, partitions)
500: {
501: if (_access ((part.MountPoint + "\\bootmgr").c_str(), 0) == 0 || _access ((part.MountPoint + "\\ntldr").c_str(), 0) == 0)
502: config.SystemLoaderPresent = true;
503:
504: if (!windowsFound && !part.MountPoint.empty() && winDir.find (part.MountPoint) == 0)
505: {
506: config.SystemPartition = part;
507: windowsFound = true;
508: }
509: }
510:
511: if (windowsFound)
512: {
513: config.DriveNumber = driveNumber;
514:
515: stringstream ss;
516: ss << "PhysicalDrive" << driveNumber;
517: config.DevicePath = ss.str();
518:
519: config.DrivePartition = partitions.front();
520: partitions.pop_front();
521: config.Partitions = partitions;
522:
523: config.InitialUnallocatedSpace = 0x7fffFFFFffffFFFFull;
524: config.TotalUnallocatedSpace = config.DrivePartition.Info.PartitionLength.QuadPart;
525:
526: foreach (const Partition &part, config.Partitions)
527: {
528: if (part.Info.StartingOffset.QuadPart < config.InitialUnallocatedSpace)
529: config.InitialUnallocatedSpace = part.Info.StartingOffset.QuadPart;
530:
531: config.TotalUnallocatedSpace -= part.Info.PartitionLength.QuadPart;
532: }
533:
534: DriveConfig = config;
535: DriveConfigValid = TRUE;
536: return DriveConfig;
537: }
538: }
539:
540: throw ParameterIncorrect (SRC_POS);
541: }
542:
543:
544: bool BootEncryption::SystemPartitionCoversWholeDrive ()
545: {
546: SystemDriveConfiguration config = GetSystemDriveConfiguration();
547:
548: return config.Partitions.size() == 1
1.1.1.2 ! root 549: && config.DrivePartition.Info.PartitionLength.QuadPart - config.SystemPartition.Info.PartitionLength.QuadPart < 64 * 1024 * 1024;
1.1 root 550: }
551:
552:
553: void BootEncryption::InstallBootLoader ()
554: {
555: Device device (GetSystemDriveConfiguration().DevicePath);
556: DWORD size;
557:
558: // MBR
559: byte *bootSecImg = MapResource ("BIN", IDR_BOOT_SECTOR, &size);
560: if (!bootSecImg || size != SECTOR_SIZE)
561: throw ParameterIncorrect (SRC_POS);
562:
563: byte bootSecBuf[SECTOR_SIZE];
564:
565: device.SeekAt (0);
566: device.Read (bootSecBuf, sizeof (bootSecBuf));
567:
568: memcpy (bootSecBuf, bootSecImg, TC_MAX_MBR_BOOT_CODE_SIZE);
569:
570: device.SeekAt (0);
571: device.Write (bootSecBuf, size);
572:
573: byte bootSecVerificationBuf[SECTOR_SIZE];
574: device.SeekAt (0);
575: device.Read (bootSecVerificationBuf, size);
576:
577: if (memcmp (bootSecBuf, bootSecVerificationBuf, size) != 0)
578: throw ErrorException ("ERROR_MBR_PROTECTED");
579:
580: // Boot loader
581: byte bootLoaderBuf[TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET - SECTOR_SIZE];
582: byte *bootLoader = MapResource ("BIN", IDR_BOOT_LOADER, &size);
583:
584: if (!bootLoader || size > sizeof (bootLoaderBuf))
585: throw ParameterIncorrect (SRC_POS);
586:
587: ZeroMemory (bootLoaderBuf, sizeof (bootLoaderBuf));
588: memcpy (bootLoaderBuf, bootLoader, size);
589:
590: device.SeekAt (SECTOR_SIZE);
591: device.Write (bootLoaderBuf, sizeof (bootLoaderBuf));
592: }
593:
594:
595: string BootEncryption::GetSystemLoaderBackupPath ()
596: {
597: char pathBuf[MAX_PATH];
598:
599: throw_sys_if (!SUCCEEDED (SHGetFolderPath (NULL, CSIDL_COMMON_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, pathBuf)));
600:
601: string path = string (pathBuf) + "\\" TC_APP_NAME;
602: CreateDirectory (path.c_str(), NULL);
603:
604: return path + '\\' + TC_SYS_BOOT_LOADER_BACKUP_NAME;
605: }
606:
1.1.1.2 ! root 607: #ifndef SETUP
1.1 root 608: void BootEncryption::CreateRescueIsoImage (bool initialSetup, const string &isoImagePath)
609: {
610: BootEncryptionStatus encStatus = GetStatus();
611: if (encStatus.SetupInProgress)
612: throw ParameterIncorrect (SRC_POS);
613:
614: Buffer imageBuf (RescueIsoImageSize);
615:
616: byte *image = imageBuf.Ptr();
617: memset (image, 0, RescueIsoImageSize);
618:
619: // Primary volume descriptor
1.1.1.2 ! root 620: strcpy ((char *)image + 0x8000, "\001CD001\001");
! 621: strcpy ((char *)image + 0x7fff + 41, "TrueCrypt Rescue Disk ");
! 622: *(uint32 *) (image + 0x7fff + 81) = RescueIsoImageSize / 2048;
! 623: *(uint32 *) (image + 0x7fff + 85) = BE32 (RescueIsoImageSize / 2048);
! 624: image[0x7fff + 121] = 1;
! 625: image[0x7fff + 124] = 1;
! 626: image[0x7fff + 125] = 1;
! 627: image[0x7fff + 128] = 1;
! 628: image[0x7fff + 130] = 8;
! 629: image[0x7fff + 131] = 8;
! 630:
! 631: image[0x7fff + 133] = 10;
! 632: image[0x7fff + 140] = 10;
! 633: image[0x7fff + 141] = 0x14;
! 634: image[0x7fff + 157] = 0x22;
! 635: image[0x7fff + 159] = 0x18;
1.1 root 636:
637: // Boot record volume descriptor
638: strcpy ((char *)image + 0x8801, "CD001\001EL TORITO SPECIFICATION");
639: image[0x8800 + 0x47] = 0x19;
640:
1.1.1.2 ! root 641: // Volume descriptor set terminator
! 642: strcpy ((char *)image + 0x9000, "\377CD001\001");
! 643:
! 644: // Path table
! 645: image[0xA000 + 0] = 1;
! 646: image[0xA000 + 2] = 0x18;
! 647: image[0xA000 + 6] = 1;
! 648:
! 649: // Root directory
! 650: image[0xc000 + 0] = 0x22;
! 651: image[0xc000 + 2] = 0x18;
! 652: image[0xc000 + 9] = 0x18;
! 653: image[0xc000 + 11] = 0x08;
! 654: image[0xc000 + 16] = 0x08;
! 655: image[0xc000 + 25] = 0x02;
! 656: image[0xc000 + 28] = 0x01;
! 657: image[0xc000 + 31] = 0x01;
! 658: image[0xc000 + 32] = 0x01;
! 659: image[0xc000 + 34] = 0x22;
! 660: image[0xc000 + 36] = 0x18;
! 661: image[0xc000 + 43] = 0x18;
! 662: image[0xc000 + 45] = 0x08;
! 663: image[0xc000 + 50] = 0x08;
! 664: image[0xc000 + 59] = 0x02;
! 665: image[0xc000 + 62] = 0x01;
! 666: *(uint32 *) (image + 0xc000 + 65) = 0x010101;
! 667:
1.1 root 668: // Validation entry
669: image[0xc800] = 1;
1.1.1.2 ! root 670: int offset = 0xc800 + 0x1c;
1.1 root 671: image[offset++] = 0xaa;
672: image[offset++] = 0x55;
673: image[offset++] = 0x55;
674: image[offset] = 0xaa;
675:
676: // Initial entry
677: offset = 0xc820;
678: image[offset++] = 0x88;
679: image[offset++] = 2;
680: image[0xc820 + 6] = 1;
681: image[0xc820 + 8] = TC_CD_BOOT_LOADER_SECTOR;
682:
683: // TrueCrypt Boot Loader
684: DWORD size;
685: byte *bootSecResourceImg = MapResource ("BIN", IDR_BOOT_SECTOR, &size);
686: if (!bootSecResourceImg || size != SECTOR_SIZE)
687: throw ParameterIncorrect (SRC_POS);
688:
689: byte bootSecImg[SECTOR_SIZE];
690: memcpy (bootSecImg, bootSecResourceImg, sizeof (bootSecImg));
691:
692: bootSecImg[TC_BOOT_SECTOR_CONFIG_OFFSET] |= TC_BOOT_CFG_FLAG_RESCUE_DISK;
693: memcpy (image + TC_CD_BOOTSECTOR_OFFSET, bootSecImg, SECTOR_SIZE);
694:
695: byte *bootLoader = MapResource ("BIN", IDR_BOOT_LOADER, &size);
696: if (!bootLoader)
697: throw ParameterIncorrect (SRC_POS);
698:
699: memcpy (image + TC_CD_BOOTSECTOR_OFFSET + SECTOR_SIZE, bootLoader, size);
700:
701: // Volume header
702: if (initialSetup)
703: {
704: if (!RescueVolumeHeaderValid)
705: throw ParameterIncorrect (SRC_POS);
706:
707: memcpy (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, RescueVolumeHeader, HEADER_SIZE);
708: }
709: else
710: {
711: Device bootDevice (GetSystemDriveConfiguration().DevicePath, true);
712: bootDevice.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET);
713: bootDevice.Read (image + TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET, HEADER_SIZE);
714: }
715:
716: // Original system loader
717: try
718: {
719: File sysBakFile (GetSystemLoaderBackupPath(), true);
720: sysBakFile.Read (image + TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET, TC_BOOT_LOADER_AREA_SIZE);
721:
722: image[TC_CD_BOOTSECTOR_OFFSET + TC_BOOT_SECTOR_CONFIG_OFFSET] |= TC_BOOT_CFG_FLAG_RESCUE_DISK_ORIG_SYS_LOADER;
723: }
724: catch (Exception &e)
725: {
726: e.Show (ParentWindow);
727: Warning ("SYS_LOADER_UNAVAILABLE_FOR_RESCUE_DISK");
728: }
729:
730: RescueIsoImage = new byte[RescueIsoImageSize];
731: if (!RescueIsoImage)
732: throw bad_alloc();
733: memcpy (RescueIsoImage, image, RescueIsoImageSize);
734:
735: if (!isoImagePath.empty())
736: {
737: File isoFile (isoImagePath, false, true);
738: isoFile.Write (image, RescueIsoImageSize);
739: }
740: }
1.1.1.2 ! root 741: #endif
1.1 root 742:
743: bool BootEncryption::VerifyRescueDisk ()
744: {
745: if (!RescueIsoImage)
746: throw ParameterIncorrect (SRC_POS);
747:
748: for (char drive = 'Z'; drive >= 'D'; --drive)
749: {
750: try
751: {
752: string path = "X:";
753: path[0] = drive;
754:
755: Device driveDevice (path, true);
756: size_t verifiedSectorCount = (TC_CD_BOOTSECTOR_OFFSET + TC_ORIG_BOOT_LOADER_BACKUP_SECTOR_OFFSET + TC_BOOT_LOADER_AREA_SIZE) / 2048;
757: Buffer buffer ((verifiedSectorCount + 1) * 2048);
758:
759: DWORD bytesRead = driveDevice.Read (buffer.Ptr(), buffer.Size());
760: if (bytesRead != buffer.Size())
761: continue;
762:
763: if (memcmp (buffer.Ptr(), RescueIsoImage, buffer.Size()) == 0)
764: return true;
765: }
766: catch (...) { }
767: }
768:
769: return false;
770: }
771:
772:
773: #ifndef SETUP
774:
775: void BootEncryption::CreateVolumeHeader (uint64 volumeSize, uint64 encryptedAreaStart, Password *password, int ea, int mode, int pkcs5)
776: {
777: PCRYPTO_INFO cryptoInfo = NULL;
778:
779: throw_sys_if (Randinit () != 0);
780: throw_sys_if (VolumeWriteHeader (TRUE, (char *) VolumeHeader, ea, mode, password, pkcs5, NULL, 0, &cryptoInfo,
781: volumeSize, 0, encryptedAreaStart, 0, FALSE) != 0);
782:
783: finally_do_arg (PCRYPTO_INFO*, &cryptoInfo, { crypto_close (*finally_arg); });
784:
785: // Initial rescue disk assumes encryption of the drive has been completed (EncryptedAreaLength == volumeSize)
786: memcpy (RescueVolumeHeader, VolumeHeader, sizeof (RescueVolumeHeader));
787: VolumeReadHeader (TRUE, (char *) RescueVolumeHeader, password, NULL, cryptoInfo);
788:
789: DecryptBuffer (RescueVolumeHeader + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo);
790:
791: if (GetHeaderField32 (RescueVolumeHeader, TC_HEADER_OFFSET_MAGIC) != 0x54525545)
792: throw ParameterIncorrect (SRC_POS);
793:
794: byte *fieldPos = RescueVolumeHeader + TC_HEADER_OFFSET_ENCRYPTED_AREA_LENGTH;
795: mputInt64 (fieldPos, volumeSize);
796:
797: EncryptBuffer (RescueVolumeHeader + HEADER_ENCRYPTED_DATA_OFFSET, HEADER_ENCRYPTED_DATA_SIZE, cryptoInfo);
798:
799: VolumeHeaderValid = true;
800: RescueVolumeHeaderValid = true;
801: }
802:
803:
804: void BootEncryption::InstallVolumeHeader ()
805: {
806: if (!VolumeHeaderValid)
807: throw ParameterIncorrect (SRC_POS);
808:
809: Device device (GetSystemDriveConfiguration().DevicePath);
810:
811: device.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET);
812: device.Write ((byte *) VolumeHeader, sizeof (VolumeHeader));
813: }
814:
815:
816: // For synchronous operations use AbortSetupWait()
817: void BootEncryption::AbortSetup ()
818: {
819: CallDriver (TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP);
820: }
821:
822:
823: // For asynchronous operations use AbortSetup()
824: void BootEncryption::AbortSetupWait ()
825: {
826: CallDriver (TC_IOCTL_ABORT_BOOT_ENCRYPTION_SETUP);
827:
828: BootEncryptionStatus encStatus = GetStatus();
829:
830: while (encStatus.SetupInProgress)
831: {
832: Sleep (TC_ABORT_TRANSFORM_WAIT_INTERVAL);
833: encStatus = GetStatus();
834: }
835: }
836:
837:
838: void BootEncryption::BackupSystemLoader ()
839: {
840: Device device (GetSystemDriveConfiguration().DevicePath, true);
841:
842: byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SECTOR_COUNT * SECTOR_SIZE];
843:
844: device.SeekAt (0);
845: device.Read (bootLoaderBuf, sizeof (bootLoaderBuf));
846:
847: // Prevent TrueCrypt loader from being backed up
848: for (size_t i = 0; i < sizeof (bootLoaderBuf) - strlen (TC_APP_NAME); ++i)
849: {
850: if (memcmp (bootLoaderBuf + i, TC_APP_NAME, strlen (TC_APP_NAME)) == 0)
851: {
852: if (AskWarnNoYes ("TC_BOOT_LOADER_ALREADY_INSTALLED") == IDNO)
853: throw UserAbort (SRC_POS);
854: return;
855: }
856: }
857:
858: File backupFile (GetSystemLoaderBackupPath(), false, true);
859: backupFile.Write (bootLoaderBuf, sizeof (bootLoaderBuf));
860: }
861:
862:
863: void BootEncryption::RestoreSystemLoader ()
864: {
865: byte bootLoaderBuf[TC_BOOT_LOADER_AREA_SECTOR_COUNT * SECTOR_SIZE];
866:
867: File backupFile (GetSystemLoaderBackupPath(), true);
868:
869: if (backupFile.Read (bootLoaderBuf, sizeof (bootLoaderBuf)) != sizeof (bootLoaderBuf))
870: throw ParameterIncorrect (SRC_POS);
871:
872: Device device (GetSystemDriveConfiguration().DevicePath);
873: device.SeekAt (0);
874: device.Write (bootLoaderBuf, sizeof (bootLoaderBuf));
875: }
876:
877:
878: void BootEncryption::RegisterFilterDriver (bool registerDriver)
879: {
880: if (!IsAdmin() && IsUacSupported())
881: {
882: Elevator::RegisterFilterDriver (registerDriver);
883: return;
884: }
885:
886: HKEY classRegKey = SetupDiOpenClassRegKey (&GUID_DEVCLASS_DISKDRIVE, KEY_READ | KEY_WRITE);
887: throw_sys_if (classRegKey == INVALID_HANDLE_VALUE);
888: finally_do_arg (HKEY, classRegKey, { RegCloseKey (finally_arg); });
889:
1.1.1.2 ! root 890: if (registerDriver && nCurrentOS == WIN_VISTA_OR_LATER)
1.1 root 891: {
1.1.1.2 ! root 892: // Attaching to the device stack after PartMgr on Vista causes all write IRPs sent to the lower device object to fail
! 893: // with STATUS_ACCESS_DENIED. Therefore, our filter is placed in front of others already registered.
1.1 root 894: size_t strSize = strlen ("truecrypt") + 1;
895: byte regKeyBuf[65536];
896: DWORD size = sizeof (regKeyBuf) - strSize;
897:
898: // SetupInstallFromInfSection() does not support prepending of values so we have to modify the registry directly
899: strncpy ((char *) regKeyBuf, "truecrypt", sizeof (regKeyBuf));
900:
901: if (RegQueryValueEx (classRegKey, "UpperFilters", NULL, NULL, regKeyBuf + strSize, &size) != ERROR_SUCCESS)
902: size = 1;
903:
904: throw_sys_if (RegSetValueEx (classRegKey, "UpperFilters", 0, REG_MULTI_SZ, regKeyBuf, strSize + size) != ERROR_SUCCESS);
905: }
906: else
907: {
908: char tempPath[MAX_PATH];
909: GetTempPath (sizeof (tempPath), tempPath);
910: string infFileName = string (tempPath) + "\\truecrypt_filter.inf";
911:
912: finally_do_arg (string, infFileName, { DeleteFile (finally_arg.c_str()); });
913: File infFile (infFileName, false, true);
914:
1.1.1.2 ! root 915: string infTxt = "[truecrypt]\r\n" + string (registerDriver ? "Add" : "Del") + "Reg=truecrypt_reg\r\n\r\n"
! 916: "[truecrypt_reg]\r\nHKR,,\"UpperFilters\",0x0001" + string (registerDriver ? "0008" : "8002") + ",\"truecrypt\"\r\n";
1.1 root 917:
918: infFile.Write ((byte *) infTxt.c_str(), infTxt.size());
919: infFile.Close();
920:
921: HINF hInf = SetupOpenInfFile (infFileName.c_str(), NULL, INF_STYLE_OLDNT | INF_STYLE_WIN4, NULL);
922: throw_sys_if (hInf == INVALID_HANDLE_VALUE);
923: finally_do_arg (HINF, hInf, { SetupCloseInfFile (finally_arg); });
924:
925: throw_sys_if (!SetupInstallFromInfSection (ParentWindow, hInf, "truecrypt", SPINST_REGISTRY, classRegKey, NULL, 0, NULL, NULL, NULL, NULL));
926: }
927: }
928:
929:
930: void BootEncryption::CheckRequirements ()
931: {
932: if (nCurrentOS == WIN_2000)
933: throw ErrorException ("SYS_ENCRYPTION_UNSUPPORTED_ON_CURRENT_OS");
934:
935: if (IsNonInstallMode())
936: throw ErrorException ("FEATURE_REQUIRES_INSTALLATION");
937:
938: SystemDriveConfiguration config = GetSystemDriveConfiguration ();
939:
940: if (config.SystemPartition.IsGPT)
941: throw ErrorException ("GPT_BOOT_DRIVE_UNSUPPORTED");
942:
943: if (config.InitialUnallocatedSpace < TC_BOOT_LOADER_AREA_SIZE)
944: throw ErrorException ("NO_SPACE_FOR_BOOT_LOADER");
945:
946: DISK_GEOMETRY geometry = GetDriveGeometry (config.DriveNumber);
947:
948: if (geometry.BytesPerSector != SECTOR_SIZE)
949: throw ErrorException ("LARGE_SECTOR_UNSUPPORTED");
950:
951: if (!config.SystemLoaderPresent)
952: throw ErrorException ("WINDOWS_NOT_ON_BOOT_DRIVE_ERROR");
953: }
954:
955:
956: void BootEncryption::Deinstall ()
957: {
958: BootEncryptionStatus encStatus = GetStatus();
959:
960: if (encStatus.DriveEncrypted || encStatus.DriveMounted)
961: throw ParameterIncorrect (SRC_POS);
962:
963: SystemDriveConfiguration config = GetSystemDriveConfiguration ();
964:
965: if (encStatus.VolumeHeaderPresent)
966: {
967: // Verify CRC of header salt
968: Device device (config.DevicePath, true);
969: byte header[SECTOR_SIZE];
970:
971: device.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET);
972: device.Read (header, sizeof (header));
973:
974: if (encStatus.VolumeHeaderSaltCrc32 != GetCrc32 ((byte *) header, PKCS5_SALT_SIZE))
975: throw ParameterIncorrect (SRC_POS);
976: }
977:
978: RegisterFilterDriver (false);
979: SetDriverServiceStartType (SERVICE_SYSTEM_START);
980:
981: try
982: {
983: RestoreSystemLoader ();
984: }
985: catch (Exception &e)
986: {
987: e.Show (ParentWindow);
988: throw ErrorException ("SYS_LOADER_RESTORE_FAILED");
989: }
990: }
991:
992:
993: int BootEncryption::ChangePassword (Password *oldPassword, Password *newPassword, int pkcs5)
994: {
995: if (GetStatus().SetupInProgress)
996: throw ParameterIncorrect (SRC_POS);
997:
998: SystemDriveConfiguration config = GetSystemDriveConfiguration ();
999:
1000: char header[HEADER_SIZE];
1001: Device device (config.DevicePath);
1002:
1003: // Only one algorithm is currently supported
1004: if (pkcs5 != 0)
1005: throw ParameterIncorrect (SRC_POS);
1006:
1007: device.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET);
1008: device.Read ((byte *) header, sizeof (header));
1009:
1010: PCRYPTO_INFO cryptoInfo = NULL;
1011:
1012: int status = VolumeReadHeader (TRUE, header, oldPassword, &cryptoInfo, NULL);
1013: finally_do_arg (PCRYPTO_INFO, cryptoInfo, { if (finally_arg) crypto_close (finally_arg); });
1014:
1015: if (status != 0)
1016: {
1017: handleError (ParentWindow, status);
1018: return status;
1019: }
1020:
1021: // Change the PKCS-5 PRF if requested by user
1022: if (pkcs5 != 0)
1023: cryptoInfo->pkcs5 = pkcs5;
1024:
1025: throw_sys_if (Randinit () != 0);
1026:
1027: /* The header will be re-encrypted PRAND_DISK_WIPE_PASSES times to prevent adversaries from using
1028: techniques such as magnetic force microscopy or magnetic force scanning tunnelling microscopy
1029: to recover the overwritten header. According to Peter Gutmann, data should be overwritten 22
1030: times (ideally, 35 times) using non-random patterns and pseudorandom data. However, as users might
1031: impatiently interupt the process (etc.) we will not use the Gutmann's patterns but will write the
1032: valid re-encrypted header, i.e. pseudorandom data, and there will be many more passes than Guttman
1033: recommends. During each pass we will write a valid working header. Each pass will use the same master
1034: key, and also the same header key, secondary key (XTS), etc., derived from the new password. The only
1035: item that will be different for each pass will be the salt. This is sufficient to cause each "version"
1036: of the header to differ substantially and in a random manner from the versions written during the
1037: other passes. */
1038:
1039: bool headerUpdated = false;
1040: int result = ERR_SUCCESS;
1041:
1042: try
1043: {
1044: for (int wipePass = 0; wipePass < PRAND_DISK_WIPE_PASSES; wipePass++)
1045: {
1046: PCRYPTO_INFO tmpCryptoInfo = NULL;
1047:
1048: status = VolumeWriteHeader (TRUE,
1049: header,
1050: cryptoInfo->ea,
1051: cryptoInfo->mode,
1052: newPassword,
1053: cryptoInfo->pkcs5,
1054: (char *) cryptoInfo->master_keydata,
1055: cryptoInfo->volume_creation_time,
1056: &tmpCryptoInfo,
1057: cryptoInfo->VolumeSize.Value,
1058: cryptoInfo->hiddenVolumeSize,
1059: cryptoInfo->EncryptedAreaStart.Value,
1060: cryptoInfo->EncryptedAreaLength.Value,
1061: wipePass < PRAND_DISK_WIPE_PASSES - 1);
1062:
1063: if (tmpCryptoInfo)
1064: crypto_close (tmpCryptoInfo);
1065:
1066: if (status != 0)
1067: {
1068: handleError (ParentWindow, status);
1069: return status;
1070: }
1071:
1072: device.SeekAt (TC_BOOT_VOLUME_HEADER_SECTOR_OFFSET);
1073: device.Write ((byte *) header, sizeof (header));
1074: headerUpdated = true;
1075: }
1076: }
1077: catch (Exception &e)
1078: {
1079: e.Show (ParentWindow);
1080: result = ERR_OS_ERROR;
1081: }
1082:
1083: if (headerUpdated)
1084: {
1085: ReopenBootVolumeHeaderRequest reopenRequest;
1086: reopenRequest.VolumePassword = *newPassword;
1087: finally_do_arg (ReopenBootVolumeHeaderRequest*, &reopenRequest, { burn (finally_arg, sizeof (*finally_arg)); });
1088:
1089: CallDriver (TC_IOCTL_REOPEN_BOOT_VOLUME_HEADER, &reopenRequest, sizeof (reopenRequest));
1090: }
1091:
1092: return result;
1093: }
1094:
1095:
1096: void BootEncryption::CheckEncryptionSetupResult ()
1097: {
1098: CallDriver (TC_IOCTL_GET_BOOT_ENCRYPTION_SETUP_RESULT);
1099: }
1100:
1101:
1102: void BootEncryption::Install ()
1103: {
1104: BootEncryptionStatus encStatus = GetStatus();
1105: if (encStatus.DriveMounted)
1106: throw ParameterIncorrect (SRC_POS);
1107:
1108: try
1109: {
1110: InstallBootLoader ();
1111: InstallVolumeHeader ();
1112:
1113: SetDriverServiceStartType (SERVICE_BOOT_START);
1114:
1115: try
1116: {
1117: RegisterFilterDriver (false);
1118: }
1119: catch (...) { }
1120:
1121: RegisterFilterDriver (true);
1122: }
1123: catch (Exception &)
1124: {
1125: try
1126: {
1127: RestoreSystemLoader ();
1128: }
1129: catch (Exception &e)
1130: {
1131: e.Show (ParentWindow);
1132: }
1133:
1134: throw;
1135: }
1136: }
1137:
1138:
1139: void BootEncryption::PrepareInstallation (bool systemPartitionOnly, Password &password, int ea, int mode, int pkcs5, const string &rescueIsoImagePath)
1140: {
1141: if (!systemPartitionOnly && !RealSystemDriveSizeValid)
1142: ProbeRealSystemDriveSize();
1143:
1144: BootEncryptionStatus encStatus = GetStatus();
1145: if (encStatus.DriveMounted)
1146: throw ParameterIncorrect (SRC_POS);
1147:
1148: CheckRequirements ();
1149:
1150: SystemDriveConfiguration config = GetSystemDriveConfiguration();
1151: BackupSystemLoader ();
1152:
1153: uint64 volumeSize;
1154: uint64 encryptedAreaStart;
1155:
1156: if (systemPartitionOnly)
1157: {
1158: volumeSize = config.SystemPartition.Info.PartitionLength.QuadPart;
1159: encryptedAreaStart = config.SystemPartition.Info.StartingOffset.QuadPart;
1160: }
1161: else
1162: {
1163: volumeSize = config.DrivePartition.Info.PartitionLength.QuadPart - TC_BOOT_LOADER_AREA_SIZE;
1164: encryptedAreaStart = config.DrivePartition.Info.StartingOffset.QuadPart + TC_BOOT_LOADER_AREA_SIZE;
1165: }
1166:
1167: CreateVolumeHeader (volumeSize, encryptedAreaStart, &password, ea, mode, pkcs5);
1168:
1169: if (!rescueIsoImagePath.empty())
1170: CreateRescueIsoImage (true, rescueIsoImagePath);
1171: }
1172:
1173:
1174: void BootEncryption::StartDecryption ()
1175: {
1176: BootEncryptionStatus encStatus = GetStatus();
1177:
1178: if (!encStatus.DeviceFilterActive || !encStatus.DriveMounted || encStatus.SetupInProgress)
1179: throw ParameterIncorrect (SRC_POS);
1180:
1181: BootEncryptionSetupRequest request;
1182: ZeroMemory (&request, sizeof (request));
1183:
1184: request.SetupMode = SetupDecryption;
1185:
1186: CallDriver (TC_IOCTL_BOOT_ENCRYPTION_SETUP, &request, sizeof (request), NULL, 0);
1187: }
1188:
1189:
1190: void BootEncryption::StartEncryption (WipeAlgorithmId wipeAlgorithm)
1191: {
1192: BootEncryptionStatus encStatus = GetStatus();
1193:
1194: if (!encStatus.DeviceFilterActive || !encStatus.DriveMounted || encStatus.SetupInProgress)
1195: throw ParameterIncorrect (SRC_POS);
1196:
1197: BootEncryptionSetupRequest request;
1198: ZeroMemory (&request, sizeof (request));
1199:
1200: request.SetupMode = SetupEncryption;
1201: request.WipeAlgorithm = wipeAlgorithm;
1202:
1203: CallDriver (TC_IOCTL_BOOT_ENCRYPTION_SETUP, &request, sizeof (request), NULL, 0);
1204: }
1205:
1.1.1.2 ! root 1206: #endif // !SETUP
1.1 root 1207:
1208: bool BootEncryption::RestartComputer (void)
1209: {
1210: TOKEN_PRIVILEGES tokenPrivil;
1211: HANDLE hTkn;
1212:
1213: if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES, &hTkn))
1214: {
1215: return false;
1216: }
1217:
1218: LookupPrivilegeValue (NULL, SE_SHUTDOWN_NAME, &tokenPrivil.Privileges[0].Luid);
1219: tokenPrivil.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1220: tokenPrivil.PrivilegeCount = 1;
1221:
1222: AdjustTokenPrivileges (hTkn, false, &tokenPrivil, 0, (PTOKEN_PRIVILEGES) NULL, 0);
1223: if (GetLastError() != ERROR_SUCCESS)
1224: return false;
1225:
1226: if (!ExitWindowsEx (EWX_REBOOT | EWX_FORCE,
1227: SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED))
1228: return false;
1229:
1230: return true;
1231: }
1232: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.