internal static SecurityStatusPal InitializeSecurityContext(
            ref SafeFreeCredentials credentialsHandle,
            ref SafeDeleteContext?securityContext,
            string?spn,
            ContextFlagsPal requestedContextFlags,
            byte[]?incomingBlob,
            ChannelBinding?channelBinding,
            ref byte[]?resultBlob,
            ref ContextFlagsPal contextFlags)
        {
            SafeFreeNegoCredentials negoCredentialsHandle = (SafeFreeNegoCredentials)credentialsHandle;

            if (negoCredentialsHandle.IsDefault && string.IsNullOrEmpty(spn))
            {
                throw new PlatformNotSupportedException(SR.net_nego_not_supported_empty_target_with_defaultcreds);
            }

            SecurityStatusPal status = EstablishSecurityContext(
                negoCredentialsHandle,
                ref securityContext,
                channelBinding,
                spn,
                requestedContextFlags,
                incomingBlob,
                ref resultBlob,
                ref contextFlags);

            // Confidentiality flag should not be set if not requested
            if (status.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded)
            {
                ContextFlagsPal mask = ContextFlagsPal.Confidentiality;
                if ((requestedContextFlags & mask) != (contextFlags & mask))
                {
                    throw new PlatformNotSupportedException(SR.net_nego_protection_level_not_supported);
                }
            }

            return(status);
        }
        private void Initialize(bool isServer, string package, NetworkCredential credential, string?spn, ContextFlagsPal requestedContextFlags, ChannelBinding?channelBinding)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this, package, spn, requestedContextFlags);
            }

            _tokenSize             = NegotiateStreamPal.QueryMaxTokenSize(package);
            _isServer              = isServer;
            _spn                   = spn;
            _securityContext       = null;
            _requestedContextFlags = requestedContextFlags;
            _package               = package;
            _channelBinding        = channelBinding;

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Info(this, $"Peer SPN-> '{_spn}'");
            }

            //
            // Check if we're using DefaultCredentials.
            //

            Debug.Assert(CredentialCache.DefaultCredentials == CredentialCache.DefaultNetworkCredentials);
            if (credential == CredentialCache.DefaultCredentials)
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Info(this, "using DefaultCredentials");
                }
                _credentialsHandle = NegotiateStreamPal.AcquireDefaultCredential(package, _isServer);
            }
            else
            {
                _credentialsHandle = NegotiateStreamPal.AcquireCredentialsHandle(package, _isServer, credential);
            }
        }
Exemple #3
0
        internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string?spn, ContextFlagsPal requestedContextFlags, ChannelBinding?channelBinding)
        {
            if (isServer)
            {
                throw new PlatformNotSupportedException(SR.net_nego_server_not_supported);
            }

            if (package.Equals("NTLM", StringComparison.OrdinalIgnoreCase))
            {
                _isSpNego = false;
            }
            else if (package.Equals("Negotiate", StringComparison.OrdinalIgnoreCase))
            {
                _isSpNego = true;
            }
            else
            {
                throw new PlatformNotSupportedException(SR.net_securitypackagesupport);
            }

            if (string.IsNullOrWhiteSpace(credential.UserName) || string.IsNullOrWhiteSpace(credential.Password))
            {
                // NTLM authentication is not possible with default credentials which are no-op
                throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
            }

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this, $"package={package}, spn={spn}, requestedContextFlags={requestedContextFlags}");
            }

            _credential     = credential;
            _spn            = spn;
            _channelBinding = channelBinding;
            _contextFlags   = requestedContextFlags;
            IsServer        = isServer;
        }
Exemple #4
0
        public virtual IAsyncResult BeginAuthenticateAsClient(
            NetworkCredential credential,
            ChannelBinding?binding,
            string targetName,
            ProtectionLevel requiredProtectionLevel,
            TokenImpersonationLevel allowedImpersonationLevel,
            AsyncCallback?asyncCallback,
            object?asyncState)
        {
#if DEBUG
            using (DebugThreadTracking.SetThreadKind(ThreadKinds.User | ThreadKinds.Async))
            {
#endif
            _negoState.ValidateCreateContext(_package, false, credential, targetName, binding, requiredProtectionLevel, allowedImpersonationLevel);

            LazyAsyncResult result = new LazyAsyncResult(_negoState, asyncState, asyncCallback);
            _negoState.ProcessAuthentication(result);

            return(result);

#if DEBUG
        }
#endif
        }
