/*++ Copyright (c) 1998 Microsoft Corporation Module Name: cmds1.c Abstract: This module implements miscellaneous commands. Author: Wesley Witt (wesw) 21-Oct-1998 Revision History: --*/ #include "cmdcons.h" #pragma hdrstop BOOLEAN AllowWildCards; NTSTATUS RcSetFileAttributes( LPCWSTR lpFileName, DWORD dwFileAttributes ); NTSTATUS RcSetFileCompression( LPCWSTR szFileName, BOOLEAN bCompress ); NTSTATUS RcGetFileAttributes( LPCWSTR lpFileName, PULONG FileAttributes ); BOOLEAN pRcCmdEnumDelFiles( IN LPCWSTR Directory, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT NTSTATUS *Status, IN PWCHAR DosDirectorySpec ); ULONG RcCmdType( IN PTOKENIZED_LINE TokenizedLine ) { LPCWSTR Arg; HANDLE FileHandle; HANDLE SectionHandle; PVOID ViewBase; ULONG FileSize; ULONG rc; ULONG cbText; WCHAR *pText; NTSTATUS Status; if (RcCmdParseHelp( TokenizedLine, MSG_TYPE_HELP )) { return 1; } // // There should be a token for TYPE and one for the arg. // ASSERT(TokenizedLine->TokenCount == 2); // // Get the argument and convert it into a full NT pathname. // Arg = TokenizedLine->Tokens->Next->String; if (!RcFormFullPath(Arg,_CmdConsBlock->TemporaryBuffer,FALSE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) { RcMessageOut(MSG_ACCESS_DENIED); return 1; } // // Get the argument and convert it into a full NT pathname. // if (!RcFormFullPath(Arg,_CmdConsBlock->TemporaryBuffer,TRUE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } // // Map in the entire file. // FileHandle = NULL; Status = SpOpenAndMapFile( _CmdConsBlock->TemporaryBuffer, &FileHandle, &SectionHandle, &ViewBase, &FileSize, FALSE ); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_CANT_OPEN_FILE); return 1; } // // See if we think the file is Unicode. We think it's Unicode // if it's even length and starts with the Unicode text marker. // pText = ViewBase; cbText = FileSize; try { if( (cbText >= sizeof(WCHAR)) && (*pText == 0xfeff) && !(cbText & 1) ) { // // Assume it's already unicode. // pText = SpMemAlloc(cbText); RtlCopyMemory(pText,(WCHAR *)ViewBase+1,cbText-sizeof(WCHAR)); pText[cbText/sizeof(WCHAR)] = 0; } else { // // It's not Unicode. Convert it from ANSI to Unicode. // // Allocate a buffer large enough to hold the maximum // unicode text. This max size occurs when // every character is single-byte, and this size is // equal to exactly double the size of the single-byte text. // pText = SpMemAlloc((cbText+1)*sizeof(WCHAR)); RtlZeroMemory(pText,(cbText+1)*sizeof(WCHAR)); Status = RtlMultiByteToUnicodeN( pText, // output: newly allocated buffer cbText * sizeof(WCHAR), // max size of output &cbText, // receives # bytes in unicode text ViewBase, // input: ANSI text (mapped file) cbText // size of input ); } }except(IN_PAGE_ERROR) { Status = STATUS_IN_PAGE_ERROR; } if( NT_SUCCESS(Status) ) { pRcEnableMoreMode(); RcTextOut(pText); pRcDisableMoreMode(); } else { RcNtError(Status,MSG_CANT_READ_FILE); } if( pText != ViewBase ) { SpMemFree(pText); } SpUnmapFile(SectionHandle,ViewBase); ZwClose(FileHandle); return 1; } ULONG RcCmdDelete( IN PTOKENIZED_LINE TokenizedLine ) { WCHAR *Final; BOOLEAN Confirm = FALSE; OBJECT_ATTRIBUTES Obja; UNICODE_STRING UnicodeString; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; PWSTR DelSpec = NULL; PWSTR DosDelSpec = NULL; WCHAR Text[2]; PWSTR YesNo = NULL; ULONG rc; if (RcCmdParseHelp( TokenizedLine, MSG_DELETE_HELP )) { goto exit; } // // Fetch the spec for the file to be deleted and convert it // into a fully-qualified NT-style path. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) { RcMessageOut(MSG_INVALID_PATH); goto exit; } // // Leave room for appending * if necessary. // DelSpec = SpMemAlloc((wcslen(_CmdConsBlock->TemporaryBuffer)+3)*sizeof(WCHAR)); wcscpy(DelSpec,_CmdConsBlock->TemporaryBuffer); // // Do the same thing, except now we want the DOS-style name. // This is used for printing in case of errors. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) { RcMessageOut(MSG_INVALID_PATH); goto exit; } DosDelSpec = SpMemAlloc((wcslen(_CmdConsBlock->TemporaryBuffer)+3)*sizeof(WCHAR)); wcscpy(DosDelSpec,_CmdConsBlock->TemporaryBuffer); // // see if the user is authorized to delete this file // if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) { RcMessageOut(MSG_ACCESS_DENIED); goto exit; } if (RcDoesPathHaveWildCards(_CmdConsBlock->TemporaryBuffer)) { Confirm = TRUE; if (!AllowWildCards) { RcMessageOut(MSG_DEL_WILDCARD_NOT_SUPPORTED); goto exit; } } // // Check to see whether the target specifies a directory. // If so, add the * so we don't need to special-case // the confirmation message later. // INIT_OBJA(&Obja,&UnicodeString,DelSpec); Status = ZwOpenFile( &Handle, FILE_READ_ATTRIBUTES, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE ); if( NT_SUCCESS(Status) ) { ZwClose(Handle); SpConcatenatePaths(DelSpec,L"*"); SpConcatenatePaths(DosDelSpec,L"*"); Confirm = TRUE; } // // Fetch yes/no text // YesNo = SpRetreiveMessageText(ImageBase,MSG_YESNO,NULL,0); if (!YesNo) { Confirm = FALSE; } if (!InBatchMode) { while( Confirm ) { RcMessageOut(MSG_CONFIRM_DELETE,DosDelSpec); if( RcLineIn(Text,2) ) { if( (Text[0] == YesNo[0]) || (Text[0] == YesNo[1]) ) { // // Wants to do it. // Confirm = FALSE; } else { if( (Text[0] == YesNo[2]) || (Text[0] == YesNo[3]) ) { // // Doesn't want to do it. // goto exit; } } } } } // // Trim back the DOS-style path so it's a path to the directory // containing the file or files to be deleted. // *wcsrchr(DosDelSpec,L'\\') = 0; // Perform deletion via callback. // Status = RcEnumerateFiles(TokenizedLine->Tokens->Next->String, DelSpec, pRcCmdEnumDelFiles, DosDelSpec); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_FILE_ENUM_ERROR); } exit: if (DelSpec) { SpMemFree(DelSpec); } if (DosDelSpec) { SpMemFree(DosDelSpec); } if (YesNo) { SpMemFree(YesNo); } return 1; } BOOLEAN pRcCmdEnumDelFiles( IN LPCWSTR Directory, IN PFILE_BOTH_DIR_INFORMATION FileInfo, OUT NTSTATUS *Status, IN PWCHAR DosDirectorySpec ) { NTSTATUS status; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; WCHAR *p; FILE_DISPOSITION_INFORMATION Disposition; unsigned u; *Status = STATUS_SUCCESS; // // Skip directories // if( FileInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { return(TRUE); } // // Form fully qualified NT path of the file to be deleted. // u = ((wcslen(Directory)+2)*sizeof(WCHAR)) + FileInfo->FileNameLength; p = SpMemAlloc(u); wcscpy(p,Directory); SpConcatenatePaths(p,FileInfo->FileName); INIT_OBJA(&Obja,&UnicodeString,p); status = ZwOpenFile( &Handle, (ACCESS_MASK)DELETE, &Obja, &IoStatusBlock, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_NON_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT ); if( !NT_SUCCESS(status) ) { RcTextOut(DosDirectorySpec); RcTextOut(L"\\"); RcTextOut(FileInfo->FileName); RcTextOut(L"\r\n"); RcNtError(status,MSG_DELETE_ERROR); SpMemFree(p); return(TRUE); } #undef DeleteFile Disposition.DeleteFile = TRUE; status = ZwSetInformationFile( Handle, &IoStatusBlock, &Disposition, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation ); ZwClose(Handle); if( !NT_SUCCESS(status) ) { RcTextOut(DosDirectorySpec); RcTextOut(L"\\"); RcTextOut(FileInfo->FileName); RcTextOut(L"\r\n"); RcNtError(status,MSG_DELETE_ERROR); } SpMemFree(p); return(TRUE); } ULONG RcCmdRename( IN PTOKENIZED_LINE TokenizedLine ) { WCHAR *Arg; WCHAR *p,*q; NTSTATUS Status; ULONG rc; // // check for help // if (RcCmdParseHelp( TokenizedLine, MSG_RENAME_HELP )) { return 1; } // // There should be a token for RENAME and one each for the source and // target names. // if (TokenizedLine->TokenCount != 3) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // use the console's temporary buffer // p = _CmdConsBlock->TemporaryBuffer; // // process the SOURCE filename // Arg = TokenizedLine->Tokens->Next->String; // // Convert the SOURCE filname into a DOS path so we // can verify if the path is allowed by our security restrictions. // if (!RcFormFullPath(Arg,p,FALSE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } if (!RcIsPathNameAllowed(p,TRUE,FALSE)) { RcMessageOut(MSG_ACCESS_DENIED); return 1; } // // Convert the SOURCE filename into a fully qualified // NT-style path name. // if (!RcFormFullPath(Arg,p,TRUE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } // // using the same buffer for the TARGET name // q = p + wcslen(p) + 1; // // get the TARGET file name // Arg = TokenizedLine->Tokens->Next->Next->String; // // Verify that the TARGET filename does not contain // any path seperator characters or drive specifier // characters. // if( wcschr(Arg,L'\\') ) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } if( RcIsAlpha(Arg[0]) && (Arg[1] == L':') ) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // Convert the DESTINATION filename into a DOS path so we // can verify if the path is allowed by our security restrictions. // if (!RcFormFullPath(Arg,q,FALSE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } if (!RcIsPathNameAllowed(q,TRUE,FALSE)) { RcMessageOut(MSG_ACCESS_DENIED); return 1; } // // Convert the SOURCE filename into a fully qualified // NT-style path name. // if (!RcFormFullPath(Arg,q,TRUE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } // // OK, looks like a plain filename specification. // Glom it onto the end of the relevent part of the // source specification so we have 2 fully qualified names. // // wcscpy(q,p); // wcscpy(wcsrchr(q,L'\\')+1,Arg); // // Call worker routine to actually do the rename. // Status = SpRenameFile(p,q,TRUE); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_RENAME_ERROR, Arg); } return 1; } ULONG RcCmdMkdir( IN PTOKENIZED_LINE TokenizedLine ) { NTSTATUS Status; UNICODE_STRING UnicodeString; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; OBJECT_ATTRIBUTES Obja; ULONG rc; if (RcCmdParseHelp( TokenizedLine, MSG_MAKEDIR_HELP )) { return 1; } // // There should be a token for MKDIR and one for the target. // ASSERT(TokenizedLine->TokenCount == 2); // // Convert the given arg into a fully qualified NT path specification. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,TRUE)) { RcMessageOut(MSG_ACCESS_DENIED); return 1; } // // Convert the given arg into a fully qualified NT path specification. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } // // Create the directory. // INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer); Status = ZwCreateFile( &Handle, FILE_LIST_DIRECTORY | SYNCHRONIZE, &Obja, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_CREATE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT, NULL, 0 ); if( NT_SUCCESS(Status) ) { ZwClose(Handle); } else { RcNtError(Status,MSG_CREATE_DIR_FAILED,TokenizedLine->Tokens->Next->String); } return 1; } ULONG RcCmdRmdir( IN PTOKENIZED_LINE TokenizedLine ) { NTSTATUS Status; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES Obja; FILE_DISPOSITION_INFORMATION Disposition; ULONG rc; if (RcCmdParseHelp( TokenizedLine, MSG_REMOVEDIR_HELP )) { return 1; } // // There should be a token for RMDIR and one for the target. // ASSERT(TokenizedLine->TokenCount == 2); // // Convert the given arg into a fully qualified NT path specification. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,TRUE)) { RcMessageOut(MSG_ACCESS_DENIED); return 1; } // // Convert the given arg into a fully qualified NT path specification. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } INIT_OBJA(&Obja,&UnicodeString,_CmdConsBlock->TemporaryBuffer); Status = ZwOpenFile( &Handle, DELETE | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_RMDIR_ERROR); return 1; } Disposition.DeleteFile = TRUE; Status = ZwSetInformationFile( Handle, &IoStatusBlock, &Disposition, sizeof(FILE_DISPOSITION_INFORMATION), FileDispositionInformation ); ZwClose(Handle); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_RMDIR_ERROR); } return 1; } ULONG RcCmdSetFlags( IN PTOKENIZED_LINE TokenizedLine ) { if (RcCmdParseHelp( TokenizedLine, MSG_SETCMD_HELP )) { return 1; } if (TokenizedLine->TokenCount == 1) { RcTextOut( L"\r\n" ); RcMessageOut(MSG_SET_ALLOW_WILDCARDS,AllowWildCards?L"TRUE":L"FALSE"); RcMessageOut(MSG_SET_ALLOW_ALLPATHS,AllowAllPaths?L"TRUE":L"FALSE"); RcMessageOut(MSG_SET_ALLOW_REMOVABLE_MEDIA,AllowRemovableMedia?L"TRUE":L"FALSE"); RcMessageOut(MSG_SET_NO_COPY_PROMPT,NoCopyPrompt?L"TRUE":L"FALSE"); RcTextOut( L"\r\n" ); return 1; } // // should have the priviledge to use the SET command // if (TokenizedLine->TokenCount != 4) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } if (RcGetSETCommandStatus() != TRUE) { RcMessageOut(MSG_SETCMD_DISABLED); return 1; } if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowallpaths")==0) { if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) { AllowAllPaths = TRUE; } else { AllowAllPaths = FALSE; } return 1; } if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowwildcards")==0) { if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) { AllowWildCards = TRUE; } else { AllowWildCards = FALSE; } return 1; } if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"allowremovablemedia")==0) { if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) { AllowRemovableMedia = TRUE; } else { AllowRemovableMedia = FALSE; } return 1; } if (_wcsicmp(TokenizedLine->Tokens->Next->String,L"nocopyprompt")==0) { if (_wcsicmp(TokenizedLine->Tokens->Next->Next->Next->String,L"true")==0) { NoCopyPrompt = TRUE; } else { NoCopyPrompt = FALSE; } return 1; } RcMessageOut(MSG_SYNTAX_ERROR); return 1; } ULONG RcCmdAttrib( IN PTOKENIZED_LINE TokenizedLine ) { NTSTATUS Status; PWCHAR AttributeString; ULONG OldAttributes; ULONG NewAttributes; BOOLEAN SetAttribute; BOOLEAN bShowHelp = TRUE; BOOLEAN bChangeCompression = FALSE; // "attrib -h " should clear the hidden attribute // and not show the help if (TokenizedLine->TokenCount > 2){ PWCHAR szSecondParam = TokenizedLine->Tokens->Next->String; bShowHelp = !wcscmp( szSecondParam, L"/?" ); } if (bShowHelp && RcCmdParseHelp( TokenizedLine, MSG_ATTRIB_HELP )) { return 1; } if (TokenizedLine->TokenCount != 3) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // Fetch the spec for the file to be attribed and convert it // into a fully-qualified NT-style path. // if (!RcFormFullPath(TokenizedLine->Tokens->Next->Next->String,_CmdConsBlock->TemporaryBuffer,FALSE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } // // see if the user is authorized to change this file // if (!RcIsPathNameAllowed(_CmdConsBlock->TemporaryBuffer,TRUE,FALSE)) { RcMessageOut(MSG_ACCESS_DENIED); return 1; } if (!RcFormFullPath(TokenizedLine->Tokens->Next->Next->String,_CmdConsBlock->TemporaryBuffer,TRUE)) { RcMessageOut(MSG_INVALID_PATH); return 1; } Status = RcGetFileAttributes( _CmdConsBlock->TemporaryBuffer, &OldAttributes ); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_CANT_OPEN_FILE); return 1; } NewAttributes = OldAttributes; for(AttributeString = TokenizedLine->Tokens->Next->String; *AttributeString; AttributeString++){ if(*AttributeString == L'+'){ SetAttribute = TRUE; AttributeString++; } else if(*AttributeString == L'-'){ SetAttribute = FALSE; AttributeString++; } else { // attribute change should start with "+" or "-" if (AttributeString == TokenizedLine->Tokens->Next->String) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // use the old state for setting or resetting (for +rsh } switch(*AttributeString){ case L'h': case L'H': if (SetAttribute) NewAttributes |= FILE_ATTRIBUTE_HIDDEN; else NewAttributes &= ~FILE_ATTRIBUTE_HIDDEN; break; case L's': case L'S': if (SetAttribute) NewAttributes |= FILE_ATTRIBUTE_SYSTEM; else NewAttributes &= ~FILE_ATTRIBUTE_SYSTEM; break; case L'r': case L'R': if (SetAttribute) NewAttributes |= FILE_ATTRIBUTE_READONLY; else NewAttributes &= ~FILE_ATTRIBUTE_READONLY; break; case L'a': case L'A': if (SetAttribute) NewAttributes |= FILE_ATTRIBUTE_ARCHIVE; else NewAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; break; case L'c': case L'C': bChangeCompression = TRUE; if (SetAttribute) NewAttributes |= FILE_ATTRIBUTE_COMPRESSED; else NewAttributes &= ~FILE_ATTRIBUTE_COMPRESSED; break; default: RcMessageOut(MSG_SYNTAX_ERROR); return 1; } /* if (SetAttribute) { FileAttributes |= Attribute; } else { FileAttributes &= ~Attribute; } */ } Status = RcSetFileAttributes( _CmdConsBlock->TemporaryBuffer, NewAttributes ); if( !NT_SUCCESS(Status) ) { RcNtError(Status,MSG_CANT_OPEN_FILE); } else { if (bChangeCompression) { BOOLEAN bCompress = (NewAttributes & FILE_ATTRIBUTE_COMPRESSED) ? TRUE : FALSE; Status = RcSetFileCompression(_CmdConsBlock->TemporaryBuffer, bCompress); if ( !NT_SUCCESS(Status) ) RcNtError(Status, MSG_ATTRIB_CANNOT_CHANGE_COMPRESSION); } } return 1; } NTSTATUS RcSetFileCompression( LPCWSTR szFileName, BOOLEAN bCompress ) { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; FILE_BASIC_INFORMATION BasicInfo; UNICODE_STRING FileName; USHORT uCompressionType; INIT_OBJA(&Obja,&FileName,szFileName); // // Open the file inhibiting the reparse behavior. // Status = ZwOpenFile( &Handle, (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if (NT_SUCCESS(Status)) { // // set & reset the compression bit also // uCompressionType = bCompress ? COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE; Status = ZwFsControlFile( Handle, // file handle NULL, // event handle NULL, // APC rountine pointer NULL, // APC context &IoStatusBlock, // IO status block FSCTL_SET_COMPRESSION, // IOCTL code &uCompressionType, // input buffer sizeof(uCompressionType), // input buffer length NULL, // output buffer pointer 0); // output buffer length DbgPrint( "ZwDeviceIoControlFile() status : %X\r\n", Status); ZwClose(Handle); } return Status; } NTSTATUS RcSetFileAttributes( LPCWSTR lpFileName, DWORD dwFileAttributes ) /*++ Routine Description: The attributes of a file can be set using SetFileAttributes. Arguments: lpFileName - Supplies the file name of the file whose attributes are to be set. dwFileAttributes - Specifies the file attributes to be set for the file. Any combination of flags is acceptable except that all other flags override the normal file attribute, FILE_ATTRIBUTE_NORMAL. FileAttributes Flags: FILE_ATTRIBUTE_NORMAL - A normal file should be created. FILE_ATTRIBUTE_READONLY - A read-only file should be created. FILE_ATTRIBUTE_HIDDEN - A hidden file should be created. FILE_ATTRIBUTE_SYSTEM - A system file should be created. FILE_ATTRIBUTE_ARCHIVE - The file should be marked so that it will be archived. Return Value: NTStatus of last NT call --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; HANDLE Handle; IO_STATUS_BLOCK IoStatusBlock; FILE_BASIC_INFORMATION BasicInfo; UNICODE_STRING FileName; USHORT uCompressionType; INIT_OBJA(&Obja,&FileName,lpFileName); // // Open the file ihibiting the reparse behavior. // Status = ZwOpenFile( &Handle, (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT ); if ( !NT_SUCCESS(Status) ) { // // Back level file systems may not support reparse points. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER. // if ( Status == STATUS_INVALID_PARAMETER ) { // // Open the file without inhibiting the reparse behavior. // Status = ZwOpenFile( &Handle, (ACCESS_MASK)FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if ( !NT_SUCCESS(Status) ) { return Status; } } else { return Status; } } // // Set the basic attributes // ZeroMemory(&BasicInfo,sizeof(BasicInfo)); BasicInfo.FileAttributes = (dwFileAttributes & FILE_ATTRIBUTE_VALID_FLAGS) | FILE_ATTRIBUTE_NORMAL; Status = ZwSetInformationFile( Handle, &IoStatusBlock, &BasicInfo, sizeof(BasicInfo), FileBasicInformation ); ZwClose(Handle); return Status; } NTSTATUS RcGetFileAttributes( LPCWSTR lpFileName, PULONG FileAttributes ) /*++ Routine Description: Arguments: lpFileName - Supplies the file name of the file whose attributes are to be set. Return Value: Not -1 - Returns the attributes of the specified file. Valid returned attributes are: FILE_ATTRIBUTE_NORMAL - The file is a normal file. FILE_ATTRIBUTE_READONLY - The file is marked read-only. FILE_ATTRIBUTE_HIDDEN - The file is marked as hidden. FILE_ATTRIBUTE_SYSTEM - The file is marked as a system file. FILE_ATTRIBUTE_ARCHIVE - The file is marked for archive. FILE_ATTRIBUTE_DIRECTORY - The file is marked as a directory. FILE_ATTRIBUTE_REPARSE_POINT - The file is marked as a reparse point. FILE_ATTRIBUTE_VOLUME_LABEL - The file is marked as a volume lable. 0xffffffff - The operation failed. Extended error status is available using GetLastError. --*/ { NTSTATUS Status; OBJECT_ATTRIBUTES Obja; UNICODE_STRING FileName; FILE_BASIC_INFORMATION BasicInfo; IO_STATUS_BLOCK IoStatusBlock; HANDLE Handle; INIT_OBJA(&Obja,&FileName,lpFileName); // // Open the file inhibiting the reparse behavior. // Status = ZwOpenFile( &Handle, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT ); if ( !NT_SUCCESS(Status) ) { // // Back level file systems may not support reparse points. // We infer this is the case when the Status is STATUS_INVALID_PARAMETER. // if ( Status == STATUS_INVALID_PARAMETER ) { // // Open the file without inhibiting the reparse behavior. // Status = ZwOpenFile( &Handle, (ACCESS_MASK)FILE_READ_ATTRIBUTES | SYNCHRONIZE, &Obja, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ); if ( !NT_SUCCESS(Status) ) { return Status; } } else { return Status; } } // // Query the file // Status = ZwQueryInformationFile( Handle, &IoStatusBlock, (PVOID) &BasicInfo, sizeof(BasicInfo), FileBasicInformation ); if (NT_SUCCESS(Status)) { *FileAttributes = BasicInfo.FileAttributes; } ZwClose( Handle ); return Status; } ULONG RcCmdNet( IN PTOKENIZED_LINE TokenizedLine ) { WCHAR *Share; WCHAR *User; WCHAR *pwch; WCHAR PasswordBuffer[64]; WCHAR Drive[3]; NTSTATUS Status = STATUS_SUCCESS; IO_STATUS_BLOCK IoStatusBlock; OBJECT_ATTRIBUTES ObjectAttributes; // // check for help // if (RcCmdParseHelp( TokenizedLine, MSG_NET_USE_HELP )) { return 1; } // // There should be a token for NET and USE and one each for the server\share, and possible // tokens for the /u:domainname\username and password. // if ((TokenizedLine->TokenCount < 3) || (TokenizedLine->TokenCount > 5)) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // The only NET command supported is USE, so verify that the second token is that. // if (_wcsicmp(TokenizedLine->Tokens->Next->String, L"USE")){ RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // Get the first parameter to NET USE // Share = TokenizedLine->Tokens->Next->Next->String; if (*Share == L'\\') { // attempt at making a connection // // Verify the share name parameter // if (*(Share+1) != L'\\') { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // get the user logon context // if (TokenizedLine->TokenCount > 3) { // // The command has the context in it, so get it. // User = TokenizedLine->Tokens->Next->Next->Next->String; if (*User != L'/') { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } User++; pwch = User; while ((*pwch != UNICODE_NULL) && (*pwch != L':')) { pwch++; } if (*pwch != L':') { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } *pwch = UNICODE_NULL; pwch++; if (_wcsicmp(User, L"USER") && _wcsicmp(User, L"U")) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } User = pwch; // // Get the password // if (TokenizedLine->TokenCount == 4) { RcMessageOut( MSG_NET_USE_PROMPT_PASSWORD ); RtlZeroMemory( PasswordBuffer, sizeof(PasswordBuffer) ); RcPasswordIn( PasswordBuffer, 60 ); } else { if (wcslen(TokenizedLine->Tokens->Next->Next->Next->Next->String) > 60) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } wcscpy(PasswordBuffer, TokenizedLine->Tokens->Next->Next->Next->Next->String); if ((PasswordBuffer[0] == L'*') && (PasswordBuffer[1] == UNICODE_NULL)) { RcMessageOut( MSG_NET_USE_PROMPT_PASSWORD ); RtlZeroMemory( PasswordBuffer, sizeof(PasswordBuffer) ); RcPasswordIn( PasswordBuffer, 60 ); } else if (PasswordBuffer[0] == L'"') { pwch = &(PasswordBuffer[1]); while (*pwch != UNICODE_NULL) { pwch++; } pwch--; if ((*pwch == L'"') && (pwch != &(PasswordBuffer[1]))) { *pwch = UNICODE_NULL; } RtlMoveMemory(PasswordBuffer, &(PasswordBuffer[1]), (PtrToUlong(pwch) - PtrToUlong(PasswordBuffer)) + sizeof(WCHAR)); } } } else { // // If we allow holding a current context, then we would use that here, but we currently // don't, so spew a syntax error message. // RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // Call worker routine to make the connection // Status = RcDoNetUse(Share, User, PasswordBuffer, Drive); RtlSecureZeroMemory(PasswordBuffer, sizeof(PasswordBuffer)); if( !NT_SUCCESS(Status) ) { RcNtError(Status, MSG_NET_USE_ERROR); } else { RcMessageOut(MSG_NET_USE_DRIVE_LETTER, Share, Drive); } } else { // attempt to disconnect // // Verify drive letter parameter // if ((*(Share+1) != L':') || (*(Share + 2) != UNICODE_NULL)) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // Verify /d parameter // User = TokenizedLine->Tokens->Next->Next->Next->String; if ((*User != L'/') || ((*(User + 1) != L'd') && (*(User + 1) != L'D'))) { RcMessageOut(MSG_SYNTAX_ERROR); return 1; } // // Call worker routine to actually do the disconnect. // Status = RcNetUnuse(Share); if( !NT_SUCCESS(Status) ) { RcNtError(Status, MSG_NET_USE_ERROR); } } return 1; }