static BOOLEAN RdrTreeConnect2Complete( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_TREE2 pTree = pParam; BAIL_ON_NT_STATUS(status); cleanup: if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, pTree); RdrFreeTreeConnectContext(pContext); } return FALSE; error: if (status != STATUS_PENDING && pTree) { RdrTree2Release(pTree); pTree = NULL; } goto cleanup; }
BOOLEAN RdrProcessNegotiateResponse2( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_SOCKET pSocket = pContext->State.TreeConnect.pSocket; PSMB_PACKET pPacket = pParam; BOOLEAN bFreeContext = FALSE; BOOLEAN bSocketLocked = FALSE; PRDR_SMB2_NEGOTIATE_RESPONSE_HEADER pHeader = NULL; PBYTE pNegHint = NULL; ULONG ulNegHintLength = 0; BAIL_ON_NT_STATUS(status); status = pPacket->pSMB2Header->error; BAIL_ON_NT_STATUS(status); LWIO_LOCK_MUTEX(bSocketLocked, &pSocket->mutex); status = RdrSmb2DecodeNegotiateResponse( pPacket, &pHeader, &pNegHint, &ulNegHintLength); BAIL_ON_NT_STATUS(status); pSocket->ulMaxTransactSize = pHeader->ulMaxTransactionSize; pSocket->ulMaxReadSize = pHeader->ulMaxReadSize; pSocket->ulMaxWriteSize = pHeader->ulMaxWriteSize; pSocket->capabilities = pHeader->ulCapabilities; pSocket->ucSecurityMode = pHeader->ucSecurityMode; pSocket->securityBlobLen = ulNegHintLength; status = LwIoAllocateMemory( pSocket->securityBlobLen, (PVOID *) &pSocket->pSecurityBlob); BAIL_ON_NT_STATUS(status); memcpy(pSocket->pSecurityBlob, pNegHint, pSocket->securityBlobLen); status = RdrSocketSetProtocol(pSocket, SMB_PROTOCOL_VERSION_2); BAIL_ON_NT_STATUS(status); pSocket->state = RDR_SOCKET_STATE_READY; RdrNotifyContextList( &pSocket->StateWaiters, bSocketLocked, &pSocket->mutex, STATUS_SUCCESS, pSocket); LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); RdrNegotiateComplete2(pContext, STATUS_SUCCESS, pSocket); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); cleanup: LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, NULL); bFreeContext = TRUE; } if (bFreeContext) { RdrFreeTreeConnectContext(pContext); } RdrFreePacket(pPacket); return FALSE; error: if (status != STATUS_PENDING && pSocket) { LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); RdrSocketInvalidate(pSocket, status); RdrSocketRelease(pSocket); } goto cleanup; }
static BOOLEAN RdrSessionSetupComplete2( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_SESSION2 pSession = pParam; PRDR_TREE2 pTree = NULL; BOOLEAN bTreeLocked = FALSE; BOOLEAN bFreeContext = FALSE; BAIL_ON_NT_STATUS(status); status = RdrTree2FindOrCreate( &pSession, pContext->State.TreeConnect.pwszSharename, &pTree); BAIL_ON_NT_STATUS(status); pContext->State.TreeConnect.pTree2 = pTree; LWIO_LOCK_MUTEX(bTreeLocked, &pTree->mutex); switch (pTree->state) { case RDR_TREE_STATE_NOT_READY: pTree->state = RDR_TREE_STATE_INITIALIZING; pContext->Continue = RdrFinishTreeConnect2; status = RdrTransceiveTreeConnect2(pContext, pTree, pTree->pwszPath); BAIL_ON_NT_STATUS(status); break; case RDR_TREE_STATE_INITIALIZING: pContext->Continue = RdrTreeConnect2Complete; LwListInsertTail(&pTree->StateWaiters, &pContext->Link); bFreeContext = TRUE; status = STATUS_PENDING; break; case RDR_TREE_STATE_READY: RdrTreeConnect2Complete(pContext, status, pTree); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_TREE_STATE_ERROR: status = pTree->error; BAIL_ON_NT_STATUS(status); break; } cleanup: LWIO_UNLOCK_MUTEX(bTreeLocked, &pTree->mutex); if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, NULL); bFreeContext = TRUE; } if (bFreeContext) { RdrFreeTreeConnectContext(pContext); } return FALSE; error: if (status != STATUS_PENDING && pTree) { LWIO_UNLOCK_MUTEX(bTreeLocked, &pTree->mutex); RdrTree2Invalidate(pTree, status); RdrTree2Release(pTree); } if (status != STATUS_PENDING && pSession) { RdrSession2Release(pSession); } goto cleanup; }
static BOOLEAN RdrProcessSessionSetupResponse2( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_SESSION2 pSession = pContext->State.TreeConnect.pSession2; PSMB_PACKET pPacket = pParam; BOOLEAN bSessionLocked = FALSE; BOOLEAN bFreeContext = FALSE; BAIL_ON_NT_STATUS(status); LWIO_LOCK_MUTEX(bSessionLocked, &pSession->mutex); if (pPacket) { status = pPacket->pSMB2Header->error; if (status == STATUS_MORE_PROCESSING_REQUIRED) { status = STATUS_SUCCESS; } BAIL_ON_NT_STATUS(status); pSession->ullSessionId = pPacket->pSMB2Header->ullSessionId; } /* Save the packet on the context for RdrNegotiateGssContextWorkItem2 */ pContext->State.TreeConnect.pPacket = pPacket; pPacket = NULL; /* Dispatch a work item to negotiate the GSS context in a separate thread. Because GSS-API could potentially block in a network call (KRB5) or an IPC call (NTLM), calling into it directly from the socket task could cause a deadlock. */ status = LwRtlQueueWorkItem( gRdrRuntime.pThreadPool, RdrNegotiateGssContextWorkItem2, pContext, 0); BAIL_ON_NT_STATUS(status); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); cleanup: LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, NULL); bFreeContext = TRUE; } if (bFreeContext) { RdrFreeTreeConnectContext(pContext); } RdrFreePacket(pPacket); return FALSE; error: if (status != STATUS_PENDING && pSession) { LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RdrSession2Invalidate(pSession, status); RdrSession2Release(pSession); } goto cleanup; }
BOOLEAN RdrNegotiateComplete2( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_SOCKET pSocket = pParam; PRDR_SESSION2 pSession = NULL; BOOLEAN bSessionLocked = FALSE; BOOLEAN bFreeContext = FALSE; PIO_CREDS pCreds = pContext->State.TreeConnect.pCreds; BAIL_ON_NT_STATUS(status); if (pContext->State.TreeConnect.bStopOnDfs && pSocket->capabilities & RDR_SMB2_CAP_DFS) { status = STATUS_DFS_EXIT_PATH_FOUND; BAIL_ON_NT_STATUS(status); } status = RdrSession2FindOrCreate( &pSocket, pContext->State.TreeConnect.pCreds, pContext->State.TreeConnect.Uid, &pSession); BAIL_ON_NT_STATUS(status); pContext->State.TreeConnect.pSession2 = pSession; LWIO_LOCK_MUTEX(bSessionLocked, &pSession->mutex); switch (pSession->state) { case RDR_SESSION_STATE_NOT_READY: pSession->state = RDR_SESSION_STATE_INITIALIZING; switch (pCreds->type) { case IO_CREDS_TYPE_KRB5_TGT: status = SMBCredTokenToKrb5CredCache( pCreds, &pContext->State.TreeConnect.pszCachePath); BAIL_ON_NT_STATUS(status); break; case IO_CREDS_TYPE_PLAIN: break; default: status = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(status); } LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RdrProcessSessionSetupResponse2(pContext, STATUS_SUCCESS, NULL); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SESSION_STATE_INITIALIZING: pContext->Continue = RdrSessionSetupComplete2; LwListInsertTail(&pSession->StateWaiters, &pContext->Link); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SESSION_STATE_READY: LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RdrSessionSetupComplete2(pContext, status, pSession); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SESSION_STATE_ERROR: status = pSession->error; BAIL_ON_NT_STATUS(status); break; } cleanup: LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, NULL); bFreeContext = TRUE; } if (bFreeContext) { RdrFreeTreeConnectContext(pContext); } return FALSE; error: if (status != STATUS_PENDING && pSession) { LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); if (status != STATUS_DFS_EXIT_PATH_FOUND) { RdrSession2Invalidate(pSession, status); } RdrSession2Release(pSession); } if (status != STATUS_PENDING && pSocket) { if (status != STATUS_DFS_EXIT_PATH_FOUND) { RdrSocketInvalidate(pSocket, status); } RdrSocketRelease(pSocket); } goto cleanup; }
static BOOLEAN RdrProcessNegotiateResponse( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_SOCKET pSocket = pContext->State.TreeConnect.pSocket; PSMB_PACKET pPacket = pParam; BOOLEAN bSocketLocked = FALSE; BOOLEAN bFreeContext = FALSE; PBYTE pGUID = NULL; PBYTE pSecurityBlob = NULL; DWORD securityBlobLen = 0; NEGOTIATE_RESPONSE_HEADER* pHeader = NULL; BAIL_ON_NT_STATUS(status); /* As a special case, it is possible to receive an SMB2 negotiate response * from an SMB1 negotiate request. */ if (pPacket->protocolVer == SMB_PROTOCOL_VERSION_2) { /* Short-circuit to SMB2 code path in connect2.c */ return RdrProcessNegotiateResponse2(pContext, status, pParam); } LWIO_LOCK_MUTEX(bSocketLocked, &pSocket->mutex); status = pPacket->pSMBHeader->error; BAIL_ON_NT_STATUS(status); status = UnmarshallNegotiateResponse( pPacket->pParams, pPacket->bufferUsed - (pPacket->pParams - pPacket->pRawBuffer), &pHeader, &pGUID, &pSecurityBlob, &securityBlobLen); BAIL_ON_NT_STATUS(status); // byte order conversions SMB_LTOH16_INPLACE(pHeader->dialectIndex); SMB_LTOH8_INPLACE(pHeader->securityMode); SMB_LTOH16_INPLACE(pHeader->maxMpxCount); SMB_LTOH16_INPLACE(pHeader->maxNumberVcs); SMB_LTOH32_INPLACE(pHeader->maxBufferSize); SMB_LTOH32_INPLACE(pHeader->maxRawSize); SMB_LTOH32_INPLACE(pHeader->sessionKey); SMB_LTOH32_INPLACE(pHeader->capabilities); SMB_LTOH32_INPLACE(pHeader->systemTimeLow); SMB_LTOH32_INPLACE(pHeader->systemTimeHigh); SMB_LTOH16_INPLACE(pHeader->serverTimeZone); SMB_LTOH8_INPLACE(pHeader->encryptionKeyLength); SMB_LTOH16_INPLACE(pHeader->byteCount); pSocket->ulMaxTransactSize = pHeader->maxBufferSize; pSocket->maxRawSize = pHeader->maxRawSize; pSocket->sessionKey = pHeader->sessionKey; pSocket->capabilities = pHeader->capabilities; pSocket->ucSecurityMode = pHeader->securityMode; pSocket->usMaxSlots = pHeader->maxMpxCount; pSocket->securityBlobLen = securityBlobLen; status = LwIoAllocateMemory( pSocket->securityBlobLen, (PVOID *) &pSocket->pSecurityBlob); BAIL_ON_NT_STATUS(status); memcpy(pSocket->pSecurityBlob, pSecurityBlob, pSocket->securityBlobLen); status = RdrSocketSetProtocol(pSocket, SMB_PROTOCOL_VERSION_1); BAIL_ON_NT_STATUS(status); pSocket->state = RDR_SOCKET_STATE_READY; RdrNotifyContextList( &pSocket->StateWaiters, bSocketLocked, &pSocket->mutex, STATUS_SUCCESS, pSocket); LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); RdrNegotiateComplete(pContext, STATUS_SUCCESS, pSocket); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); cleanup: LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, NULL); bFreeContext = TRUE; } if (bFreeContext) { RdrFreeTreeConnectContext(pContext); } RdrFreePacket(pPacket); return FALSE; error: if (status != STATUS_PENDING) { LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); RdrSocketInvalidate(pSocket, status); RdrSocketRelease(pSocket); } goto cleanup; }
static BOOLEAN RdrNegotiateComplete( PRDR_OP_CONTEXT pContext, NTSTATUS status, PVOID pParam ) { PRDR_SOCKET pSocket = pParam; PRDR_SESSION pSession = NULL; BOOLEAN bSessionLocked = FALSE; BOOLEAN bFreeContext = FALSE; PIO_CREDS pCreds = pContext->State.TreeConnect.pCreds; BAIL_ON_NT_STATUS(status); /* Several op contexts could be queued with this function * as the continue routine before we transition to SMB2 mode, * so we need to hand off to the correct function in this case. * Subsequent attempts should go straight to connect2.c */ if (pSocket->version == SMB_PROTOCOL_VERSION_2) { /* Short circuit to SMB2 session setup logic in connect2.c */ return RdrNegotiateComplete2(pContext, status, pParam); } if (pContext->State.TreeConnect.bStopOnDfs && pSocket->capabilities & CAP_DFS) { /* Abort tree connect because we need to do DFS referral processing first */ status = STATUS_DFS_EXIT_PATH_FOUND; BAIL_ON_NT_STATUS(status); } status = RdrSessionFindOrCreate( &pSocket, pContext->State.TreeConnect.pCreds, pContext->State.TreeConnect.Uid, &pSession); BAIL_ON_NT_STATUS(status); pContext->State.TreeConnect.pSession = pSession; LWIO_LOCK_MUTEX(bSessionLocked, &pSession->mutex); switch (pSession->state) { case RDR_SESSION_STATE_NOT_READY: pSession->state = RDR_SESSION_STATE_INITIALIZING; switch (pCreds->type) { case IO_CREDS_TYPE_KRB5_TGT: status = SMBCredTokenToKrb5CredCache( pCreds, &pContext->State.TreeConnect.pszCachePath); BAIL_ON_NT_STATUS(status); break; case IO_CREDS_TYPE_PLAIN: break; default: status = STATUS_ACCESS_DENIED; BAIL_ON_NT_STATUS(status); } LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RdrProcessSessionSetupResponse(pContext, STATUS_SUCCESS, NULL); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SESSION_STATE_INITIALIZING: pContext->Continue = RdrSessionSetupComplete; LwListInsertTail(&pSession->StateWaiters, &pContext->Link); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SESSION_STATE_READY: LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RdrSessionSetupComplete(pContext, status, pSession); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SESSION_STATE_ERROR: status = pSession->error; BAIL_ON_NT_STATUS(status); break; } cleanup: LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); if (status != STATUS_PENDING) { RdrContinueContext(pContext->State.TreeConnect.pContinue, status, NULL); bFreeContext = TRUE; } if (bFreeContext) { RdrFreeTreeConnectContext(pContext); } return FALSE; error: if (status != STATUS_PENDING && pSession) { LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); if (status != STATUS_DFS_EXIT_PATH_FOUND) { RdrSessionInvalidate(pSession, status); } RdrSessionRelease(pSession); } if (status != STATUS_PENDING && pSocket) { if (status != STATUS_DFS_EXIT_PATH_FOUND) { RdrSocketInvalidate(pSocket, status); } RdrSocketRelease(pSocket); } goto cleanup; }
NTSTATUS RdrTreeConnect( PCWSTR pwszHostname, PCWSTR pwszSharename, PIO_CREDS pCreds, uid_t Uid, BOOLEAN bStopOnDfs, PRDR_OP_CONTEXT pContinue ) { NTSTATUS status = STATUS_SUCCESS; PRDR_OP_CONTEXT pContext = NULL; BOOLEAN bSocketLocked = FALSE; PRDR_SOCKET pSocket = NULL; status = RdrCreateContext(pContinue->pIrp, &pContext); BAIL_ON_NT_STATUS(status); LWIO_LOG_DEBUG("Tree connect context %p will continue %p\n", pContext, pContinue); pContext->State.TreeConnect.Uid = Uid; pContext->State.TreeConnect.bStopOnDfs = bStopOnDfs; pContext->State.TreeConnect.pContinue = pContinue; status = LwRtlWC16StringDuplicate( &pContext->State.TreeConnect.pwszSharename, pwszSharename); BAIL_ON_NT_STATUS(status); pContext->State.TreeConnect.pCreds = pCreds; status = RdrSocketFindOrCreate( pwszHostname, &pSocket); BAIL_ON_NT_STATUS(status); pContext->State.TreeConnect.pSocket = pSocket; LWIO_LOCK_MUTEX(bSocketLocked, &pSocket->mutex); switch (pSocket->state) { case RDR_SOCKET_STATE_NOT_READY: pSocket->state = RDR_SOCKET_STATE_CONNECTING; LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); /* Add extra reference to socket for work item */ RdrSocketRetain(pSocket); status = LwRtlQueueWorkItem( gRdrRuntime.pThreadPool, RdrSocketConnectWorkItem, pSocket, 0); if (status) { /* Nevermind */ RdrSocketRelease(pSocket); } BAIL_ON_NT_STATUS(status); pContext->Continue = RdrProcessNegotiateResponse; status = RdrTransceiveNegotiate(pContext, pSocket); BAIL_ON_NT_STATUS(status); break; case RDR_SOCKET_STATE_CONNECTING: case RDR_SOCKET_STATE_NEGOTIATING: pContext->Continue = RdrNegotiateComplete; LwListInsertTail(&pSocket->StateWaiters, &pContext->Link); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SOCKET_STATE_READY: LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); RdrNegotiateComplete(pContext, STATUS_SUCCESS, pSocket); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); break; case RDR_SOCKET_STATE_ERROR: status = pSocket->error; BAIL_ON_NT_STATUS(status); break; } cleanup: LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); if (status != STATUS_PENDING) { RdrFreeTreeConnectContext(pContext); } return status; error: if (status != STATUS_PENDING && pSocket) { LWIO_UNLOCK_MUTEX(bSocketLocked, &pSocket->mutex); RdrSocketInvalidate(pSocket, status); RdrSocketRelease(pSocket); } goto cleanup; }