Exemple #5
0
 public virtual IAsyncResult BeginAuthenticateAsClient(
     NetworkCredential credential, ChannelBinding?binding, string targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel,
     AsyncCallback?asyncCallback, object?asyncState) =>
 TaskToApm.Begin(AuthenticateAsClientAsync(credential, binding, targetName, requiredProtectionLevel, allowedImpersonationLevel), asyncCallback, asyncState);
Exemple #6
0
 public virtual IAsyncResult BeginAuthenticateAsClient(NetworkCredential credential, ChannelBinding?binding, string targetName, AsyncCallback?asyncCallback, object?asyncState) =>
 BeginAuthenticateAsClient(credential, binding, targetName, ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification,
                           asyncCallback, asyncState);
Exemple #7
0
 public virtual Task AuthenticateAsClientAsync(NetworkCredential credential, ChannelBinding?binding, string targetName)
 {
     return(Task.Factory.FromAsync(BeginAuthenticateAsClient, EndAuthenticateAsClient, credential, binding, targetName, null));
 }
Exemple #8
0
 public static IChannelBinding?ToInterface(this ChannelBinding?binding)
 {
     return((binding == null) ? null : new ChannelBindingAdapter(binding));
 }
Exemple #9
0
        public Authorization?Authenticate(string?challenge, NetworkCredential?credential, object sessionCookie, string?spn, ChannelBinding?channelBindingToken)
        {
            lock (_sessions)
            {
                NegotiateAuthentication?clientContext;
                if (!_sessions.TryGetValue(sessionCookie, out clientContext))
                {
                    if (credential == null)
                    {
                        return(null);
                    }

                    _sessions[sessionCookie] = clientContext =
                        new NegotiateAuthentication(
                            new NegotiateAuthenticationClientOptions
                    {
                        Credential = credential,
                        TargetName = spn,
                        Binding    = channelBindingToken
                    });
                }

                NegotiateAuthenticationStatusCode statusCode;
                string?resp = clientContext.GetOutgoingBlob(challenge, out statusCode);

                if (statusCode != NegotiateAuthenticationStatusCode.Completed &&
                    statusCode != NegotiateAuthenticationStatusCode.ContinueNeeded)
                {
                    return(null);
                }

                if (!clientContext.IsAuthenticated)
                {
                    return(new Authorization(resp, false));
                }
                else
                {
                    _sessions.Remove(sessionCookie);
                    clientContext.Dispose();
                    return(new Authorization(resp, true));
                }
            }
        }
