/// <summary> /// It is called when SQL batch request arrives /// </summary> public virtual TDSMessageCollection OnSQLBatchRequest(ITDSServerSession session, TDSMessage message) { // Delegate to the query engine TDSMessageCollection responseMessage = Engine.ExecuteBatch(session, message); // Check if session packet size is different than the engine packet size if (session.PacketSize != Arguments.PacketSize) { // Get the first message TDSMessage firstMessage = responseMessage[0]; // Find DONE token in it int indexOfDone = firstMessage.IndexOf(firstMessage.Where(t => t is TDSDoneToken).First()); // Create new packet size environment change token TDSEnvChangeToken envChange = new TDSEnvChangeToken(TDSEnvChangeTokenType.PacketSize, Arguments.PacketSize.ToString(), session.PacketSize.ToString()); // Log response TDSUtilities.Log(Arguments.Log, "Response", envChange); // Insert env change before done token firstMessage.Insert(indexOfDone, envChange); // Update session with the new packet size session.PacketSize = (uint)Arguments.PacketSize; } return(responseMessage); }
/// <summary> /// Handler for pre-login request /// </summary> public override TDSMessageCollection OnPreLoginRequest(ITDSServerSession session, TDSMessage request) { // Delegate to the base class TDSMessageCollection response = base.OnPreLoginRequest(session, request); // Check if arguments are of the routing server if (Arguments is RoutingTDSServerArguments) { // Cast to routing server arguments RoutingTDSServerArguments serverArguments = Arguments as RoutingTDSServerArguments; // Check if routing is configured during login if (serverArguments.RouteOnPacket == TDSMessageType.TDS7Login) { // Check if pre-login response is contained inside the first message if (response.Count > 0 && response[0].Any(t => t is TDSPreLoginToken)) { // Find the first prelogin token TDSPreLoginToken preLoginResponse = (TDSPreLoginToken)response[0].Where(t => t is TDSPreLoginToken).First(); // Inflate pre-login request from the message TDSPreLoginToken preLoginRequest = request[0] as TDSPreLoginToken; // Update MARS with the requested value preLoginResponse.IsMARS = preLoginRequest.IsMARS; } } } return(response); }
/// <summary> /// Handler for login request /// </summary> public override TDSMessageCollection OnLogin7Request(ITDSServerSession session, TDSMessage request) { // Get the collection from the normal behavior On Login7 Request TDSMessageCollection login7Collection = base.OnLogin7Request(session, request); // Check if arguments are of the Federated Authentication server if (Arguments is FederatedAuthenticationNegativeTDSServerArguments) { // Cast to federated authentication server arguments FederatedAuthenticationNegativeTDSServerArguments ServerArguments = Arguments as FederatedAuthenticationNegativeTDSServerArguments; // Get the Federated Authentication ExtAck from Login 7 TDSFeatureExtAckFederatedAuthenticationOption fedAutExtAct = GetFeatureExtAckFederatedAuthenticationOptionFromLogin7(login7Collection); // If not found, return the base collection intact if (fedAutExtAct != null) { switch (ServerArguments.Scenario) { case FederatedAuthenticationNegativeTDSScenarioType.NonceMissingInFedAuthFEATUREXTACK: { // Delete the nonce from the Token fedAutExtAct.ClientNonce = null; break; } case FederatedAuthenticationNegativeTDSScenarioType.FedAuthMissingInFEATUREEXTACK: { // Remove the Fed Auth Ext Ack from the options list in the FeatureExtAckToken GetFeatureExtAckTokenFromLogin7(login7Collection).Options.Remove(fedAutExtAct); break; } case FederatedAuthenticationNegativeTDSScenarioType.SignatureMissingInFedAuthFEATUREXTACK: { // Delete the signature from the Token fedAutExtAct.Signature = null; break; } } } } // Return the collection return(login7Collection); }
/// <summary> /// It is called when SQL batch request arrives /// </summary> /// <param name="message">TDS message recieved</param> /// <returns>TDS message to respond with</returns> public override TDSMessageCollection OnSQLBatchRequest(ITDSServerSession session, TDSMessage request) { // Delegate to the base class to produce the response first TDSMessageCollection batchResponse = base.OnSQLBatchRequest(session, request); // Check if arguments are of routing server if (Arguments is RoutingTDSServerArguments) { // Cast to routing server arguments RoutingTDSServerArguments ServerArguments = Arguments as RoutingTDSServerArguments; // Check routing condition if (ServerArguments.RouteOnPacket == TDSMessageType.SQLBatch) { // Construct routing token TDSPacketToken routingToken = CreateRoutingToken(); // Log response TDSUtilities.Log(Arguments.Log, "Response", routingToken); // Insert the routing token at the beginning of the response batchResponse[0].Insert(0, routingToken); } else { // Get the first response message TDSMessage responseMessage = batchResponse[0]; // Reset the content of the first message responseMessage.Clear(); // Prepare ERROR token with the denial details responseMessage.Add(new TDSErrorToken(11111, 1, 14, "Client should have been routed by now", Arguments.ServerName)); // Log response TDSUtilities.Log(Arguments.Log, "Response", responseMessage[0]); // Prepare DONE token responseMessage.Add(new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Error)); // Log response TDSUtilities.Log(Arguments.Log, "Response", responseMessage[1]); } } // Register only one message with the collection return(batchResponse); }
/// <summary> /// Handler for login request /// </summary> public override TDSMessageCollection OnPreLoginRequest(ITDSServerSession session, TDSMessage request) { // Get the collection from a valid On PreLogin Request TDSMessageCollection preLoginCollection = base.OnPreLoginRequest(session, request); // Check if arguments are of the Federated Authentication server if (Arguments is FederatedAuthenticationNegativeTDSServerArguments) { // Cast to federated authentication server arguments FederatedAuthenticationNegativeTDSServerArguments ServerArguments = Arguments as FederatedAuthenticationNegativeTDSServerArguments; // Find the is token carrying on TDSPreLoginToken TDSPreLoginToken preLoginToken = preLoginCollection.Find(message => message.Exists(packetToken => packetToken is TDSPreLoginToken)). Find(packetToken => packetToken is TDSPreLoginToken) as TDSPreLoginToken; switch (ServerArguments.Scenario) { case FederatedAuthenticationNegativeTDSScenarioType.NonceMissingInFedAuthPreLogin: { // If we have the prelogin token if (preLoginToken != null && preLoginToken.Nonce != null) { // Nullify the nonce from the Token preLoginToken.Nonce = null; } break; } case FederatedAuthenticationNegativeTDSScenarioType.InvalidB_FEDAUTHREQUIREDResponse: { // If we have the prelogin token if (preLoginToken != null) { // Set an illegal value for B_FEDAUTHREQURED preLoginToken.FedAuthRequired = TdsPreLoginFedAuthRequiredOption.Illegal; } break; } } } // Return the collection return(preLoginCollection); }
/// <summary> /// Complete login sequence /// </summary> protected override TDSMessageCollection OnAuthenticationCompleted(ITDSServerSession session) { // Delegate to the base class TDSMessageCollection responseMessageCollection = base.OnAuthenticationCompleted(session); // Check if arguments are of routing server if (Arguments is RoutingTDSServerArguments) { // Cast to routing server arguments RoutingTDSServerArguments serverArguments = Arguments as RoutingTDSServerArguments; // Check routing condition if (serverArguments.RouteOnPacket == TDSMessageType.TDS7Login) { // Construct routing token TDSPacketToken routingToken = CreateRoutingToken(); // Log response TDSUtilities.Log(Arguments.Log, "Response", routingToken); // Get the first message TDSMessage targetMessage = responseMessageCollection[0]; // Index at which to insert the routing token int insertIndex = targetMessage.Count - 1; // VSTS# 1021027 - Read-Only Routing yields TDS protocol error // Resolution: Send TDS FeatureExtAct token before TDS ENVCHANGE token with routing information TDSPacketToken featureExtAckToken = targetMessage.Find(t => t is TDSFeatureExtAckToken); // Check if found if (featureExtAckToken != null) { // Find token position insertIndex = targetMessage.IndexOf(featureExtAckToken); } // Insert right before the done token targetMessage.Insert(insertIndex, routingToken); } } return(responseMessageCollection); }
/// <summary> /// Complete the Federated Login /// </summary> /// <param name="session">Server session</param> /// <returns>Federated Login message collection</returns> protected virtual TDSMessageCollection OnFederatedAuthenticationCompleted(ITDSServerSession session, byte[] ticket) { // Delegate to successful authentication routine TDSMessageCollection responseMessageCollection = OnAuthenticationCompleted(session); // Get the last message TDSMessage targetMessage = responseMessageCollection.Last(); IFederatedAuthenticationTicket decryptedTicket = null; try { // Get the Federated Authentication ticket using RPS decryptedTicket = FederatedAuthenticationTicketService.DecryptTicket((session as GenericTDSServerSession).FederatedAuthenticationLibrary, ticket); if (decryptedTicket is RpsTicket) { TDSUtilities.Log(Arguments.Log, "RPS ticket session key: ", (decryptedTicket as RpsTicket).sessionKey); } else if (decryptedTicket is JwtTicket) { TDSUtilities.Log(Arguments.Log, "JWT Ticket Received", null); } } catch (Exception ex) { // Prepare ERROR token TDSErrorToken errorToken = new TDSErrorToken(54879, 1, 20, "Authentication error in Federated Authentication Ticket Service: " + ex.Message, Arguments.ServerName); // Log response TDSUtilities.Log(Arguments.Log, "Response", errorToken); // Create DONE token TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Error); // Log response TDSUtilities.Log(Arguments.Log, "Response", doneToken); // Return the message and stop processing request return(new TDSMessageCollection(new TDSMessage(TDSMessageType.Response, errorToken, doneToken))); } // Create federated authentication extension option TDSFeatureExtAckFederatedAuthenticationOption federatedAuthenticationOption; if ((session as GenericTDSServerSession).FederatedAuthenticationLibrary == TDSFedAuthLibraryType.MSAL) { // For the time being, fake fedauth tokens are used for ADAL, so decryptedTicket is null. federatedAuthenticationOption = new TDSFeatureExtAckFederatedAuthenticationOption((session as GenericTDSServerSession).ClientNonce, null); } else { federatedAuthenticationOption = new TDSFeatureExtAckFederatedAuthenticationOption((session as GenericTDSServerSession).ClientNonce, decryptedTicket.GetSignature((session as GenericTDSServerSession).ClientNonce)); } // Look for feature extension token TDSFeatureExtAckToken featureExtActToken = (TDSFeatureExtAckToken)targetMessage.Where(t => t is TDSFeatureExtAckToken).FirstOrDefault(); // Check if response already contains federated authentication if (featureExtActToken == null) { // Create Feature extension Ack token featureExtActToken = new TDSFeatureExtAckToken(federatedAuthenticationOption); // Serialize feature extension token into the response // The last token is Done token, so we should put feautureextack token before done token targetMessage.Insert(targetMessage.Count - 1, featureExtActToken); } else { // Update featureExtActToken.Options.Add(federatedAuthenticationOption); } // Log response TDSUtilities.Log(Arguments.Log, "Response", federatedAuthenticationOption); // Wrap a message with a collection return(responseMessageCollection); }
/// <summary> /// Advances one step in SSPI authentication sequence /// </summary> protected virtual TDSMessageCollection ContinueSSPIAuthentication(ITDSServerSession session, byte[] payload) { // Response to send to the client SSPIResponse response; try { // Check if we have an SSPI context if (session.NTUserAuthenticationContext == null) { // This is the first step so we need to create a server context and initialize it session.NTUserAuthenticationContext = SSPIContext.CreateServer(); // Run the first step of authentication response = session.NTUserAuthenticationContext.StartServerAuthentication(payload); } else { // Process SSPI request from the client response = session.NTUserAuthenticationContext.ContinueServerAuthentication(payload); } } catch (Exception e) { // Prepare ERROR token with the reason TDSErrorToken errorToken = new TDSErrorToken(12345, 1, 15, "Failed to accept security SSPI context", Arguments.ServerName); // Log response TDSUtilities.Log(Arguments.Log, "Response", errorToken); // Serialize the error token into the response packet TDSMessage responseErrorMessage = new TDSMessage(TDSMessageType.Response, errorToken); // Prepare ERROR token with the final errorToken errorToken = new TDSErrorToken(12345, 1, 15, e.Message, Arguments.ServerName); // Log response TDSUtilities.Log(Arguments.Log, "Response", errorToken); // Serialize the error token into the response packet responseErrorMessage.Add(errorToken); // Create DONE token TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Error); // Log response TDSUtilities.Log(Arguments.Log, "Response", doneToken); // Serialize DONE token into the response packet responseErrorMessage.Add(doneToken); // Respond with a single message return(new TDSMessageCollection(responseErrorMessage)); } // Message collection to respond with TDSMessageCollection responseMessages = new TDSMessageCollection(); // Check if there's a response if (response != null) { // Check if there's a payload if (response.Payload != null) { // Create SSPI token TDSSSPIToken sspiToken = new TDSSSPIToken(response.Payload); // Log response TDSUtilities.Log(Arguments.Log, "Response", sspiToken); // Prepare response message with a single response token responseMessages.Add(new TDSMessage(TDSMessageType.Response, sspiToken)); // Check if we can complete login if (!response.IsFinal) { // Send the message to the client return(responseMessages); } } } // Reset SQL user identifier since we're using NT authentication session.SQLUserID = null; // Append successfully authentication response responseMessages.AddRange(OnAuthenticationCompleted(session)); // Return the message with SSPI token return(responseMessages); }
/// <summary> /// Handler for login request /// </summary> public virtual TDSMessageCollection OnLogin7Request(ITDSServerSession session, TDSMessage request) { // Inflate login7 request from the message TDSLogin7Token loginRequest = request[0] as TDSLogin7Token; // Log request TDSUtilities.Log(Arguments.Log, "Request", loginRequest); // Update server context session.Database = string.IsNullOrEmpty(loginRequest.Database) ? "master" : loginRequest.Database; // Resolve TDS version session.TDSVersion = TDSVersion.Resolve(TDSVersion.GetTDSVersion(Arguments.ServerVersion), loginRequest.TDSVersion); // Check for the TDS version TDSMessageCollection collection = CheckTDSVersion(session); // Check if any errors are posted if (collection != null) { // Version check needs to send own message hence we can't proceed return(collection); } // Indicates federated authentication bool bIsFedAuthConnection = false; // Federated authentication option to be used later TDSLogin7FedAuthOptionToken federatedAuthenticationOption = null; // Check if feature extension block is available if (loginRequest.FeatureExt != null) { // Go over the feature extension data foreach (TDSLogin7FeatureOptionToken option in loginRequest.FeatureExt) { // Check option type switch (option.FeatureID) { case TDSFeatureID.SessionRecovery: { // Enable session recovery session.IsSessionRecoveryEnabled = true; // Cast to session state options TDSLogin7SessionRecoveryOptionToken sessionStateOption = option as TDSLogin7SessionRecoveryOptionToken; // Inflate session state (session as GenericTDSServerSession).Inflate(sessionStateOption.Initial, sessionStateOption.Current); break; } case TDSFeatureID.FederatedAuthentication: { // Cast to federated authentication option federatedAuthenticationOption = option as TDSLogin7FedAuthOptionToken; // Mark authentication as federated bIsFedAuthConnection = true; // Validate federated authentication option collection = CheckFederatedAuthenticationOption(session, option as TDSLogin7FedAuthOptionToken); if (collection != null) { // Version error happened. return(collection); } // Save the fed auth library to be used (session as GenericTDSServerSession).FederatedAuthenticationLibrary = federatedAuthenticationOption.Library; break; } default: { // Do nothing break; } } } } // Check if SSPI authentication is requested if (loginRequest.OptionalFlags2.IntegratedSecurity == TDSLogin7OptionalFlags2IntSecurity.On) { // Delegate to SSPI authentication return(ContinueSSPIAuthentication(session, loginRequest.SSPI)); } // If it is not a FedAuth connection or the server has been started up as not supporting FedAuth, just ignore the FeatureExtension // Yes unfortunately for the fake server, supporting FedAuth = Requiring FedAuth if (!bIsFedAuthConnection || Arguments.FedAuthRequiredPreLoginOption == TdsPreLoginFedAuthRequiredOption.FedAuthNotRequired) { // We use SQL authentication session.SQLUserID = loginRequest.UserID; // Process with the SQL login. return(OnSqlAuthenticationCompleted(session)); } else { // Fedauth feature extension is present and server has been started up as Requiring (or Supporting) FedAuth if (federatedAuthenticationOption.IsRequestingAuthenticationInfo) { // Must provide client with more info before completing authentication return(OnFederatedAuthenticationInfoRequest(session)); } else { return(OnFederatedAuthenticationCompleted(session, federatedAuthenticationOption.Token)); } } }
private TDSFeatureExtAckFederatedAuthenticationOption GetFeatureExtAckFederatedAuthenticationOptionFromLogin7(TDSMessageCollection login7Collection) { // Get the Fed Auth Ext Ack from the list of options in the feature ExtAck return(GetFeatureExtAckTokenFromLogin7(login7Collection).Options. Where(o => o is TDSFeatureExtAckFederatedAuthenticationOption).FirstOrDefault() as TDSFeatureExtAckFederatedAuthenticationOption); }
private TDSFeatureExtAckToken GetFeatureExtAckTokenFromLogin7(TDSMessageCollection login7Collection) { // Find the is token carrying on TDSFeatureExtAckToken return(login7Collection.Find(m => m.Exists(p => p is TDSFeatureExtAckToken)). Find(t => t is TDSFeatureExtAckToken) as TDSFeatureExtAckToken); }
/// <summary> /// Run one cycle of the parser to process incoming stream of data or dispatch outgoing data /// </summary> public void Run() { // Check if there's a message being inflated if (MessageBeingReceived == null) { // Create a new message MessageBeingReceived = new TDSMessage(); } // Inflate the message using incoming stream // This call will use only as much data is it needs and leave everything else on the stream if (MessageBeingReceived.InflateClientRequest(Transport)) { // Call into the server logics to process the message and generate the response TDSMessageCollection responseMessages = null; // Check the type of the packet switch (MessageBeingReceived.MessageType) { case TDSMessageType.PreLogin: { // Call into the subscriber to process the packet responseMessages = Server.OnPreLoginRequest(Session, MessageBeingReceived); break; } case TDSMessageType.TDS7Login: { // Call into the subscriber to process the packet responseMessages = Server.OnLogin7Request(Session, MessageBeingReceived); // Check if encryption needs to be turned off if (Session.Encryption == TDSEncryptionType.LoginOnly) { // Disable transport encryption DisableTransportEncryption(); } break; } case TDSMessageType.SSPI: { // Call into the subscriber to process the packet responseMessages = Server.OnSSPIRequest(Session, MessageBeingReceived); break; } case TDSMessageType.SQLBatch: { // Call into the subscriber to process the packet responseMessages = Server.OnSQLBatchRequest(Session, MessageBeingReceived); break; } case TDSMessageType.Attention: { // Call into the subscriber to process the packet responseMessages = Server.OnAttention(Session, MessageBeingReceived); break; } case TDSMessageType.FederatedAuthenticationToken: { // Call into the subscriber to process the packet responseMessages = Server.OnFederatedAuthenticationTokenMessage(Session, MessageBeingReceived); break; } case TDSMessageType.RPC: { // Call into the subscriber to process the packet responseMessages = Server.OnRPCRequest(Session, MessageBeingReceived); break; } case TDSMessageType.TransactionManager: { // Call into the subscriber to process the packet responseMessages = Server.OnTransactionManagerRequest(Session, MessageBeingReceived); break; } default: { // New code is needed to process this message throw new NotImplementedException(string.Format("Handler of TDS message \"{0}\" is not implemented", MessageBeingReceived.MessageType)); } } // Check if TDS packet size changed if (Session.PacketSize != Transport.PacketSize) { // Update packet size Transport.PacketSize = Session.PacketSize; } // Send all messages to the client responseMessages.Deflate(Transport); // Check the type of message just sent if (MessageBeingReceived.MessageType == TDSMessageType.PreLogin) { // Check encryption settings on the session if (Session.Encryption == TDSEncryptionType.LoginOnly || Session.Encryption == TDSEncryptionType.Full) { // Enable server side encryption EnableServerTransportEncryption(Session.EncryptionCertificate); } } // Reset the current message because it's complete // It would also trigger creation of the next message during the next cycle MessageBeingReceived = null; } }
/// <summary> /// Advances one step in SSPI authentication sequence /// </summary> protected virtual TDSMessageCollection ContinueSSPIAuthentication(ITDSServerSession session, byte[] payload) { // Response to send to the client SSPIResponse response; try { // Check if we have an SSPI context if (session.NTUserAuthenticationContext == null) { // This is the first step so we need to create a server context and initialize it session.NTUserAuthenticationContext = SSPIContext.CreateServer(); // Run the first step of authentication response = session.NTUserAuthenticationContext.StartServerAuthentication(payload); } else { // Process SSPI request from the client response = session.NTUserAuthenticationContext.ContinueServerAuthentication(payload); } } catch (Exception e) { // Prepare ERROR token with the reason TDSErrorToken errorToken = new TDSErrorToken(12345, 1, 15, "Failed to accept security SSPI context", Arguments.ServerName); // Log response TDSUtilities.Log(Arguments.Log, "Response", errorToken); // Serialize the error token into the response packet TDSMessage responseErrorMessage = new TDSMessage(TDSMessageType.Response, errorToken); // Prepare ERROR token with the final errorToken errorToken = new TDSErrorToken(12345, 1, 15, e.Message, Arguments.ServerName); // Log response TDSUtilities.Log(Arguments.Log, "Response", errorToken); // Serialize the error token into the response packet responseErrorMessage.Add(errorToken); // Create DONE token TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Error); // Log response TDSUtilities.Log(Arguments.Log, "Response", doneToken); // Serialize DONE token into the response packet responseErrorMessage.Add(doneToken); // Respond with a single message return new TDSMessageCollection(responseErrorMessage); } // Message collection to respond with TDSMessageCollection responseMessages = new TDSMessageCollection(); // Check if there's a response if (response != null) { // Check if there's a payload if (response.Payload != null) { // Create SSPI token TDSSSPIToken sspiToken = new TDSSSPIToken(response.Payload); // Log response TDSUtilities.Log(Arguments.Log, "Response", sspiToken); // Prepare response message with a single response token responseMessages.Add(new TDSMessage(TDSMessageType.Response, sspiToken)); // Check if we can complete login if (!response.IsFinal) { // Send the message to the client return responseMessages; } } } // Reset SQL user identifier since we're using NT authentication session.SQLUserID = null; // Append successfully authentication response responseMessages.AddRange(OnAuthenticationCompleted(session)); // Return the message with SSPI token return responseMessages; }
private TDSFeatureExtAckFederatedAuthenticationOption GetFeatureExtAckFederatedAuthenticationOptionFromLogin7(TDSMessageCollection login7Collection) { // Get the Fed Auth Ext Ack from the list of options in the feature ExtAck return GetFeatureExtAckTokenFromLogin7(login7Collection).Options. Where(o => o is TDSFeatureExtAckFederatedAuthenticationOption).FirstOrDefault() as TDSFeatureExtAckFederatedAuthenticationOption; }
private TDSFeatureExtAckToken GetFeatureExtAckTokenFromLogin7(TDSMessageCollection login7Collection) { // Find the is token carrying on TDSFeatureExtAckToken return login7Collection.Find(m => m.Exists(p => p is TDSFeatureExtAckToken)). Find(t => t is TDSFeatureExtAckToken) as TDSFeatureExtAckToken; }