static VOID RdrNegotiateGssContextWorkItem( PVOID pParam ) { NTSTATUS status = STATUS_SUCCESS; PRDR_OP_CONTEXT pContext = pParam; PRDR_SESSION pSession = pContext->State.TreeConnect.pSession; PRDR_SOCKET pSocket = pSession->pSocket; PSMB_PACKET pPacket = pContext->State.TreeConnect.pPacket; PWSTR pwszNativeOS = NULL; PWSTR pwszNativeLanman = NULL; PWSTR pwszNativeDomain = NULL; PBYTE pInBlob = NULL; DWORD dwInBlobLength = 0; PBYTE pOutBlob = NULL; DWORD dwOutBlobLength = 0; PSESSION_SETUP_RESPONSE_HEADER_WC_4 pResponseHeader = NULL; BOOLEAN bSessionLocked = FALSE; if (pPacket) { status = UnmarshallSessionSetupResponse_WC_4( pPacket->pParams, pPacket->bufferLen - pPacket->bufferUsed, 0, &pResponseHeader, &pInBlob, &pwszNativeOS, &pwszNativeLanman, &pwszNativeDomain); BAIL_ON_NT_STATUS(status); dwInBlobLength = pResponseHeader->securityBlobLength; } else { pInBlob = pSession->pSocket->pSecurityBlob; dwInBlobLength = pSession->pSocket->securityBlobLen; } if (pContext->State.TreeConnect.pszCachePath) { status = SMBKrb5SetDefaultCachePath( pContext->State.TreeConnect.pszCachePath, NULL); BAIL_ON_NT_STATUS(status); } if (!pContext->State.TreeConnect.hGssContext) { status = SMBGSSContextBuild( pSocket->pwszCanonicalName, pContext->State.TreeConnect.pCreds, &pContext->State.TreeConnect.hGssContext); BAIL_ON_NT_STATUS(status); } status = SMBGSSContextNegotiate( pContext->State.TreeConnect.hGssContext, pInBlob, dwInBlobLength, &pOutBlob, &dwOutBlobLength); BAIL_ON_NT_STATUS(status); if (!SMBGSSContextNegotiateComplete(pContext->State.TreeConnect.hGssContext)) { pContext->Continue = RdrProcessSessionSetupResponse; status = RdrTransceiveSessionSetup( pContext, pSession, pOutBlob, dwOutBlobLength); BAIL_ON_NT_STATUS(status); } else { LWIO_LOCK_MUTEX(bSessionLocked, &pSession->mutex); if (pContext->Packet.haveSignature && (!memcmp(pPacket->pSMBHeader->extra.securitySignature, pContext->Packet.pSMBHeader->extra.securitySignature, sizeof(pContext->Packet.pSMBHeader->extra.securitySignature)))) { LWIO_LOG_WARNING("Server is exhibiting signing bug; ignoring signatures from server"); RdrSocketSetIgnoreServerSignatures(pSocket, TRUE); } status = SMBGSSContextGetSessionKey( pContext->State.TreeConnect.hGssContext, &pSession->pSessionKey, &pSession->dwSessionKeyLength); BAIL_ON_NT_STATUS(status); if (!pSocket->pSessionKey && pSession->pSessionKey && !(pContext->State.TreeConnect.pCreds->type == IO_CREDS_TYPE_PLAIN && pContext->State.TreeConnect.pCreds->payload.plain.pwszUsername[0] == '\0')) { status = LwIoAllocateMemory( pSession->dwSessionKeyLength, OUT_PPVOID(&pSocket->pSessionKey)); BAIL_ON_NT_STATUS(status); memcpy(pSocket->pSessionKey, pSession->pSessionKey, pSession->dwSessionKeyLength); pSocket->dwSessionKeyLength = pSession->dwSessionKeyLength; RdrSocketBeginSequence(pSocket); } status = RdrSocketAddSessionByUID(pSocket, pSession); BAIL_ON_NT_STATUS(status); pSession->state = RDR_SESSION_STATE_READY; RdrNotifyContextList( &pSession->StateWaiters, bSessionLocked, &pSession->mutex, status, pSession); LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RdrSessionSetupComplete(pContext, status, pSession); status = STATUS_PENDING; BAIL_ON_NT_STATUS(status); } cleanup: LWIO_UNLOCK_MUTEX(bSessionLocked, &pSession->mutex); RTL_FREE(&pOutBlob); if (status != STATUS_PENDING) { if (pContext->State.TreeConnect.hGssContext) { SMBGSSContextFree(pContext->State.TreeConnect.hGssContext); } RdrSessionInvalidate(pSession, status); RdrSessionRelease(pSession); RdrSessionSetupComplete(pContext, status, NULL); } return; error: goto cleanup; }
DWORD SMBGSSContextBuild( PCWSTR pwszServerName, PIO_CREDS pCreds, PHANDLE phSMBGSSContext ) { DWORD dwError = 0; DWORD dwMajorStatus = 0; DWORD dwMinorStatus = 0; PSMB_GSS_SEC_CONTEXT pContext = NULL; PSTR pszTargetName = NULL; PSTR pszServerName = NULL; PSTR pszUsername = NULL; PSTR pszDomain = NULL; PSTR pszPassword = NULL; gss_buffer_desc usernameBuffer = {0}; gss_buffer_desc inputNameBuffer = {0}; gss_buffer_desc authDataBuffer = {0}; gss_name_t pUsername = NULL; gss_OID_set_desc desiredMechs; gss_OID_set actualMechs; OM_uint32 timeRec = 0; SEC_WINNT_AUTH_IDENTITY authData; static gss_OID_desc gssCredOptionPasswordOidDesc = { .length = GSS_CRED_OPT_PW_LEN, .elements = GSS_CRED_OPT_PW }; static gss_OID_desc gssNtlmOidDesc = { .length = GSS_MECH_NTLM_LEN, .elements = GSS_MECH_NTLM }; size_t sCopyServerChars = 0; dwError = LwRtlCStringAllocateFromWC16String(&pszServerName, pwszServerName); BAIL_ON_LWIO_ERROR(dwError); LWIO_LOG_DEBUG("Build GSS Context for server [%s]", LWIO_SAFE_LOG_STRING(pszServerName)); dwError = LwIoAllocateMemory( sizeof(SMB_GSS_SEC_CONTEXT), (PVOID*)&pContext); BAIL_ON_LWIO_ERROR(dwError); pContext->state = SMB_GSS_SEC_CONTEXT_STATE_INITIAL; if (pCreds) { switch (pCreds->type) { case IO_CREDS_TYPE_KRB5_CCACHE: dwError = STATUS_ACCESS_DENIED; BAIL_ON_LWIO_ERROR(dwError); break; case IO_CREDS_TYPE_KRB5_TGT: sCopyServerChars = strlen(pszServerName); if (sCopyServerChars > 0 && pszServerName[sCopyServerChars - 1] == '.') { // Strip the trailing dot sCopyServerChars --; } if (sCopyServerChars > INT_MAX) { dwError = STATUS_INTEGER_OVERFLOW; BAIL_ON_LWIO_ERROR(dwError); } dwError = SMBAllocateStringPrintf( &pszTargetName, "cifs/%.*s@", (int)sCopyServerChars, pszServerName); BAIL_ON_LWIO_ERROR(dwError); inputNameBuffer.value = pszTargetName; inputNameBuffer.length = strlen(pszTargetName) + 1; dwMajorStatus = gss_import_name( (OM_uint32 *)&dwMinorStatus, &inputNameBuffer, (gss_OID) gss_nt_krb5_name, &pContext->target_name); smb_display_status("gss_import_name", dwMajorStatus, dwMinorStatus); BAIL_ON_SEC_ERROR(dwMajorStatus); dwError = LwRtlCStringAllocateFromWC16String( &pszUsername, pCreds->payload.krb5Tgt.pwszClientPrincipal); BAIL_ON_NT_STATUS(dwError); usernameBuffer.value = pszUsername; usernameBuffer.length = strlen(pszUsername) + 1; dwMajorStatus = gss_import_name( (OM_uint32 *)&dwMinorStatus, &usernameBuffer, GSS_C_NT_USER_NAME, &pUsername); BAIL_ON_SEC_ERROR(dwMajorStatus); desiredMechs.count = 1; desiredMechs.elements = (gss_OID) gss_mech_krb5; dwMajorStatus = gss_acquire_cred( (OM_uint32 *)&dwMinorStatus, pUsername, 0, &desiredMechs, GSS_C_INITIATE, &pContext->credHandle, &actualMechs, &timeRec); BAIL_ON_SEC_ERROR(dwMajorStatus); break; case IO_CREDS_TYPE_PLAIN: inputNameBuffer.value = (void*) "unset"; inputNameBuffer.length = strlen("unset"); dwMajorStatus = gss_import_name( (OM_uint32 *)&dwMinorStatus, &inputNameBuffer, (gss_OID) gss_nt_krb5_name, &pContext->target_name); smb_display_status("gss_import_name", dwMajorStatus, dwMinorStatus); BAIL_ON_SEC_ERROR(dwMajorStatus); if (pCreds->payload.plain.pwszUsername) { dwError = LwRtlCStringAllocateFromWC16String(&pszUsername, pCreds->payload.plain.pwszUsername); BAIL_ON_LWIO_ERROR(dwError); usernameBuffer.value = pszUsername; usernameBuffer.length = strlen(pszUsername); // If "" is passed in, that means to use anonymous // authentication. gss_import_name fails on "" though if (usernameBuffer.length) { dwMajorStatus = gss_import_name( (OM_uint32 *)&dwMinorStatus, &usernameBuffer, GSS_C_NT_USER_NAME, &pUsername); BAIL_ON_SEC_ERROR(dwMajorStatus); } } desiredMechs.count = 1; desiredMechs.elements = (gss_OID) &gssNtlmOidDesc; dwMajorStatus = gss_acquire_cred( (OM_uint32 *)&dwMinorStatus, pUsername, 0, &desiredMechs, GSS_C_INITIATE, &pContext->credHandle, &actualMechs, &timeRec); BAIL_ON_SEC_ERROR(dwMajorStatus); if (pCreds->payload.plain.pwszUsername && pCreds->payload.plain.pwszPassword && pCreds->payload.plain.pwszDomain) { dwError = LwRtlCStringAllocateFromWC16String(&pszDomain, pCreds->payload.plain.pwszDomain); BAIL_ON_LWIO_ERROR(dwError); dwError = LwRtlCStringAllocateFromWC16String(&pszPassword, pCreds->payload.plain.pwszPassword); BAIL_ON_LWIO_ERROR(dwError); authData.User = pszUsername; authData.UserLength = strlen(pszUsername); authData.Domain = pszDomain; authData.DomainLength = strlen(pszDomain); authData.Password = pszPassword; authData.PasswordLength = strlen(pszPassword); authData.Flags = 0; authDataBuffer.value = &authData; authDataBuffer.length = sizeof(authData); dwMajorStatus = gssspi_set_cred_option( (OM_uint32 *)&dwMinorStatus, pContext->credHandle, (gss_OID) &gssCredOptionPasswordOidDesc, &authDataBuffer); BAIL_ON_SEC_ERROR(dwMajorStatus); } break; } } dwError = LwIoAllocateMemory( sizeof(CtxtHandle), (PVOID*)&pContext->pGSSContext); BAIL_ON_LWIO_ERROR(dwError); *pContext->pGSSContext = GSS_C_NO_CONTEXT; *phSMBGSSContext = (HANDLE)pContext; cleanup: if (pUsername != NULL) { gss_release_name((OM_uint32 *)&dwMinorStatus, &pUsername); } LWIO_SAFE_FREE_STRING(pszTargetName); LWIO_SAFE_FREE_STRING(pszServerName); LWIO_SAFE_FREE_STRING(pszUsername); LWIO_SAFE_FREE_STRING(pszDomain); LWIO_SAFE_FREE_STRING(pszPassword); return dwError; sec_error: dwError = LWIO_ERROR_GSS; error: *phSMBGSSContext = NULL; if (pContext) { SMBGSSContextFree(pContext); } goto cleanup; }