Exemple #10
0
        public Authorization?Authenticate(string?challenge, NetworkCredential?credential, object sessionCookie, string?spn, ChannelBinding?channelBindingToken)
        {
            try
            {
                lock (_sessions)
                {
                    NTAuthentication?clientContext;
                    if (!_sessions.TryGetValue(sessionCookie, out clientContext))
                    {
                        if (credential == null)
                        {
                            return(null);
                        }

                        _sessions[sessionCookie] =
                            clientContext        =
                                new NTAuthentication(false, "Ntlm", credential, spn, ContextFlagsPal.Connection, channelBindingToken);
                    }

                    string?resp = clientContext.GetOutgoingBlob(challenge);

                    if (!clientContext.IsCompleted)
                    {
                        return(new Authorization(resp, false));
                    }
                    else
                    {
                        _sessions.Remove(sessionCookie);
                        return(new Authorization(resp, true));
                    }
                }
            }
            // From reflected type NTAuthentication in System.Net.Security.
            catch (NullReferenceException)
            {
                return(null);
            }
        }
        private static async Task <HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMessage request, Uri authUri, bool async, ICredentials credentials, bool isProxyAuth, HttpConnection connection, HttpConnectionPool connectionPool, CancellationToken cancellationToken)
        {
            HttpResponseMessage response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);

            if (!isProxyAuth && connection.Kind == HttpConnectionKind.Proxy && !ProxySupportsConnectionAuth(response))
            {
                // Proxy didn't indicate that it supports connection-based auth, so we can't proceed.
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(connection, $"Proxy doesn't support connection-based auth, uri={authUri}");
                }
                return(response);
            }

            if (TryGetAuthenticationChallenge(response, isProxyAuth, authUri, credentials, out AuthenticationChallenge challenge))
            {
                if (challenge.AuthenticationType == AuthenticationType.Negotiate ||
                    challenge.AuthenticationType == AuthenticationType.Ntlm)
                {
                    bool isNewConnection = false;
                    bool needDrain       = true;
                    try
                    {
                        if (response.Headers.ConnectionClose.GetValueOrDefault())
                        {
                            // Server is closing the connection and asking us to authenticate on a new connection.

                            // First, detach the current connection from the pool. This means it will no longer count against the connection limit.
                            // Instead, it will be replaced by the new connection below.
                            connection.DetachFromPool();

                            connection = await connectionPool.CreateHttp11ConnectionAsync(request, async, cancellationToken).ConfigureAwait(false);

                            connection !.Acquire();
                            isNewConnection = true;
                            needDrain       = false;
                        }

                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Uri: {authUri.AbsoluteUri}");
                        }

                        // Calculate SPN (Service Principal Name) using the host name of the request.
                        // Use the request's 'Host' header if available. Otherwise, use the request uri.
                        // Ignore the 'Host' header if this is proxy authentication since we need to use
                        // the host name of the proxy itself for SPN calculation.
                        string hostName;
                        if (!isProxyAuth && request.HasHeaders && request.Headers.Host != null)
                        {
                            // Use the host name without any normalization.
                            hostName = request.Headers.Host;
                            if (NetEventSource.Log.IsEnabled())
                            {
                                NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, Host: {hostName}");
                            }
                        }
                        else
                        {
                            // Need to use FQDN normalized host so that CNAME's are traversed.
                            // Use DNS to do the forward lookup to an A (host) record.
                            // But skip DNS lookup on IP literals. Otherwise, we would end up
                            // doing an unintended reverse DNS lookup.
                            UriHostNameType hnt = authUri.HostNameType;
                            if (hnt == UriHostNameType.IPv6 || hnt == UriHostNameType.IPv4)
                            {
                                hostName = authUri.IdnHost;
                            }
                            else
                            {
                                IPHostEntry result = await Dns.GetHostEntryAsync(authUri.IdnHost, cancellationToken).ConfigureAwait(false);

                                hostName = result.HostName;
                            }

                            if (!isProxyAuth && !authUri.IsDefaultPort && UsePortInSpn)
                            {
                                hostName = string.Create(null, stackalloc char[128], $"{hostName}:{authUri.Port}");
                            }
                        }

                        string spn = "HTTP/" + hostName;
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Info(connection, $"Authentication: {challenge.AuthenticationType}, SPN: {spn}");
                        }

                        ChannelBinding?  channelBinding = connection.TransportContext?.GetChannelBinding(ChannelBindingKind.Endpoint);
                        NTAuthentication authContext    = new NTAuthentication(isServer: false, challenge.SchemeName, challenge.Credential, spn, ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, channelBinding);
                        string?          challengeData  = challenge.ChallengeData;
                        try
                        {
                            while (true)
                            {
                                string?challengeResponse = authContext.GetOutgoingBlob(challengeData);
                                if (challengeResponse == null)
                                {
                                    // Response indicated denial even after login, so stop processing and return current response.
                                    break;
                                }

                                if (needDrain)
                                {
                                    await connection.DrainResponseAsync(response !, cancellationToken).ConfigureAwait(false);
                                }

                                SetRequestAuthenticationHeaderValue(request, new AuthenticationHeaderValue(challenge.SchemeName, challengeResponse), isProxyAuth);

                                response = await InnerSendAsync(request, async, isProxyAuth, connectionPool, connection, cancellationToken).ConfigureAwait(false);

                                if (authContext.IsCompleted || !TryGetRepeatedChallenge(response, challenge.SchemeName, isProxyAuth, out challengeData))
                                {
                                    break;
                                }

                                needDrain = true;
                            }
                        }
                        finally
                        {
                            authContext.CloseContext();
                        }
                    }
                    finally
                    {
                        if (isNewConnection)
                        {
                            connection !.Release();
                        }
                    }
                }
            }

            return(response !);
        }
