static NTSTATUS SMBGssGetSessionKey( gss_ctx_id_t Context, PBYTE* ppSessionKey, PDWORD pdwSessionKeyLength ) { NTSTATUS status = STATUS_SUCCESS; PBYTE pSessionKey = NULL; DWORD dwSessionKeyLength = 0; OM_uint32 gssMajor = GSS_S_COMPLETE; OM_uint32 gssMinor = 0; gss_buffer_set_t sessionKey = NULL; gssMajor = gss_inquire_sec_context_by_oid( &gssMinor, Context, GSS_C_INQ_SSPI_SESSION_KEY, &sessionKey); if (gssMajor != GSS_S_COMPLETE) { smb_display_status("gss_inquire_sec_context_by_oid", gssMajor, gssMinor); // TODO - error code conversion status = gssMajor; BAIL_ON_LWIO_ERROR(status); } // The key is in element 0 and the key type OID is in element 1 if (!sessionKey || (sessionKey->count < 1) || !sessionKey->elements[0].value || (0 == sessionKey->elements[0].length)) { LWIO_ASSERT_MSG(FALSE, "Invalid session key"); status = STATUS_ASSERTION_FAILURE; BAIL_ON_LWIO_ERROR(status); } status = LW_RTL_ALLOCATE(&pSessionKey, BYTE, sessionKey->elements[0].length); BAIL_ON_LWIO_ERROR(status); memcpy(pSessionKey, sessionKey->elements[0].value, sessionKey->elements[0].length); dwSessionKeyLength = sessionKey->elements[0].length; cleanup: gss_release_buffer_set(&gssMinor, &sessionKey); *ppSessionKey = pSessionKey; *pdwSessionKeyLength = dwSessionKeyLength; return status; error: LWIO_SAFE_FREE_MEMORY(pSessionKey); dwSessionKeyLength = 0; goto cleanup; }
LW_NTSTATUS LwIoQueryStateDriver( LW_PWSTR pwszDriverName, PLWIO_DRIVER_STATE pState ) { NTSTATUS status = 0; LWMsgCall* pCall = NULL; LWMsgParams in = LWMSG_PARAMS_INITIALIZER; LWMsgParams out = LWMSG_PARAMS_INITIALIZER; status = LwIoConnectionAcquireCall(&pCall); BAIL_ON_NT_STATUS(status); in.tag = LWIO_QUERY_STATE_DRIVER; in.data = pwszDriverName; status = MAP_LWMSG_STATUS(lwmsg_call_dispatch(pCall, &in, &out, NULL, NULL)); BAIL_ON_NT_STATUS(status); switch (out.tag) { case LWIO_QUERY_STATE_DRIVER_SUCCESS: *pState = *((PLWIO_DRIVER_STATE) out.data); break; case LWIO_QUERY_STATE_DRIVER_FAILED: status = ((PLWIO_STATUS_REPLY) out.data)->dwError; BAIL_ON_LWIO_ERROR(status); break; default: status = STATUS_INTERNAL_ERROR; BAIL_ON_LWIO_ERROR(status); break; } cleanup: if (pCall) { lwmsg_call_destroy_params(pCall, &out); lwmsg_call_release(pCall); } return status; error: goto cleanup; }
DWORD SMBAllocateString( PCSTR pszInputString, PSTR* ppszOutputString ) { DWORD dwError = 0; DWORD dwLen = 0; PSTR pszOutputString = NULL; if (!pszInputString) { dwError = LWIO_ERROR_INVALID_PARAMETER; BAIL_ON_LWIO_ERROR(dwError); } dwLen = strlen(pszInputString); dwError = LwIoAllocateMemory(dwLen+1, (PVOID *)&pszOutputString); BAIL_ON_LWIO_ERROR(dwError); if (dwLen) { memcpy(pszOutputString, pszInputString, dwLen); } *ppszOutputString = pszOutputString; cleanup: return dwError; error: LWIO_SAFE_FREE_STRING(pszOutputString); *ppszOutputString = NULL; goto cleanup; }
DWORD SMBKrb5SetDefaultCachePath( PCSTR pszCachePath, PSTR* ppszOrigCachePath ) { DWORD dwError = 0; DWORD dwMajorStatus = 0; DWORD dwMinorStatus = 0; PSTR pszOrigCachePath = NULL; // Set the default for gss dwMajorStatus = gss_krb5_ccache_name( (OM_uint32 *)&dwMinorStatus, pszCachePath, (ppszOrigCachePath) ? (const char**)&pszOrigCachePath : NULL); BAIL_ON_SEC_ERROR(dwMajorStatus); if (ppszOrigCachePath) { if (!IsNullOrEmptyString(pszOrigCachePath)) { dwError = SMBAllocateString(pszOrigCachePath, ppszOrigCachePath); BAIL_ON_LWIO_ERROR(dwError); } else { *ppszOrigCachePath = NULL; } } LWIO_LOG_DEBUG("Cache path set to [%s]", LWIO_SAFE_LOG_STRING(pszCachePath)); cleanup: return dwError; sec_error: error: if (ppszOrigCachePath) { *ppszOrigCachePath = NULL; } goto cleanup; }
DWORD SMBGSSContextNegotiate( HANDLE hSMBGSSContext, PBYTE pSecurityInputBlob, DWORD dwSecurityInputBlobLen, PBYTE* ppSecurityBlob, PDWORD pdwSecurityBlobLength ) { DWORD dwError = 0; DWORD dwMajorStatus = 0; DWORD dwMinorStatus = 0; PSMB_GSS_SEC_CONTEXT pContext = (PSMB_GSS_SEC_CONTEXT)hSMBGSSContext; gss_buffer_desc input_desc = {0}; gss_buffer_desc output_desc = {0}; DWORD ret_flags = 0; PBYTE pSecurityBlob = NULL; DWORD dwSecurityBlobLength = 0; static gss_OID_desc gss_spnego_mech_oid_desc = {6, (void *)"\x2b\x06\x01\x05\x05\x02"}; if (pContext->state == SMB_GSS_SEC_CONTEXT_STATE_COMPLETE) { goto cleanup; } input_desc.value = pSecurityInputBlob; input_desc.length = dwSecurityInputBlobLen; dwMajorStatus = gss_init_sec_context( (OM_uint32 *)&dwMinorStatus, pContext->credHandle, pContext->pGSSContext, pContext->target_name, &gss_spnego_mech_oid_desc, GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG, 0, NULL, &input_desc, NULL, &output_desc, &ret_flags, NULL); smb_display_status("gss_init_sec_context", dwMajorStatus, dwMinorStatus); switch (dwMajorStatus) { case GSS_S_CONTINUE_NEEDED: pContext->state = SMB_GSS_SEC_CONTEXT_STATE_NEGOTIATE; break; case GSS_S_COMPLETE: pContext->state = SMB_GSS_SEC_CONTEXT_STATE_COMPLETE; break; case GSS_S_FAILURE: switch (dwMinorStatus) { case ((DWORD) KRB5KRB_AP_ERR_SKEW): dwError = LWIO_ERROR_CLOCK_SKEW; break; case ((DWORD) KRB5KDC_ERR_TGT_REVOKED): dwError = LW_STATUS_KDC_CERT_REVOKED; break; case ((DWORD) KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN): dwError = STATUS_ACCESS_DENIED; break; default: dwError = LWIO_ERROR_GSS; } BAIL_ON_LWIO_ERROR(dwError); break; default: dwError = LWIO_ERROR_GSS; BAIL_ON_LWIO_ERROR(dwError); break; } if (output_desc.length) { dwError = LwIoAllocateMemory( output_desc.length, (PVOID*)&pSecurityBlob); BAIL_ON_LWIO_ERROR(dwError); memcpy(pSecurityBlob, output_desc.value, output_desc.length); dwSecurityBlobLength = output_desc.length; } *ppSecurityBlob = pSecurityBlob; *pdwSecurityBlobLength = dwSecurityBlobLength; cleanup: gss_release_buffer(&dwMinorStatus, &output_desc); return dwError; error: *ppSecurityBlob = NULL; *pdwSecurityBlobLength = 0; LWIO_SAFE_FREE_MEMORY(pSecurityBlob); 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; }