You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
622 lines
14 KiB
622 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
blsecret.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code to read and write secrets from disk.
|
|
|
|
Author:
|
|
|
|
Adam Barr (adamba) 13-June-1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "bootlib.h"
|
|
|
|
#define SEC_FAR
|
|
typedef BOOLEAN BOOL;
|
|
typedef unsigned char BYTE, *PBYTE;
|
|
typedef unsigned long DWORD;
|
|
#define LM20_PWLEN 14
|
|
#include "crypt.h"
|
|
#include "rc4.h"
|
|
|
|
//
|
|
// Defined in the bootssp library.
|
|
//
|
|
|
|
BOOL
|
|
CalculateLmOwfPassword(
|
|
IN PLM_PASSWORD LmPassword,
|
|
OUT PLM_OWF_PASSWORD LmOwfPassword
|
|
);
|
|
|
|
BOOL
|
|
CalculateNtOwfPassword(
|
|
IN PNT_PASSWORD NtPassword,
|
|
OUT PNT_OWF_PASSWORD NtOwfPassword
|
|
);
|
|
|
|
// This must be evenly divisible by sizeof(USHORT)
|
|
#define ASSUMED_SECTOR_SIZE 512
|
|
|
|
#if 0
|
|
VOID
|
|
BlpDumpSector(
|
|
PUCHAR Sector
|
|
)
|
|
{
|
|
int i, j;
|
|
|
|
PUCHAR SectorChar = (PUCHAR)Sector;
|
|
|
|
for (i = 0; i < ASSUMED_SECTOR_SIZE; i+= 16) {
|
|
|
|
for (j = 0; j < 16; j++) {
|
|
DbgPrint("%2.2x ", SectorChar[i + j]);
|
|
}
|
|
DbgPrint(" ");
|
|
for (j = 0; j < 16; j++) {
|
|
if ((SectorChar[i+j] >= ' ') && (SectorChar[i+j] < '~')) {
|
|
DbgPrint("%c", SectorChar[i+j]);
|
|
} else {
|
|
DbgPrint(".");
|
|
}
|
|
}
|
|
DbgPrint("\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
ARC_STATUS
|
|
BlOpenRawDisk(
|
|
PULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine opens the raw disk for read/write.
|
|
|
|
Arguments:
|
|
|
|
FileId - returns the FileId is successful, for use in subsequent calls.
|
|
|
|
Return Value:
|
|
|
|
The status return from the ArcOpen.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ARC_STATUS ArcStatus;
|
|
|
|
//
|
|
// Open the disk in raw mode. Need to check if it is the right string for Alpha.
|
|
// On x86 this eventually turns into an int13 read of disk 0x80 which
|
|
// is what we want and is more-or-less guaranteed to be in the format
|
|
// we expect (e.g. 512 byte sectors).
|
|
//
|
|
|
|
ArcStatus = ArcOpen("multi(0)disk(0)rdisk(0)partition(0)", ArcOpenReadWrite, FileId);
|
|
|
|
if (ArcStatus != ESUCCESS) {
|
|
|
|
DbgPrint("BlOpenRawDisk: ArcStatus on ArcOpen: %x\n", ArcStatus);
|
|
|
|
}
|
|
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BlCloseRawDisk(
|
|
ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes the raw disk.
|
|
|
|
Arguments:
|
|
|
|
FileId - The FileId returned by BlOpenRawDisk.
|
|
|
|
Return Value:
|
|
|
|
The status return from the ArcClose.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return ArcClose(FileId);
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BlCheckForFreeSectors (
|
|
ULONG FileId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine makes sure that the MBR looks correct and that there
|
|
is nothing installed (OnTrack or EZ-Drive -- need to detect
|
|
NT fault-tolerance also) that would prevent us from using the third
|
|
sector for storing the password secret.
|
|
|
|
Arguments:
|
|
|
|
FileId - The FileId returned by BlOpenRawDisk.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS if the disk is OK, or an error.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ARC_STATUS ArcStatus;
|
|
USHORT Sector[ASSUMED_SECTOR_SIZE/sizeof(USHORT)];
|
|
ULONG BytesProcessed;
|
|
PPARTITION_DESCRIPTOR FirstPartition;
|
|
LARGE_INTEGER SeekPosition;
|
|
|
|
|
|
//
|
|
// Make sure we are at the beginning of the disk.
|
|
//
|
|
|
|
SeekPosition.QuadPart = 0;
|
|
|
|
ArcStatus = ArcSeek(FileId, &SeekPosition, SeekAbsolute);
|
|
|
|
if (ArcStatus != ESUCCESS) {
|
|
|
|
DbgPrint("BlCheckForFreeSectors: ArcStatus on ArcSeek: %x\n", ArcStatus);
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
//
|
|
// Read the MBR at the start of the disk.
|
|
//
|
|
|
|
ArcStatus = ArcRead(FileId, Sector, ASSUMED_SECTOR_SIZE, &BytesProcessed);
|
|
|
|
if (ArcStatus != ESUCCESS) {
|
|
|
|
DbgPrint("BlCheckForFreeSectors: ArcStatus on ArcRead MBR: %x\n", ArcStatus);
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
#if 0
|
|
BlpDumpSector((PUCHAR)Sector);
|
|
#endif
|
|
|
|
//
|
|
// Make sure the signature is OK, and that the type of partition
|
|
// 0 is not 0x54 (OnTrack) or 0x55 (EZ-Drive).
|
|
//
|
|
|
|
if (Sector[BOOT_SIGNATURE_OFFSET] != BOOT_RECORD_SIGNATURE) {
|
|
|
|
DbgPrint("BlCheckForFreeSectors: Boot record signature %x not found (%x found)\n",
|
|
BOOT_RECORD_SIGNATURE,
|
|
Sector[BOOT_SIGNATURE_OFFSET] );
|
|
return EINVAL;
|
|
}
|
|
|
|
//
|
|
// FirstPartition is the first entry in the partition table.
|
|
//
|
|
|
|
FirstPartition = (PPARTITION_DESCRIPTOR)&Sector[PARTITION_TABLE_OFFSET];
|
|
|
|
if ((FirstPartition->PartitionType == 0x54) ||
|
|
(FirstPartition->PartitionType == 0x55)) {
|
|
|
|
DbgPrint("BlCheckForFreeSectors: First partition has type %x, exiting\n", FirstPartition->PartitionType);
|
|
return EINVAL;
|
|
}
|
|
|
|
DbgPrint("BlCheckForFreeSectors: Partition type is %d\n", FirstPartition->PartitionType);
|
|
|
|
#if 0
|
|
//
|
|
// Make the active entry the first one in the partition table.
|
|
//
|
|
|
|
if ((FirstPartition->ActiveFlag & PARTITION_ACTIVE_FLAG) != PARTITION_ACTIVE_FLAG) {
|
|
|
|
PPARTITION_DESCRIPTOR ActivePartition;
|
|
PARTITION_DESCRIPTOR TempPartition;
|
|
ULONG i;
|
|
|
|
ActivePartition = FirstPartition;
|
|
|
|
for (i = 1; i < NUM_PARTITION_TABLE_ENTRIES; i++) {
|
|
|
|
++ActivePartition;
|
|
if ((ActivePartition->ActiveFlag & PARTITION_ACTIVE_FLAG) == PARTITION_ACTIVE_FLAG) {
|
|
|
|
DbgPrint("BlCheckForFreeSector: Moving active partition %d to the front\n", i);
|
|
|
|
TempPartition = *FirstPartition;
|
|
*FirstPartition = *ActivePartition;
|
|
*ActivePartition = TempPartition;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == NUM_PARTITION_TABLE_ENTRIES) {
|
|
|
|
DbgPrint("BlCheckForFreeSector: Could not find an active partition!!\n");
|
|
|
|
} else {
|
|
|
|
ArcStatus = ArcSeek(FileId, &SeekPosition, SeekAbsolute);
|
|
|
|
if (ArcStatus != ESUCCESS) {
|
|
|
|
DbgPrint("BlCheckForFreeSectors: ArcStatus on ArcSeek: %x\n", ArcStatus);
|
|
return ArcStatus;
|
|
}
|
|
|
|
ArcStatus = ArcWrite(FileId, Sector, ASSUMED_SECTOR_SIZE, &BytesProcessed);
|
|
|
|
if ((ArcStatus != ESUCCESS) ||
|
|
(BytesProcessed != ASSUMED_SECTOR_SIZE)) {
|
|
|
|
DbgPrint("BlCheckForFreeSectors: ArcStatus on ArcWrite MBR: %x (%x)\n", ArcStatus, BytesProcessed);
|
|
return ArcStatus;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return ESUCCESS;
|
|
|
|
}
|
|
|
|
|
|
ARC_STATUS
|
|
BlReadSecret(
|
|
ULONG FileId,
|
|
PRI_SECRET Secret
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads the secret from the disk, if present.
|
|
|
|
Arguments:
|
|
|
|
FileId - The FileId returned by BlOpenRawDisk.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS if the secret is OK, an error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ARC_STATUS ArcStatus;
|
|
ULONG BytesRead;
|
|
LARGE_INTEGER SeekPosition;
|
|
|
|
|
|
//
|
|
// Seek to the third sector.
|
|
//
|
|
|
|
SeekPosition.QuadPart = 2 * ASSUMED_SECTOR_SIZE;
|
|
|
|
ArcStatus = ArcSeek(FileId, &SeekPosition, SeekAbsolute);
|
|
|
|
if (ArcStatus != ESUCCESS) {
|
|
|
|
DbgPrint("BlReadSecret: ArcStatus on ArcSeek: %x\n", ArcStatus);
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
//
|
|
// Read a secret-sized chunk.
|
|
//
|
|
|
|
ArcStatus = ArcRead(FileId, Secret, sizeof(RI_SECRET), &BytesRead);
|
|
|
|
if ((ArcStatus != ESUCCESS) ||
|
|
(BytesRead != sizeof(RI_SECRET))) {
|
|
|
|
DbgPrint("BlReadSecret: ArcStatus on ArcRead secret: %x, read %d\n", ArcStatus, BytesRead);
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
if (memcmp(Secret->Signature, RI_SECRET_SIGNATURE, 4) != 0) {
|
|
|
|
DbgPrint("BlReadSecret: No signature found\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
return ESUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ARC_STATUS
|
|
BlWriteSecret(
|
|
ULONG FileId,
|
|
PRI_SECRET Secret
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes the secret to the disk.
|
|
|
|
Arguments:
|
|
|
|
FileId - The FileId returned by BlOpenRawDisk.
|
|
|
|
Return Value:
|
|
|
|
ESUCCESS if the secret is written OK, an error otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ARC_STATUS ArcStatus;
|
|
ULONG BytesWritten;
|
|
LARGE_INTEGER SeekPosition;
|
|
|
|
|
|
//
|
|
// Seek to the third sector.
|
|
//
|
|
|
|
SeekPosition.QuadPart = 2 * ASSUMED_SECTOR_SIZE;
|
|
|
|
ArcStatus = ArcSeek(FileId, &SeekPosition, SeekAbsolute);
|
|
|
|
if (ArcStatus != ESUCCESS) {
|
|
|
|
DbgPrint("BlWriteSecret: ArcStatus on ArcSeek: %x\n", ArcStatus);
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
//
|
|
// Write a secret-sized chunk.
|
|
//
|
|
|
|
ArcStatus = ArcWrite(FileId, Secret, sizeof(RI_SECRET), &BytesWritten);
|
|
|
|
if ((ArcStatus != ESUCCESS) ||
|
|
(BytesWritten != sizeof(RI_SECRET))) {
|
|
|
|
DbgPrint("BlWriteSecret: ArcStatus on ArcWrite secret: %x, wrote %d\n", ArcStatus, BytesWritten);
|
|
return ArcStatus;
|
|
|
|
}
|
|
|
|
return ESUCCESS;
|
|
|
|
}
|
|
#endif // defined(REMOTE_BOOT)
|
|
|
|
|
|
VOID
|
|
BlInitializeSecret(
|
|
IN PUCHAR Domain,
|
|
IN PUCHAR User,
|
|
IN PUCHAR LmOwfPassword1,
|
|
IN PUCHAR NtOwfPassword1,
|
|
#if defined(REMOTE_BOOT)
|
|
IN PUCHAR LmOwfPassword2 OPTIONAL,
|
|
IN PUCHAR NtOwfPassword2 OPTIONAL,
|
|
#endif // defined(REMOTE_BOOT)
|
|
IN PUCHAR Sid,
|
|
IN OUT PRI_SECRET Secret
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the secret structures. The passwords
|
|
are OWFed and then encrypted with the User string.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
int Length;
|
|
struct RC4_KEYSTRUCT Key;
|
|
|
|
#if !defined(BL_USE_LM_PASSWORD)
|
|
UNREFERENCED_PARAMETER( LmOwfPassword1 );
|
|
#endif
|
|
|
|
memset(Secret, 0, sizeof(RI_SECRET));
|
|
|
|
memcpy(Secret->Signature, RI_SECRET_SIGNATURE, 4);
|
|
Secret->Version = 1;
|
|
|
|
Length = (ULONG)strlen((PCHAR)Domain);
|
|
memcpy(Secret->Domain, Domain, Length);
|
|
|
|
Length = (ULONG)strlen((PCHAR)User);
|
|
memcpy(Secret->User, User, Length);
|
|
|
|
memcpy(Secret->Sid, Sid, RI_SECRET_SID_SIZE);
|
|
|
|
//
|
|
// Encrypt the passwords using the user name.
|
|
//
|
|
|
|
#if defined(BL_USE_LM_PASSWORD)
|
|
memcpy(Secret->LmEncryptedPassword1, LmOwfPassword1, LM_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, LM_OWF_PASSWORD_SIZE, Secret->LmEncryptedPassword1);
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
if (LmOwfPassword2 != NULL) {
|
|
memcpy(Secret->LmEncryptedPassword2, LmOwfPassword2, LM_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, LM_OWF_PASSWORD_SIZE, Secret->LmEncryptedPassword2);
|
|
}
|
|
#endif // defined(REMOTE_BOOT)
|
|
#endif // defined(BL_USE_LM_PASSWORD)
|
|
|
|
memcpy(Secret->NtEncryptedPassword1, NtOwfPassword1, NT_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, (ULONG)strlen((PCHAR)User), User);
|
|
rc4(&Key, NT_OWF_PASSWORD_SIZE, Secret->NtEncryptedPassword1);
|
|
|
|
#if defined(REMOTE_BOOT)
|
|
if (NtOwfPassword2 != NULL) {
|
|
memcpy(Secret->NtEncryptedPassword2, NtOwfPassword2, NT_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, NT_OWF_PASSWORD_SIZE, Secret->NtEncryptedPassword2);
|
|
}
|
|
#endif // defined(REMOTE_BOOT)
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(REMOTE_BOOT_SECURITY)
|
|
VOID
|
|
BlParseSecret(
|
|
IN OUT PUCHAR Domain,
|
|
IN OUT PUCHAR User,
|
|
IN OUT PUCHAR LmOwfPassword1,
|
|
IN OUT PUCHAR NtOwfPassword1,
|
|
IN OUT PUCHAR LmOwfPassword2,
|
|
IN OUT PUCHAR NtOwfPassword2,
|
|
IN OUT PUCHAR Sid,
|
|
IN PRI_SECRET Secret
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine parses a secret structure. The passwords
|
|
are unencrypted with the User string and returned in OWF form.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
struct RC4_KEYSTRUCT Key;
|
|
|
|
memcpy(Domain, Secret->Domain, RI_SECRET_DOMAIN_SIZE);
|
|
Domain[RI_SECRET_DOMAIN_SIZE] = '\0';
|
|
|
|
memcpy(User, Secret->User, RI_SECRET_USER_SIZE);
|
|
User[RI_SECRET_USER_SIZE] = '\0';
|
|
|
|
memcpy(Sid, Secret->Sid, RI_SECRET_SID_SIZE);
|
|
|
|
//
|
|
// Decrypt the passwords using the user name.
|
|
//
|
|
|
|
#if defined(BL_USE_LM_PASSWORD)
|
|
memcpy(LmOwfPassword1, Secret->LmEncryptedPassword1, LM_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, LM_OWF_PASSWORD_SIZE, LmOwfPassword1);
|
|
|
|
memcpy(LmOwfPassword2, Secret->LmEncryptedPassword2, LM_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, LM_OWF_PASSWORD_SIZE, LmOwfPassword2);
|
|
#else
|
|
memset(LmOwfPassword1, 0, LM_OWF_PASSWORD_SIZE);
|
|
memset(LmOwfPassword2, 0, LM_OWF_PASSWORD_SIZE);
|
|
#endif // defined(BL_USE_LM_PASSWORD)
|
|
|
|
memcpy(NtOwfPassword1, Secret->NtEncryptedPassword1, NT_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, NT_OWF_PASSWORD_SIZE, NtOwfPassword1);
|
|
|
|
memcpy(NtOwfPassword2, Secret->NtEncryptedPassword2, NT_OWF_PASSWORD_SIZE);
|
|
rc4_key(&Key, strlen(User), User);
|
|
rc4(&Key, NT_OWF_PASSWORD_SIZE, NtOwfPassword2);
|
|
|
|
}
|
|
#endif // defined(REMOTE_BOOT_SECURITY)
|
|
|
|
|
|
|
|
VOID
|
|
BlOwfPassword(
|
|
IN PUCHAR Password,
|
|
IN PUNICODE_STRING UnicodePassword,
|
|
IN OUT PUCHAR LmOwfPassword,
|
|
IN OUT PUCHAR NtOwfPassword
|
|
)
|
|
{
|
|
char TmpText[CLEAR_BLOCK_LENGTH*2] = {'\0'};
|
|
int Length;
|
|
int i;
|
|
|
|
Length = (ULONG)strlen((PCHAR)Password);
|
|
|
|
//
|
|
// Copy the string to TmpText, converting to uppercase.
|
|
//
|
|
|
|
if (Length == 0 || Length > LM20_PWLEN) {
|
|
TmpText[0] = 0;
|
|
} else {
|
|
for (i = 0; i <= Length; i++) {
|
|
TmpText[i] = (char)toupper(Password[i]);
|
|
}
|
|
}
|
|
|
|
CalculateLmOwfPassword((PLM_PASSWORD)TmpText, (PLM_OWF_PASSWORD)LmOwfPassword);
|
|
|
|
CalculateNtOwfPassword(UnicodePassword, (PNT_OWF_PASSWORD)NtOwfPassword);
|
|
|
|
}
|
|
|