Exemple #12
0
        internal static SecurityStatusPal AcceptSecurityContext(
            SafeFreeCredentials?credentialsHandle,
            ref SafeDeleteContext?securityContext,
            ContextFlagsPal requestedContextFlags,
            ReadOnlySpan <byte> incomingBlob,
            ChannelBinding?channelBinding,
            ref byte[] resultBlob,
            out int resultBlobLength,
            ref ContextFlagsPal contextFlags)
        {
            securityContext ??= new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle !);

            SafeDeleteNegoContext negoContext   = (SafeDeleteNegoContext)securityContext;
            SafeGssContextHandle  contextHandle = negoContext.GssContext;

            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
            try
            {
                Interop.NetSecurityNative.Status status;
                Interop.NetSecurityNative.Status minorStatus;
                status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus,
                                                                    negoContext.AcceptorCredential,
                                                                    ref contextHandle,
                                                                    incomingBlob,
                                                                    ref token,
                                                                    out uint outputFlags,
                                                                    out bool isNtlmUsed);

                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
                {
                    if (negoContext.GssContext.IsInvalid)
                    {
                        contextHandle.Dispose();
                    }

                    Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, gex);
                    }
                    resultBlobLength = 0;
                    return(new SecurityStatusPal(GetErrorCode(gex), gex));
                }

                resultBlob = token.ToByteArray();

                Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");

                contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);
                resultBlobLength = resultBlob.Length;

                SecurityStatusPalErrorCode errorCode;
                if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        string protocol = isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
                        NetEventSource.Info(securityContext, $"AcceptSecurityContext: actual protocol = {protocol}");
                    }

                    negoContext.SetAuthenticationPackage(isNtlmUsed);
                    errorCode = SecurityStatusPalErrorCode.OK;
                }
                else
                {
                    errorCode = SecurityStatusPalErrorCode.ContinueNeeded;
                }

                return(new SecurityStatusPal(errorCode));
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(null, ex);
                }
                resultBlobLength = 0;
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex));
            }
            finally
            {
                token.Dispose();

                // Save the inner context handle for further calls to NetSecurity
                //
                // For the first call `negoContext.GssContext` is invalid and we expect the
                // inital handle to be returned from AcceptSecContext. For any subsequent
                // call the handle should stay the same or it can be destroyed by the native
                // AcceptSecContext call.
                Debug.Assert(
                    negoContext.GssContext == contextHandle ||
                    negoContext.GssContext.IsInvalid ||
                    contextHandle.IsInvalid);
                negoContext.SetGssContext(contextHandle);
            }
        }
Exemple #13
0
        private static SecurityStatusPal EstablishSecurityContext(
            SafeFreeNegoCredentials credential,
            ref SafeDeleteContext?context,
            ChannelBinding?channelBinding,
            string?targetName,
            ContextFlagsPal inFlags,
            ReadOnlySpan <byte> incomingBlob,
            out byte[]?resultBuffer,
            ref ContextFlagsPal outFlags)
        {
            bool isNtlmOnly = credential.IsNtlmOnly;

            resultBuffer = null;

            if (context == null)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    string protocol = isNtlmOnly ? "NTLM" : "SPNEGO";
                    NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}");
                }

                context = new SafeDeleteNegoContext(credential, targetName !);
            }

            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
            Interop.NetSecurityNative.Status    status;
            Interop.NetSecurityNative.Status    minorStatus;
            SafeDeleteNegoContext negoContext   = (SafeDeleteNegoContext)context;
            SafeGssContextHandle  contextHandle = negoContext.GssContext;

            try
            {
                Interop.NetSecurityNative.GssFlags inputFlags =
                    ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
                uint outputFlags;
                bool isNtlmUsed;

                if (channelBinding != null)
                {
                    // If a TLS channel binding token (cbt) is available then get the pointer
                    // to the application specific data.
                    int appDataOffset = Marshal.SizeOf <SecChannelBindings>();
                    Debug.Assert(appDataOffset < channelBinding.Size);
                    IntPtr cbtAppData     = channelBinding.DangerousGetHandle() + appDataOffset;
                    int    cbtAppDataSize = channelBinding.Size - appDataOffset;
                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
                                                                      credential.GssCredential,
                                                                      ref contextHandle,
                                                                      isNtlmOnly,
                                                                      cbtAppData,
                                                                      cbtAppDataSize,
                                                                      negoContext.TargetName,
                                                                      (uint)inputFlags,
                                                                      incomingBlob,
                                                                      ref token,
                                                                      out outputFlags,
                                                                      out isNtlmUsed);
                }
                else
                {
                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
                                                                      credential.GssCredential,
                                                                      ref contextHandle,
                                                                      isNtlmOnly,
                                                                      negoContext.TargetName,
                                                                      (uint)inputFlags,
                                                                      incomingBlob,
                                                                      ref token,
                                                                      out outputFlags,
                                                                      out isNtlmUsed);
                }

                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
                {
                    if (negoContext.GssContext.IsInvalid)
                    {
                        context.Dispose();
                    }

                    Interop.NetSecurityNative.GssApiException gex = new Interop.NetSecurityNative.GssApiException(status, minorStatus);
                    if (NetEventSource.Log.IsEnabled())
                    {
                        NetEventSource.Error(null, gex);
                    }
                    resultBuffer = Array.Empty <byte>();
                    return(new SecurityStatusPal(GetErrorCode(gex), gex));
                }

                resultBuffer = token.ToByteArray();

                if (status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
                        NetEventSource.Info(context, $"actual protocol = {protocol}");
                    }

                    // Populate protocol used for authentication
                    negoContext.SetAuthenticationPackage(isNtlmUsed);
                }

                Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
                outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);

                SecurityStatusPalErrorCode errorCode = status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE ?
                                                       SecurityStatusPalErrorCode.OK :
                                                       SecurityStatusPalErrorCode.ContinueNeeded;
                return(new SecurityStatusPal(errorCode));
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(null, ex);
                }
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex));
            }
            finally
            {
                token.Dispose();

                // Save the inner context handle for further calls to NetSecurity
                //
                // For the first call `negoContext.GssContext` is invalid and we expect the
                // inital handle to be returned from InitSecContext. For any subsequent
                // call the handle should stay the same or it can be destroyed by the native
                // InitSecContext call.
                Debug.Assert(
                    negoContext.GssContext == contextHandle ||
                    negoContext.GssContext.IsInvalid ||
                    contextHandle.IsInvalid);
                negoContext.SetGssContext(contextHandle);
            }
        }
