/*++ 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 }