NTSTATUS SrvBuildTreeRelativePath( PLWIO_SRV_TREE pTree, PWSTR pwszFilename, PIO_FILE_NAME pFilename ) { NTSTATUS ntStatus = STATUS_SUCCESS; BOOLEAN bInLock = FALSE; PWSTR pwszFilePath = NULL; if (SrvTreeIsNamedPipe(pTree)) { LWIO_LOCK_RWMUTEX_SHARED(bInLock, &pTree->pShareInfo->mutex); ntStatus = SrvBuildFilePath( pTree->pShareInfo->pwszPath, pwszFilename, &pwszFilePath); BAIL_ON_NT_STATUS(ntStatus); } else { wchar16_t wszBackslash[] = {'\\', 0}; if (!IsNullOrEmptyString(pwszFilename) && SMBWc16sCmp(pwszFilename, &wszBackslash[0])) { ntStatus = SrvBuildFilePath( NULL, pwszFilename, &pwszFilePath); BAIL_ON_NT_STATUS(ntStatus); } LWIO_LOCK_RWMUTEX_SHARED(bInLock, &pTree->pShareInfo->mutex); pFilename->RootFileHandle = pTree->hFile; } pFilename->FileName = pwszFilePath; cleanup: LWIO_UNLOCK_RWMUTEX(bInLock, &pTree->pShareInfo->mutex); return ntStatus; error: goto cleanup; }
static NTSTATUS SrvBuildOpenResponse( PSRV_EXEC_CONTEXT pExecContext ) { NTSTATUS ntStatus = 0; PLWIO_SRV_CONNECTION pConnection = pExecContext->pConnection; PSRV_PROTOCOL_EXEC_CONTEXT pCtxProtocol = pExecContext->pProtocolContext; PSRV_EXEC_CONTEXT_SMB_V1 pCtxSmb1 = pCtxProtocol->pSmb1Context; ULONG iMsg = pCtxSmb1->iMsg; PSRV_MESSAGE_SMB_V1 pSmbRequest = &pCtxSmb1->pRequests[iMsg]; PSRV_MESSAGE_SMB_V1 pSmbResponse = &pCtxSmb1->pResponses[iMsg]; POPEN_RESPONSE_HEADER pResponseHeader = NULL; // Do not free PBYTE pOutBuffer = pSmbResponse->pBuffer; ULONG ulBytesAvailable = pSmbResponse->ulBytesAvailable; ULONG ulOffset = 0; ULONG ulTotalBytesUsed = 0; PSRV_OPEN_STATE_SMB_V1 pOpenState = NULL; pOpenState = (PSRV_OPEN_STATE_SMB_V1)pCtxSmb1->hState; if (!pSmbResponse->ulSerialNum) { ntStatus = SrvMarshalHeader_SMB_V1( pOutBuffer, ulOffset, ulBytesAvailable, COM_OPEN_ANDX, STATUS_SUCCESS, TRUE, pConnection->serverProperties.Capabilities, pOpenState->pTree->tid, SMB_V1_GET_PROCESS_ID(pSmbRequest->pHeader), pCtxSmb1->pSession->uid, pSmbRequest->pHeader->mid, pConnection->serverProperties.bRequireSecuritySignatures, &pSmbResponse->pHeader, &pSmbResponse->pWordCount, &pSmbResponse->pAndXHeader, &pSmbResponse->usHeaderSize); } else { ntStatus = SrvMarshalHeaderAndX_SMB_V1( pOutBuffer, ulOffset, ulBytesAvailable, COM_OPEN_ANDX, &pSmbResponse->pWordCount, &pSmbResponse->pAndXHeader, &pSmbResponse->usHeaderSize); } BAIL_ON_NT_STATUS(ntStatus); pOutBuffer += pSmbResponse->usHeaderSize; ulOffset += pSmbResponse->usHeaderSize; ulBytesAvailable -= pSmbResponse->usHeaderSize; ulTotalBytesUsed += pSmbResponse->usHeaderSize; *pSmbResponse->pWordCount = 15; if (ulBytesAvailable < sizeof(OPEN_RESPONSE_HEADER)) { ntStatus = STATUS_INVALID_BUFFER_SIZE; BAIL_ON_NT_STATUS(ntStatus); } pResponseHeader = (POPEN_RESPONSE_HEADER)pOutBuffer; // pOutBuffer += sizeof(OPEN_RESPONSE_HEADER); // ulOffset += sizeof(OPEN_RESPONSE_HEADER); // ulBytesAvailable -= sizeof(OPEN_RESPONSE_HEADER); ulTotalBytesUsed += sizeof(OPEN_RESPONSE_HEADER); pResponseHeader->usFid = pOpenState->pFile->fid; pResponseHeader->ulServerFid = pOpenState->pFile->fid; pResponseHeader->usOpenAction = pOpenState->ulCreateAction; switch (pOpenState->ucOplockLevel) { case SMB_OPLOCK_LEVEL_I: // file is opened only by this user at the current time pResponseHeader->usOpenAction |= 0x8000; break; default: pResponseHeader->usOpenAction &= ~0x8000; break; } // TODO: Mirroring this field is close, but probably not exactly correct pResponseHeader->usGrantedAccess = pOpenState->pRequestHeader->usDesiredAccess; pResponseHeader->usFileAttributes = pOpenState->networkOpenInfo.FileAttributes; ntStatus = WireNTTimeToSMBUTime( pOpenState->networkOpenInfo.LastWriteTime, &pResponseHeader->ulLastWriteTime); BAIL_ON_NT_STATUS(ntStatus); pResponseHeader->ulDataSize = SMB_MIN( UINT32_MAX, pOpenState->networkOpenInfo.EndOfFile); if (SrvTreeIsNamedPipe(pOpenState->pTree)) { ntStatus = SrvMarshallPipeInfo( pOpenState->pFilePipeInfo, pOpenState->pFilePipeLocalInfo, &pResponseHeader->usDeviceState); BAIL_ON_NT_STATUS(ntStatus); pResponseHeader->usFileType = (USHORT)pOpenState->filePipeInfo.ReadMode; } else { pResponseHeader->usFileType = 0; pResponseHeader->usDeviceState = 0; } pResponseHeader->usReserved = 0; pResponseHeader->usByteCount = 0; pSmbResponse->ulMessageSize = ulTotalBytesUsed; cleanup: return ntStatus; error: if (ulTotalBytesUsed) { pSmbResponse->pHeader = NULL; pSmbResponse->pAndXHeader = NULL; memset(pSmbResponse->pBuffer, 0, ulTotalBytesUsed); } pSmbResponse->ulMessageSize = 0; goto cleanup; }
static NTSTATUS SrvRequestOpenXOplocks( PSRV_EXEC_CONTEXT pExecContext ) { NTSTATUS ntStatus = STATUS_SUCCESS; SRV_OPLOCK_INFO batchOplockChain[] = { { IO_OPLOCK_REQUEST_OPLOCK_BATCH, SMB_OPLOCK_LEVEL_BATCH }, { SMB_OPLOCK_LEVEL_NONE, SMB_OPLOCK_LEVEL_NONE } }; SRV_OPLOCK_INFO exclOplockChain[] = { { IO_OPLOCK_REQUEST_OPLOCK_LEVEL_1, SMB_OPLOCK_LEVEL_I }, { SMB_OPLOCK_LEVEL_NONE, SMB_OPLOCK_LEVEL_NONE } }; SRV_OPLOCK_INFO noOplockChain[] = { { SMB_OPLOCK_LEVEL_NONE, SMB_OPLOCK_LEVEL_NONE } }; PSRV_OPLOCK_INFO pOplockCursor = NULL; PSRV_PROTOCOL_EXEC_CONTEXT pCtxProtocol = pExecContext->pProtocolContext; PSRV_EXEC_CONTEXT_SMB_V1 pCtxSmb1 = pCtxProtocol->pSmb1Context; PSRV_OPEN_STATE_SMB_V1 pOpenState = NULL; PSRV_OPLOCK_STATE_SMB_V1 pOplockState = NULL; BOOLEAN bContinue = TRUE; pOpenState = (PSRV_OPEN_STATE_SMB_V1)pCtxSmb1->hState; if (SrvTreeIsNamedPipe(pOpenState->pTree) || (pOpenState->pNetworkOpenInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) || (!(pOpenState->pRequestHeader->usFlags & (SMB_OPLOCK_REQUEST_BATCH|SMB_OPLOCK_REQUEST_EXCLUSIVE)))) { pOplockCursor = &noOplockChain[0]; goto done; } ntStatus = SrvBuildOplockState( pExecContext->pConnection, pCtxSmb1->pSession, pCtxSmb1->pTree, pOpenState->pFile, &pOplockState); BAIL_ON_NT_STATUS(ntStatus); if (pOpenState->pRequestHeader->usFlags & SMB_OPLOCK_REQUEST_BATCH) { pOplockCursor = &batchOplockChain[0]; } else if (pOpenState->pRequestHeader->usFlags & SMB_OPLOCK_REQUEST_EXCLUSIVE) { pOplockCursor = &exclOplockChain[0]; } else { /* We should never get here, since we don't want to uselessly * allocate an OplockState; this is handled at the top of the * function. */ LWIO_ASSERT(0); } while (bContinue && (pOplockCursor->oplockRequest != SMB_OPLOCK_LEVEL_NONE)) { pOplockState->oplockBuffer_in.OplockRequestType = pOplockCursor->oplockRequest; SrvPrepareOplockStateAsync(pOplockState); ntStatus = IoFsControlFile( pOpenState->pFile->hFile, pOplockState->pAcb, &pOplockState->ioStatusBlock, IO_FSCTL_OPLOCK_REQUEST, &pOplockState->oplockBuffer_in, sizeof(pOplockState->oplockBuffer_in), &pOplockState->oplockBuffer_out, sizeof(pOplockState->oplockBuffer_out)); switch (ntStatus) { case STATUS_OPLOCK_NOT_GRANTED: SrvReleaseOplockStateAsync(pOplockState); // completed sync pOplockCursor++; break; case STATUS_PENDING: InterlockedIncrement(&pOplockState->refCount); ntStatus = SrvFileSetOplockState( pOpenState->pFile, pOplockState, &SrvCancelOplockStateHandle, &SrvReleaseOplockStateHandle); if (ntStatus != STATUS_SUCCESS) { InterlockedDecrement(&pOplockState->refCount); } BAIL_ON_NT_STATUS(ntStatus); SrvFileSetOplockLevel( pOpenState->pFile, pOplockCursor->oplockLevel); ntStatus = STATUS_SUCCESS; bContinue = FALSE; break; default: SrvReleaseOplockStateAsync(pOplockState); // completed sync BAIL_ON_NT_STATUS(ntStatus); break; } } done: pOpenState->ucOplockLevel = pOplockCursor->oplockLevel; cleanup: if (pOplockState) { SrvReleaseOplockState(pOplockState); } return ntStatus; error: goto cleanup; }
static NTSTATUS SrvQueryFileOpenInformation( PSRV_EXEC_CONTEXT pExecContext ) { NTSTATUS ntStatus = 0; PSRV_PROTOCOL_EXEC_CONTEXT pCtxProtocol = pExecContext->pProtocolContext; PSRV_EXEC_CONTEXT_SMB_V1 pCtxSmb1 = pCtxProtocol->pSmb1Context; PSRV_OPEN_STATE_SMB_V1 pOpenState = NULL; pOpenState = (PSRV_OPEN_STATE_SMB_V1)pCtxSmb1->hState; if (!IoRtlEcpListIsAcknowledged( pOpenState->pEcpList, SRV_ECP_TYPE_NET_OPEN_INFO)) { if (!pOpenState->pNetworkOpenInfo) { pOpenState->pNetworkOpenInfo = &pOpenState->networkOpenInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pNetworkOpenInfo, sizeof(pOpenState->networkOpenInfo), FileNetworkOpenInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } } else { pOpenState->pNetworkOpenInfo = &pOpenState->networkOpenInfo; } if (!(pOpenState->pNetworkOpenInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { SrvFileBlockIdleTimeout(pOpenState->pFile); } else { SrvFileUnblockIdleTimeout(pOpenState->pFile); } if (SrvTreeIsNamedPipe(pOpenState->pTree)) { if (!IoRtlEcpListIsAcknowledged( pOpenState->pEcpList, SRV_ECP_TYPE_PIPE_INFO)) { if (!pOpenState->pFilePipeInfo) { pOpenState->pFilePipeInfo = &pOpenState->filePipeInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pFilePipeInfo, sizeof(pOpenState->filePipeInfo), FilePipeInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } } else { pOpenState->pFilePipeInfo = &pOpenState->filePipeInfo; } if (!IoRtlEcpListIsAcknowledged( pOpenState->pEcpList, SRV_ECP_TYPE_PIPE_LOCAL_INFO)) { if (!pOpenState->pFilePipeLocalInfo) { pOpenState->pFilePipeLocalInfo = &pOpenState->filePipeLocalInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pFilePipeLocalInfo, sizeof(pOpenState->filePipeLocalInfo), FilePipeLocalInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } } else { pOpenState->pFilePipeLocalInfo = &pOpenState->filePipeLocalInfo; } } cleanup: return ntStatus; error: goto cleanup; }
static NTSTATUS SrvBuildOpenState( PSRV_EXEC_CONTEXT pExecContext, POPEN_REQUEST_HEADER pRequestHeader, PWSTR pwszFilename, PSRV_OPEN_STATE_SMB_V1* ppOpenState ) { NTSTATUS ntStatus = STATUS_SUCCESS; PLWIO_SRV_CONNECTION pConnection = pExecContext->pConnection; PSRV_PROTOCOL_EXEC_CONTEXT pCtxProtocol = pExecContext->pProtocolContext; PSRV_EXEC_CONTEXT_SMB_V1 pCtxSmb1 = pCtxProtocol->pSmb1Context; PSRV_OPEN_STATE_SMB_V1 pOpenState = NULL; ntStatus = SrvAllocateMemory( sizeof(SRV_OPEN_STATE_SMB_V1), (PVOID*)&pOpenState); BAIL_ON_NT_STATUS(ntStatus); pOpenState->refCount = 1; pthread_mutex_init(&pOpenState->mutex, NULL); pOpenState->pMutex = &pOpenState->mutex; pOpenState->stage = SRV_OPEN_STAGE_SMB_V1_INITIAL; // TODO: Handle root fids ntStatus = SrvAllocateMemory( sizeof(IO_FILE_NAME), (PVOID*)&pOpenState->pFilename); BAIL_ON_NT_STATUS(ntStatus); pOpenState->pTree = SrvTreeAcquire(pCtxSmb1->pTree); ntStatus = SrvBuildTreeRelativePath( pCtxSmb1->pTree, pwszFilename, pOpenState->pFilename); BAIL_ON_NT_STATUS(ntStatus); pOpenState->pwszFilename = pwszFilename; ntStatus = IoRtlEcpListAllocate(&pOpenState->pEcpList); BAIL_ON_NT_STATUS(ntStatus); ntStatus = IoRtlEcpListInsert(pOpenState->pEcpList, SRV_ECP_TYPE_NET_OPEN_INFO, &pOpenState->networkOpenInfo, sizeof(pOpenState->networkOpenInfo), NULL); BAIL_ON_NT_STATUS(ntStatus); /* For named pipes, we need to pipe some extra data into the npfs driver: * - Session key * - Client principal name * - Client address */ if (SrvTreeIsNamedPipe(pCtxSmb1->pTree)) { ntStatus = SrvConnectionGetNamedPipeSessionKey( pConnection, pOpenState->pEcpList); BAIL_ON_NT_STATUS(ntStatus); ntStatus = SrvConnectionGetNamedPipeClientAddress( pConnection, pOpenState->pEcpList); BAIL_ON_NT_STATUS(ntStatus); ntStatus = IoRtlEcpListInsert(pOpenState->pEcpList, SRV_ECP_TYPE_PIPE_INFO, &pOpenState->filePipeInfo, sizeof(pOpenState->filePipeInfo), NULL); BAIL_ON_NT_STATUS(ntStatus); ntStatus = IoRtlEcpListInsert(pOpenState->pEcpList, SRV_ECP_TYPE_PIPE_LOCAL_INFO, &pOpenState->filePipeLocalInfo, sizeof(pOpenState->filePipeLocalInfo), NULL); BAIL_ON_NT_STATUS(ntStatus); } pOpenState->usCreateOptions = FILE_NON_DIRECTORY_FILE; pOpenState->usShareAccess = (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE); switch (pRequestHeader->usDesiredAccess & 0x70) { case 0x00: /* compatibility mode */ ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); break; case 0x10: /* deny read/write/execute (exclusive) */ pOpenState->usShareAccess &= (USHORT)~FILE_SHARE_READ; pOpenState->usShareAccess &= (USHORT)~FILE_SHARE_WRITE; break; case 0x20: /* deny write */ pOpenState->usShareAccess &= (USHORT)~FILE_SHARE_WRITE; break; case 0x30: /* deny read/execute */ pOpenState->usShareAccess &= (USHORT)~FILE_SHARE_READ; break; case 0x40: /* deny none */ break; } /* desired access mask */ switch (pRequestHeader->usDesiredAccess & 0x7) { case 0x00: pOpenState->ulDesiredAccessMask = GENERIC_READ; break; case 0x01: pOpenState->ulDesiredAccessMask = GENERIC_WRITE; break; case 0x02: pOpenState->ulDesiredAccessMask = GENERIC_READ | GENERIC_WRITE; break; case 0x03: pOpenState->ulDesiredAccessMask = (GENERIC_READ | GENERIC_EXECUTE); break; default: ntStatus = STATUS_INVALID_PARAMETER; BAIL_ON_NT_STATUS(ntStatus); break; } /* action to take if the file exists */ switch (pRequestHeader->usOpenFunction) { case 0x0000: /* Weird EXECUTE -> OPEN_IF semantics */ if ((pOpenState->ulDesiredAccessMask & GENERIC_EXECUTE) == 0) { ntStatus = STATUS_INVALID_DISPOSITION; BAIL_ON_NT_STATUS(ntStatus); } pOpenState->ulCreateDisposition = FILE_OPEN_IF; break; case 0x0001: /* Open file */ pOpenState->ulCreateDisposition = FILE_OPEN; break; case 0x0002: /* truncate file */ pOpenState->ulCreateDisposition = FILE_OVERWRITE; break; case 0x0010: /* create new file */ pOpenState->ulCreateDisposition = FILE_CREATE; break; case 0x0011: /* open or create file */ pOpenState->ulCreateDisposition = FILE_OPEN_IF; break; case 0x0012: /* create or truncate */ pOpenState->ulCreateDisposition = FILE_OVERWRITE_IF; break; default: ntStatus = STATUS_INVALID_DISPOSITION; BAIL_ON_NT_STATUS(ntStatus); break; } pOpenState->pRequestHeader = pRequestHeader; *ppOpenState = pOpenState; cleanup: return ntStatus; error: *ppOpenState = NULL; if (pOpenState) { SrvFreeOpenState(pOpenState); } goto cleanup; }
static NTSTATUS SrvQueryFileOpenInformation( PSRV_EXEC_CONTEXT pExecContext ) { NTSTATUS ntStatus = 0; PSRV_PROTOCOL_EXEC_CONTEXT pCtxProtocol = pExecContext->pProtocolContext; PSRV_EXEC_CONTEXT_SMB_V1 pCtxSmb1 = pCtxProtocol->pSmb1Context; PSRV_OPEN_STATE_SMB_V1 pOpenState = NULL; pOpenState = (PSRV_OPEN_STATE_SMB_V1)pCtxSmb1->hState; if (!pOpenState->pFileBasicInfo) { pOpenState->pFileBasicInfo = &pOpenState->fileBasicInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pFileBasicInfo, sizeof(pOpenState->fileBasicInfo), FileBasicInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } if (!pOpenState->pFileStdInfo) { pOpenState->pFileStdInfo = &pOpenState->fileStdInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pFileStdInfo, sizeof(pOpenState->fileStdInfo), FileStandardInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } if (SrvTreeIsNamedPipe(pOpenState->pTree)) { if (!pOpenState->pFilePipeInfo) { pOpenState->pFilePipeInfo = &pOpenState->filePipeInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pFilePipeInfo, sizeof(pOpenState->filePipeInfo), FilePipeInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } if (!pOpenState->pFilePipeLocalInfo) { pOpenState->pFilePipeLocalInfo = &pOpenState->filePipeLocalInfo; SrvPrepareOpenStateAsync(pOpenState, pExecContext); ntStatus = IoQueryInformationFile( pOpenState->pFile->hFile, pOpenState->pAcb, &pOpenState->ioStatusBlock, pOpenState->pFilePipeLocalInfo, sizeof(pOpenState->filePipeLocalInfo), FilePipeLocalInformation); BAIL_ON_NT_STATUS(ntStatus); SrvReleaseOpenStateAsync(pOpenState); // completed synchronously } } cleanup: return ntStatus; error: goto cleanup; }