Exemple #14
0
        public Authorization?Authenticate(string?challenge, NetworkCredential?credential, object sessionCookie, string?spn, ChannelBinding?channelBindingToken)
        {
            lock (_sessions)
            {
                NegotiateAuthentication?clientContext;
                if (!_sessions.TryGetValue(sessionCookie, out clientContext))
                {
                    if (credential == null)
                    {
                        return(null);
                    }

                    ProtectionLevel protectionLevel = ProtectionLevel.Sign;
                    // Workaround for https://github.com/gssapi/gss-ntlmssp/issues/77
                    // GSSAPI NTLM SSP does not support gss_wrap/gss_unwrap unless confidentiality
                    // is negotiated.
                    if (OperatingSystem.IsLinux())
                    {
                        protectionLevel = ProtectionLevel.EncryptAndSign;
                    }

                    _sessions[sessionCookie] = clientContext =
                        new NegotiateAuthentication(
                            new NegotiateAuthenticationClientOptions
                    {
                        Credential = credential,
                        TargetName = spn,
                        RequiredProtectionLevel = protectionLevel,
                        Binding = channelBindingToken
                    });
                }

                string?resp = null;
                NegotiateAuthenticationStatusCode statusCode;

                if (!clientContext.IsAuthenticated)
                {
                    // If auth is not yet completed keep producing
                    // challenge responses with GetOutgoingBlob
                    resp = clientContext.GetOutgoingBlob(challenge, out statusCode);
                    if (statusCode != NegotiateAuthenticationStatusCode.Completed &&
                        statusCode != NegotiateAuthenticationStatusCode.ContinueNeeded)
                    {
                        return(null);
                    }
                    if (clientContext.IsAuthenticated && resp == null)
                    {
                        resp = "\r\n";
                    }
                }
                else
                {
                    // If auth completed and still have a challenge then
                    // server may be doing "correct" form of GSSAPI SASL.
                    // Validate incoming and produce outgoing SASL security
                    // layer negotiate message.

                    resp = GetSecurityLayerOutgoingBlob(challenge, clientContext);
                }

                return(new Authorization(resp, clientContext.IsAuthenticated));
            }
        }
