private static byte[] GetSpnegoTokenResponseBytes(byte[] mechanismOutput, NTStatus status, byte[]?mechanismIdentifier) { SimpleProtectedNegotiationTokenResponse tokenResponse = new SimpleProtectedNegotiationTokenResponse(); if (status == NTStatus.STATUS_SUCCESS) { tokenResponse.NegState = NegState.AcceptCompleted; } else if (status == NTStatus.SEC_I_CONTINUE_NEEDED) { tokenResponse.NegState = NegState.AcceptIncomplete; } else { tokenResponse.NegState = NegState.Reject; } tokenResponse.SupportedMechanism = mechanismIdentifier; tokenResponse.ResponseToken = mechanismOutput; return(tokenResponse.GetBytes()); }
public virtual NTStatus AcceptSecurityContext(ref GssContext?context, byte[] inputToken, out byte[]?outputToken) { outputToken = null; SimpleProtectedNegotiationToken?spnegoToken = null; try { spnegoToken = SimpleProtectedNegotiationToken.ReadToken(inputToken, 0, false); } catch { // ignored } if (spnegoToken != null) { if (spnegoToken is SimpleProtectedNegotiationTokenInit tokenInit) { if (tokenInit.MechanismTypeList.Count == 0) { return(NTStatus.SEC_E_INVALID_TOKEN); } // RFC 4178: Note that in order to avoid an extra round trip, the first context establishment token // of the initiator's preferred mechanism SHOULD be embedded in the initial negotiation message. byte[] preferredMechanism = tokenInit.MechanismTypeList[0]; IGssMechanism?mechanism = FindMechanism(preferredMechanism); bool isPreferredMechanism = (mechanism != null); if (!isPreferredMechanism) { mechanism = FindMechanism(tokenInit.MechanismTypeList); } if (mechanism == null) { return(NTStatus.SEC_E_SECPKG_NOT_FOUND); } NTStatus status; context = new GssContext(mechanism, null); if (isPreferredMechanism) { status = mechanism.AcceptSecurityContext(ref context.MechanismContext, tokenInit.MechanismToken, out byte[] mechanismOutput); outputToken = GetSpnegoTokenResponseBytes(mechanismOutput, status, mechanism.Identifier); } else { status = NTStatus.SEC_I_CONTINUE_NEEDED; outputToken = GetSpnegoTokenResponseBytes(null, status, mechanism.Identifier); } return(status); } else // SimpleProtectedNegotiationTokenResponse { if (context == null) { return(NTStatus.SEC_E_INVALID_TOKEN); } IGssMechanism mechanism = context.Mechanism; SimpleProtectedNegotiationTokenResponse tokenResponse = (SimpleProtectedNegotiationTokenResponse)spnegoToken; NTStatus status = mechanism.AcceptSecurityContext(ref context.MechanismContext, tokenResponse.ResponseToken, out byte[] mechanismOutput); outputToken = GetSpnegoTokenResponseBytes(mechanismOutput, status, null); return(status); } } // [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob. // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication. if (!AuthenticationMessageUtils.IsSignatureValid(inputToken)) { return(NTStatus.SEC_E_INVALID_TOKEN); } MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(inputToken); IGssMechanism? ntlmAuthenticationProvider = FindMechanism(NtlmSspIdentifier); if (ntlmAuthenticationProvider == null) { return(NTStatus.SEC_E_SECPKG_NOT_FOUND); } if (messageType == MessageTypeName.Negotiate) { context = new GssContext(ntlmAuthenticationProvider, null); } if (context == null) { return(NTStatus.SEC_E_INVALID_TOKEN); } return(ntlmAuthenticationProvider.AcceptSecurityContext(ref context.MechanismContext, inputToken, out outputToken)); }