windows-nt/Source/XPSP1/NT/base/ntsetup/textmode/cmdcons/util.c

695 lines
17 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
util.c
Abstract:
This module implements all utility functions.
Author:
Wesley Witt (wesw) 21-Oct-1998
Revision History:
--*/
#include "cmdcons.h"
#pragma hdrstop
#include "remboot.h"
HANDLE NetUseHandles[26] = { NULL };
BOOLEAN RdrIsInKernelMode = FALSE;
RC_ALLOWED_DIRECTORY AllowedDirs[] = {
{ FALSE, L"$WIN_NT$.~BT" },
{ FALSE, L"$WIN_NT$.~LS" },
{ FALSE, L"CMDCONS" },
{ TRUE, L"SYSTEM VOLUME INFORMATION" }
};
BOOLEAN
RcIsPathNameAllowed(
IN LPCWSTR FullPath,
IN BOOLEAN RemovableMediaOk,
IN BOOLEAN Mkdir
)
/*++
Routine Description:
This routine verifies that the specified path name is
allowed based on the security context that the console
user is logged into.
Arguments:
FullPath - specifies the full path to be verified.
Return Value:
FALSE if failure, indicating the path is not allowed.
TRUE otherwise.
--*/
{
WCHAR TempBuf[MAX_PATH*2];
UNICODE_STRING UnicodeString;
OBJECT_ATTRIBUTES Obja;
HANDLE Handle;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
BOOL isDirectory = TRUE;
//
// should we bypass security?
//
if (AllowAllPaths) {
return TRUE;
}
//
// some special processing for dos paths
// we must make sure that only the root and %systemdir% are allowed.
//
if (FullPath[1] == L':' && FullPath[2] == L'\\' && FullPath[3] == 0) {
//
// root directory is ok.
//
return TRUE;
}
SpStringToUpper((PWSTR)FullPath);
if (!RcGetNTFileName((PWSTR)FullPath,TempBuf))
return FALSE;
INIT_OBJA(&Obja,&UnicodeString,TempBuf);
Status = ZwOpenFile(
&Handle,
FILE_READ_ATTRIBUTES,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_DIRECTORY_FILE
);
if( !NT_SUCCESS(Status) ) {
isDirectory = FALSE;
} else {
ZwClose( Handle );
}
if (isDirectory == FALSE && wcsrchr( FullPath, L'\\' ) == ( &FullPath[2] )) {
//
// if the cannonicalized path has only one slash the user is trying to do something
// to the files in the root, which we allow.
//
// however we do not allow users to mess with directories at the root
//
if (Mkdir) {
return FALSE;
} else {
return TRUE;
}
}
ASSERT(SelectedInstall != NULL);
if(SelectedInstall != NULL) {
//
// Get the length of the first element in the path
//
PCWSTR sz;
DWORD Len;
DWORD i;
WCHAR SelectedInstallDrive;
sz = wcschr(FullPath + 3, L'\\');
if(NULL == sz) {
sz = FullPath + wcslen(FullPath);
}
Len = (DWORD) ((sz - FullPath) - 3);
SelectedInstallDrive = RcToUpper(SelectedInstall->DriveLetter);
//
// See if path begins with the install path
//
if(FullPath[0] == SelectedInstallDrive && 0 == _wcsnicmp(FullPath + 3, SelectedInstall->Path, Len)) {
return TRUE;
}
//
// See if the path begins with an allowed dir
//
for(i = 0; i < sizeof(AllowedDirs) / sizeof(AllowedDirs[0]); ++i) {
if((!AllowedDirs[i].MustBeOnInstallDrive || FullPath[0] == SelectedInstallDrive) &&
0 == _wcsnicmp(FullPath + 3, AllowedDirs[i].Directory, Len)) {
return TRUE;
}
}
}
if (RcIsFileOnRemovableMedia(TempBuf) == STATUS_SUCCESS) {
if (RemovableMediaOk) {
return TRUE;
}
}
if (RcIsNetworkDrive(TempBuf) == STATUS_SUCCESS) {
//
// Context that was used for connection will do appropriate security checking.
//
return TRUE;
}
return FALSE;
}
BOOLEAN
RcDoesPathHaveWildCards(
IN LPCWSTR FullPath
)
/*++
Routine Description:
This routine verifies that the specified path name is
allowed based on the security context that the console
user is logged into.
Arguments:
FullPath - specifies the full path to be verified.
Return Value:
FALSE if failure, indicating the path is not allowed.
TRUE otherwise.
--*/
{
if (wcsrchr( FullPath, L'*' )) {
return TRUE;
}
if (wcsrchr( FullPath, L'?' )) {
return TRUE;
}
return FALSE;
}
NTSTATUS
RcIsNetworkDrive(
IN PWSTR FileName
)
/*++
Routine Description:
This routine returns if the FileName given is a network path.
Arguments:
FileName - specifies the full path to be checked.
Return Value:
Any other than STATUS_SUCCESS if failure, indicating the path is not on the network,
STATUS_SUCCESS otherwise.
--*/
{
NTSTATUS Status;
FILE_FS_DEVICE_INFORMATION DeviceInfo;
PWSTR BaseNtName;
if (wcsncmp(FileName, L"\\DosDevice", wcslen(L"\\DosDevice")) == 0) {
Status = GetDriveLetterLinkTarget( FileName, &BaseNtName );
if (!NT_SUCCESS(Status)) {
return Status;
}
} else {
BaseNtName = FileName;
}
Status = pRcGetDeviceInfo( BaseNtName, &DeviceInfo );
if(NT_SUCCESS(Status)) {
if (DeviceInfo.DeviceType != FILE_DEVICE_NETWORK_FILE_SYSTEM) {
Status = STATUS_NO_MEDIA;
}
}
return Status;
}
NTSTATUS
RcDoNetUse(
PWSTR Share,
PWSTR User,
PWSTR Password,
PWSTR Drive
)
/*++
Routine Description:
This routine attempts to make a connection using the redirector to the remote server.
Arguments:
Share - A string of the form "\\server\share"
User - A string of the form "domain\user"
Password - A string containing the password information.
Drive - Filled in with a string of the form "X", where X is the drive letter the share
has been mapped to.
Return Value:
STATUS_SUCCESS if successful, indicating Drive contains the mapped drive letter,
otherwise the appropriate error code.
--*/
{
NTSTATUS Status;
PWSTR NtDeviceName;
ULONG ShareLength;
WCHAR DriveLetter;
WCHAR temporaryBuffer[128];
PWCHAR Temp, Temp2;
HANDLE Handle;
ULONG EaBufferLength;
PWSTR UserName;
PWSTR DomainName;
PVOID EaBuffer;
UNICODE_STRING UnicodeString;
UNICODE_STRING UnicodeString2;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_FULL_EA_INFORMATION FullEaInfo;
//
// Switch the redirector to kernel-mode security if it is not.
//
if (!RdrIsInKernelMode) {
Status = PutRdrInKernelMode();
if (!NT_SUCCESS(Status)) {
return Status;
}
RdrIsInKernelMode = TRUE;
}
//
// Search for an open drive letter, starting at D: and working up.
//
wcscpy(temporaryBuffer, L"\\DosDevices\\D:");
Temp = wcsstr(temporaryBuffer, L"D:");
for (DriveLetter = L'D'; (Temp && (DriveLetter <= L'Z')); DriveLetter++) {
*Temp = DriveLetter;
Status = GetDriveLetterLinkTarget( temporaryBuffer, &Temp2 );
if (!NT_SUCCESS(Status)) {
break;
}
}
if (DriveLetter > L'Z') {
return STATUS_OBJECT_NAME_INVALID;
}
//
// Build the NT device name.
//
ShareLength = wcslen(Share);
NtDeviceName = SpMemAlloc(ShareLength * sizeof(WCHAR) + sizeof(L"\\Device\\LanmanRedirector\\;X:0"));
if (NtDeviceName == NULL) {
return STATUS_NO_MEMORY;
}
wcscpy(NtDeviceName, L"\\Device\\LanmanRedirector\\;");
temporaryBuffer[0] = DriveLetter;
temporaryBuffer[1] = UNICODE_NULL;
wcscat(NtDeviceName, temporaryBuffer);
wcscat(NtDeviceName, L":0");
wcscat(NtDeviceName, Share + 1);
//
// Chop the username and domainname into individual values.
//
wcscpy(temporaryBuffer, User);
DomainName = temporaryBuffer;
UserName = wcsstr(temporaryBuffer, L"\\");
if (UserName == NULL) {
SpMemFree(NtDeviceName);
return STATUS_OBJECT_NAME_INVALID;
}
*UserName = UNICODE_NULL;
UserName++;
//
// Create buffer with user credentials
//
EaBufferLength = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
EaBufferLength += sizeof(EA_NAME_DOMAIN);
EaBufferLength += (wcslen(DomainName) * sizeof(WCHAR));
if (EaBufferLength & (sizeof(ULONG) - 1)) {
//
// Long align the next entry
//
EaBufferLength += (sizeof(ULONG) - (EaBufferLength & (sizeof(ULONG) - 1)));
}
EaBufferLength += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
EaBufferLength += sizeof(EA_NAME_USERNAME);
EaBufferLength += (wcslen(UserName) * sizeof(WCHAR));
if (EaBufferLength & (sizeof(ULONG) - 1)) {
//
// Long align the next entry
//
EaBufferLength += (sizeof(ULONG) - (EaBufferLength & (sizeof(ULONG) - 1)));
}
EaBufferLength += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]);
EaBufferLength += sizeof(EA_NAME_PASSWORD);
EaBufferLength += (wcslen(Password) * sizeof(WCHAR));
EaBuffer = SpMemAlloc(EaBufferLength);
if (EaBuffer == NULL) {
SpMemFree(NtDeviceName);
return STATUS_NO_MEMORY;
}
FullEaInfo = (PFILE_FULL_EA_INFORMATION)EaBuffer;
FullEaInfo->Flags = 0;
FullEaInfo->EaNameLength = sizeof(EA_NAME_DOMAIN) - 1;
FullEaInfo->EaValueLength = (wcslen(DomainName)) * sizeof(WCHAR);
strcpy(&(FullEaInfo->EaName[0]), EA_NAME_DOMAIN);
memcpy(&(FullEaInfo->EaName[FullEaInfo->EaNameLength + 1]), DomainName, FullEaInfo->EaValueLength);
FullEaInfo->NextEntryOffset = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
FullEaInfo->EaNameLength + 1 +
FullEaInfo->EaValueLength;
if (FullEaInfo->NextEntryOffset & (sizeof(ULONG) - 1)) {
FullEaInfo->NextEntryOffset += (sizeof(ULONG) -
(FullEaInfo->NextEntryOffset &
(sizeof(ULONG) - 1)));
}
FullEaInfo = (PFILE_FULL_EA_INFORMATION)(((char *)FullEaInfo) + FullEaInfo->NextEntryOffset);
FullEaInfo->Flags = 0;
FullEaInfo->EaNameLength = sizeof(EA_NAME_USERNAME) - 1;
FullEaInfo->EaValueLength = (wcslen(UserName)) * sizeof(WCHAR);
strcpy(&(FullEaInfo->EaName[0]), EA_NAME_USERNAME);
memcpy(&(FullEaInfo->EaName[FullEaInfo->EaNameLength + 1]), UserName, FullEaInfo->EaValueLength);
FullEaInfo->NextEntryOffset = FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
FullEaInfo->EaNameLength + 1 +
FullEaInfo->EaValueLength;
if (FullEaInfo->NextEntryOffset & (sizeof(ULONG) - 1)) {
FullEaInfo->NextEntryOffset += (sizeof(ULONG) -
(FullEaInfo->NextEntryOffset &
(sizeof(ULONG) - 1)));
}
FullEaInfo = (PFILE_FULL_EA_INFORMATION)(((char *)FullEaInfo) + FullEaInfo->NextEntryOffset);
FullEaInfo->Flags = 0;
FullEaInfo->EaNameLength = sizeof(EA_NAME_PASSWORD) - 1;
FullEaInfo->EaValueLength = (wcslen(Password)) * sizeof(WCHAR);
strcpy(&(FullEaInfo->EaName[0]), EA_NAME_PASSWORD);
memcpy(&(FullEaInfo->EaName[FullEaInfo->EaNameLength + 1]), Password, FullEaInfo->EaValueLength);
FullEaInfo->NextEntryOffset = 0;
//
// Now make the connection
//
RtlInitUnicodeString(&UnicodeString, NtDeviceName);
InitializeObjectAttributes(&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = ZwCreateFile(&Handle,
SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN_IF,
(FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT),
EaBuffer,
EaBufferLength
);
if (NT_SUCCESS(Status) && NT_SUCCESS(IoStatusBlock.Status)) {
//
// Save off the handle so we can close it later if need be
//
NetUseHandles[DriveLetter - L'A'] = Handle;
Drive[0] = DriveLetter;
Drive[1] = L':';
Drive[2] = UNICODE_NULL;
//
// Now create a symbolic link from the dos drive letter to the redirector
//
wcscpy(temporaryBuffer, L"\\DosDevices\\");
wcscat(temporaryBuffer, Drive);
RtlInitUnicodeString(&UnicodeString2, temporaryBuffer);
Status = IoCreateSymbolicLink(&UnicodeString2, &UnicodeString);
if (!NT_SUCCESS(Status)) {
ZwClose(Handle);
NetUseHandles[DriveLetter - L'A'] = NULL;
} else {
RcAddDrive(DriveLetter);
}
}
SpMemFree(NtDeviceName);
return Status;
}
NTSTATUS
RcNetUnuse(
PWSTR Drive
)
/*++
Routine Description:
This routine closes a network connection.
Arguments:
Drive - A string of the form "X:", where X is the drive letter returned by a previous call to
NetDoNetUse().
Return Value:
STATUS_SUCCESS if successful, indicating the drive letter has been unmapped,
otherwise the appropriate error code.
--*/
{
NTSTATUS Status;
WCHAR DriveLetter;
WCHAR temporaryBuffer[128];
UNICODE_STRING UnicodeString;
DriveLetter = *Drive;
if ((DriveLetter >= L'a') && (DriveLetter <= L'z')) {
DriveLetter = L'A' + (DriveLetter - L'a');
}
if ((DriveLetter < L'A') | (DriveLetter > L'Z')) {
return STATUS_OBJECT_NAME_INVALID;
}
if (NetUseHandles[DriveLetter - L'A'] == NULL) {
return STATUS_OBJECT_NAME_INVALID;
}
if (RcGetCurrentDriveLetter() == DriveLetter) {
return STATUS_CONNECTION_IN_USE;
}
wcscpy(temporaryBuffer, L"\\DosDevices\\");
wcscat(temporaryBuffer, Drive);
RtlInitUnicodeString(&UnicodeString, temporaryBuffer);
Status = IoDeleteSymbolicLink(&UnicodeString);
if (NT_SUCCESS(Status)) {
ZwClose(NetUseHandles[DriveLetter - L'A']);
NetUseHandles[DriveLetter - L'A'] = NULL;
RcRemoveDrive(DriveLetter);
}
return Status;
}
NTSTATUS
PutRdrInKernelMode(
VOID
)
/*++
Routine Description:
This routine IOCTLs down to the rdr to force it to use kernel-mode security.
Arguments:
None.
Return Value:
STATUS_SUCCESS if successful, otherwise the appropriate error code.
--*/
{
OBJECT_ATTRIBUTES ObjectAttributes;
UNICODE_STRING UnicodeString;
IO_STATUS_BLOCK IoStatusBlock;
NTSTATUS Status;
HANDLE Handle;
RtlInitUnicodeString(&UnicodeString, DD_NFS_DEVICE_NAME_U);
InitializeObjectAttributes(
&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = ZwCreateFile(
&Handle,
GENERIC_READ | GENERIC_WRITE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if (!NT_SUCCESS(Status)) {
KdPrint(("SPCMDCON: Unable to open redirector. %x\n", Status));
return Status;
}
Status = ZwDeviceIoControlFile(Handle,
NULL,
NULL,
NULL,
&IoStatusBlock,
IOCTL_LMMR_USEKERNELSEC,
NULL,
0,
NULL,
0
);
if (NT_SUCCESS(Status)) {
Status = IoStatusBlock.Status;
}
ZwClose(Handle);
return Status;
}
BOOLEAN
RcIsArc(
VOID
)
/*++
Routine Description:
Run time check to determine if this is an Arc system. We attempt to read an
Arc variable using the Hal. This will fail for Bios based systems.
Arguments:
None
Return Value:
True = This is an Arc system.
--*/
{
#ifdef _X86_
ARC_STATUS ArcStatus = EBADF;
//
// Get the env var into the temp buffer.
//
UCHAR wbuff[130];
//
// Get the env var into the temp buffer.
//
ArcStatus = HalGetEnvironmentVariable(
"OsLoader",
sizeof(wbuff),
wbuff
);
return((ArcStatus == ESUCCESS) ? TRUE: FALSE);
#else
return TRUE;
#endif
}