Exemple #15
0
        public Authorization?Authenticate(string?challenge, NetworkCredential?credential, object sessionCookie, string?spn, ChannelBinding?channelBindingToken)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this);
            }
            try
            {
                lock (_sessions)
                {
                    NetworkCredential?cachedCredential;
                    if (!_sessions.TryGetValue(sessionCookie, out cachedCredential))
                    {
                        if (credential == null || ReferenceEquals(credential, CredentialCache.DefaultNetworkCredentials))
                        {
                            return(null);
                        }

                        _sessions[sessionCookie] = credential;

                        string userName = credential.UserName;
                        string domain   = credential.Domain;

                        if (domain != null && domain.Length > 0)
                        {
                            userName = domain + "\\" + userName;
                        }

                        return(new Authorization(Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(userName)), false));
                    }
                    else
                    {
                        _sessions.Remove(sessionCookie);

                        return(new Authorization(Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(cachedCredential.Password)), true));
                    }
                }
            }
            finally
            {
                if (NetEventSource.IsEnabled)
                {
                    NetEventSource.Exit(this);
                }
            }
        }
        public Authorization?Authenticate(string?challenge, NetworkCredential?credential, object sessionCookie, string?spn, ChannelBinding?channelBindingToken)
        {
            try
            {
                lock (_sessions)
                {
                    NTAuthentication?clientContext;
                    if (!_sessions.TryGetValue(sessionCookie, out clientContext))
                    {
                        if (credential == null)
                        {
                            return(null);
                        }

                        _sessions[sessionCookie] =
                            clientContext        =
                                new NTAuthentication(false, "Negotiate", credential, spn,
                                                     ContextFlagsPal.Connection | ContextFlagsPal.InitIntegrity, channelBindingToken);
                    }

                    byte[]? byteResp;
                    string?resp = null;

                    if (!clientContext.IsCompleted)
                    {
                        // If auth is not yet completed keep producing
                        // challenge responses with GetOutgoingBlob

                        byte[]? decodedChallenge = null;
                        if (challenge != null)
                        {
                            decodedChallenge =
                                Convert.FromBase64String(challenge);
                        }
                        byteResp = clientContext.GetOutgoingBlob(decodedChallenge, false);
                        if (clientContext.IsCompleted && byteResp == null)
                        {
                            resp = "\r\n";
                        }
                        if (byteResp != null)
                        {
                            resp = Convert.ToBase64String(byteResp);
                        }
                    }
                    else
                    {
                        // If auth completed and still have a challenge then
                        // server may be doing "correct" form of GSSAPI SASL.
                        // Validate incoming and produce outgoing SASL security
                        // layer negotiate message.

                        resp = GetSecurityLayerOutgoingBlob(challenge, clientContext);
                    }

                    return(new Authorization(resp, clientContext.IsCompleted));
                }
            }
            // From reflected type NTAuthentication in System.Net.Security.
            catch (NullReferenceException)
            {
                return(null);
            }
        }
Exemple #17
0
 public virtual void AuthenticateAsClient(
     NetworkCredential credential, ChannelBinding?binding, string targetName, ProtectionLevel requiredProtectionLevel, TokenImpersonationLevel allowedImpersonationLevel)
 {
     ValidateCreateContext(DefaultPackage, isServer: false, credential, targetName, binding, requiredProtectionLevel, allowedImpersonationLevel);
     AuthenticateAsync(new SyncReadWriteAdapter(InnerStream)).GetAwaiter().GetResult();
 }
Exemple #18
0
        internal static SecurityStatusPal AcceptSecurityContext(
            SafeFreeCredentials?credentialsHandle,
            ref SafeDeleteContext?securityContext,
            ContextFlagsPal requestedContextFlags,
            byte[]?incomingBlob,
            ChannelBinding?channelBinding,
            ref byte[] resultBlob,
            ref ContextFlagsPal contextFlags)
        {
            if (securityContext == null)
            {
                securityContext = new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle !);
            }

            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;

            try
            {
                SafeGssContextHandle?contextHandle = negoContext.GssContext;
                bool done = GssAcceptSecurityContext(
                    ref contextHandle,
                    negoContext.AcceptorCredential,
                    incomingBlob,
                    out resultBlob,
                    out uint outputFlags,
                    out bool isNtlmUsed);

                Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi");
                Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

                // Save the inner context handle for further calls to NetSecurity
                Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
                if (null == negoContext.GssContext)
                {
                    negoContext.SetGssContext(contextHandle !);
                }

                contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true);

                SecurityStatusPalErrorCode errorCode;
                if (done)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        string protocol = isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
                        NetEventSource.Info(securityContext, $"AcceptSecurityContext: actual protocol = {protocol}");
                    }

                    negoContext.SetAuthenticationPackage(isNtlmUsed);
                    errorCode = (isNtlmUsed && resultBlob.Length > 0) ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded;
                }
                else
                {
                    errorCode = SecurityStatusPalErrorCode.ContinueNeeded;
                }

                return(new SecurityStatusPal(errorCode));
            }
            catch (Interop.NetSecurityNative.GssApiException gex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(null, gex);
                }
                return(new SecurityStatusPal(GetErrorCode(gex), gex));
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(null, ex);
                }
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex));
            }
        }
Exemple #19
0
 public virtual Task AuthenticateAsClientAsync(NetworkCredential credential, ChannelBinding?binding, string targetName) =>
 AuthenticateAsClientAsync(credential, binding, targetName, ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification);
Exemple #20
0
        internal void ValidateCreateContext(
            string package,
            bool isServer,
            NetworkCredential credential,
            string?servicePrincipalName,
            ChannelBinding?channelBinding,
            ProtectionLevel protectionLevel,
            TokenImpersonationLevel impersonationLevel)
        {
            if (_exception != null && !_canRetryAuthentication)
            {
                ExceptionDispatchInfo.Throw(_exception);
            }

            if (_context != null && _context.IsValidContext)
            {
                throw new InvalidOperationException(SR.net_auth_reauth);
            }

            if (credential == null)
            {
                throw new ArgumentNullException(nameof(credential));
            }

            if (servicePrincipalName == null)
            {
                throw new ArgumentNullException(nameof(servicePrincipalName));
            }

            NegotiateStreamPal.ValidateImpersonationLevel(impersonationLevel);
            if (_context != null && IsServer != isServer)
            {
                throw new InvalidOperationException(SR.net_auth_client_server);
            }

            _exception = null;
            _remoteOk  = false;
            _framer    = new StreamFramer(_innerStream);
            _framer.WriteHeader.MessageId = FrameHeader.HandshakeId;

            _expectedProtectionLevel    = protectionLevel;
            _expectedImpersonationLevel = isServer ? impersonationLevel : TokenImpersonationLevel.None;
            _writeSequenceNumber        = 0;
            _readSequenceNumber         = 0;

            ContextFlagsPal flags = ContextFlagsPal.Connection;

            // A workaround for the client when talking to Win9x on the server side.
            if (protectionLevel == ProtectionLevel.None && !isServer)
            {
                package = NegotiationInfoClass.NTLM;
            }
            else if (protectionLevel == ProtectionLevel.EncryptAndSign)
            {
                flags |= ContextFlagsPal.Confidentiality;
            }
            else if (protectionLevel == ProtectionLevel.Sign)
            {
                // Assuming user expects NT4 SP4 and above.
                flags |= (ContextFlagsPal.ReplayDetect | ContextFlagsPal.SequenceDetect | ContextFlagsPal.InitIntegrity);
            }

            if (isServer)
            {
                if (_extendedProtectionPolicy !.PolicyEnforcement == PolicyEnforcement.WhenSupported)
                {
                    flags |= ContextFlagsPal.AllowMissingBindings;
                }

                if (_extendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never &&
                    _extendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy)
                {
                    flags |= ContextFlagsPal.ProxyBindings;
                }
            }
            else
            {
                // Server side should not request any of these flags.
                if (protectionLevel != ProtectionLevel.None)
                {
                    flags |= ContextFlagsPal.MutualAuth;
                }

                if (impersonationLevel == TokenImpersonationLevel.Identification)
                {
                    flags |= ContextFlagsPal.InitIdentify;
                }

                if (impersonationLevel == TokenImpersonationLevel.Delegation)
                {
                    flags |= ContextFlagsPal.Delegate;
                }
            }

            _canRetryAuthentication = false;

            try
            {
                _context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding !);
            }
            catch (Win32Exception e)
            {
                throw new AuthenticationException(SR.net_auth_SSPI, e);
            }
        }
Exemple #21
0
        private static SecurityStatusPal EstablishSecurityContext(
            SafeFreeNegoCredentials credential,
            ref SafeDeleteContext?context,
            ChannelBinding?channelBinding,
            string?targetName,
            ContextFlagsPal inFlags,
            byte[]?incomingBlob,
            ref byte[]?resultBuffer,
            ref ContextFlagsPal outFlags)
        {
            bool isNtlmOnly = credential.IsNtlmOnly;

            if (context == null)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    string protocol = isNtlmOnly ? "NTLM" : "SPNEGO";
                    NetEventSource.Info(context, $"requested protocol = {protocol}, target = {targetName}");
                }

                context = new SafeDeleteNegoContext(credential, targetName !);
            }

            SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;

            try
            {
                Interop.NetSecurityNative.GssFlags inputFlags =
                    ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags, isServer: false);
                uint outputFlags;
                bool isNtlmUsed;
                SafeGssContextHandle?contextHandle = negoContext.GssContext;
                bool done = GssInitSecurityContext(
                    ref contextHandle,
                    credential.GssCredential,
                    isNtlmOnly,
                    channelBinding,
                    negoContext.TargetName,
                    inputFlags,
                    incomingBlob,
                    out resultBuffer,
                    out outputFlags,
                    out isNtlmUsed);

                if (done)
                {
                    if (NetEventSource.Log.IsEnabled())
                    {
                        string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos";
                        NetEventSource.Info(context, $"actual protocol = {protocol}");
                    }

                    // Populate protocol used for authentication
                    negoContext.SetAuthenticationPackage(isNtlmUsed);
                }

                Debug.Assert(resultBuffer != null, "Unexpected null buffer returned by GssApi");
                outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
                    (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: false);
                Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);

                // Save the inner context handle for further calls to NetSecurity
                Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
                if (null == negoContext.GssContext)
                {
                    negoContext.SetGssContext(contextHandle !);
                }

                SecurityStatusPalErrorCode errorCode = done ?
                                                       (negoContext.IsNtlmUsed && resultBuffer.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) :
                                                       SecurityStatusPalErrorCode.ContinueNeeded;
                return(new SecurityStatusPal(errorCode));
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(null, ex);
                }
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex));
            }
        }
 internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string?spn, ContextFlagsPal requestedContextFlags, ChannelBinding?channelBinding)
 {
     Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
 }
Exemple #23
0
        private static bool GssInitSecurityContext(
            ref SafeGssContextHandle?context,
            SafeGssCredHandle credential,
            bool isNtlm,
            ChannelBinding?channelBinding,
            SafeGssNameHandle?targetName,
            Interop.NetSecurityNative.GssFlags inFlags,
            byte[]?buffer,
            out byte[]?outputBuffer,
            out uint outFlags,
            out bool isNtlmUsed)
        {
            outputBuffer = null;
            outFlags     = 0;

            // EstablishSecurityContext is called multiple times in a session.
            // In each call, we need to pass the context handle from the previous call.
            // For the first call, the context handle will be null.
            bool newContext = false;

            if (context == null)
            {
                newContext = true;
                context    = new SafeGssContextHandle();
            }

            Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer);
            Interop.NetSecurityNative.Status    status;

            try
            {
                Interop.NetSecurityNative.Status minorStatus;

                if (channelBinding != null)
                {
                    // If a TLS channel binding token (cbt) is available then get the pointer
                    // to the application specific data.
                    int appDataOffset = Marshal.SizeOf <SecChannelBindings>();
                    Debug.Assert(appDataOffset < channelBinding.Size);
                    IntPtr cbtAppData     = channelBinding.DangerousGetHandle() + appDataOffset;
                    int    cbtAppDataSize = channelBinding.Size - appDataOffset;
                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
                                                                      credential,
                                                                      ref context,
                                                                      isNtlm,
                                                                      cbtAppData,
                                                                      cbtAppDataSize,
                                                                      targetName,
                                                                      (uint)inFlags,
                                                                      buffer,
                                                                      (buffer == null) ? 0 : buffer.Length,
                                                                      ref token,
                                                                      out outFlags,
                                                                      out isNtlmUsed);
                }
                else
                {
                    status = Interop.NetSecurityNative.InitSecContext(out minorStatus,
                                                                      credential,
                                                                      ref context,
                                                                      isNtlm,
                                                                      targetName,
                                                                      (uint)inFlags,
                                                                      buffer,
                                                                      (buffer == null) ? 0 : buffer.Length,
                                                                      ref token,
                                                                      out outFlags,
                                                                      out isNtlmUsed);
                }

                if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) &&
                    (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED))
                {
                    if (newContext)
                    {
                        context.Dispose();
                        context = null;
                    }
                    throw new Interop.NetSecurityNative.GssApiException(status, minorStatus);
                }

                outputBuffer = token.ToByteArray();
            }
            finally
            {
                token.Dispose();
            }

            return(status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE);
        }
Exemple #24
0
            internal AuthenticateCallbackContext(SmtpConnection thisPtr, ISmtpAuthenticationModule module, NetworkCredential credential, string?spn, ChannelBinding?Token)
            {
                _thisPtr    = thisPtr;
                _module     = module;
                _credential = credential;
                _spn        = spn;
                _token      = Token;

                _result = null;
            }