internal void TdsLogin(SqlLogin rec, TdsEnums.FeatureExtension requestedFeatures, SessionData recoverySessionData, FederatedAuthenticationFeatureExtensionData? fedAuthFeatureExtensionData) { _physicalStateObj.SetTimeoutSeconds(rec.timeout); Debug.Assert(recoverySessionData == null || (requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0, "Recovery session data without session recovery feature request"); Debug.Assert(TdsEnums.MAXLEN_HOSTNAME>=rec.hostName.Length, "_workstationId.Length exceeds the max length for this value"); Debug.Assert(!(rec.useSSPI && _connHandler._fedAuthRequired), "Cannot use SSPI when server has responded 0x01 for FedAuthRequired PreLogin Option."); Debug.Assert(!rec.useSSPI || (requestedFeatures & TdsEnums.FeatureExtension.FedAuth) == 0, "Cannot use both SSPI and FedAuth"); Debug.Assert(fedAuthFeatureExtensionData == null || (requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0, "fedAuthFeatureExtensionData provided without fed auth feature request"); Debug.Assert(fedAuthFeatureExtensionData != null || (requestedFeatures & TdsEnums.FeatureExtension.FedAuth) == 0, "Fed Auth feature requested without specifying fedAuthFeatureExtensionData."); Debug.Assert(rec.userName == null || (rec.userName != null && TdsEnums.MAXLEN_USERNAME >= rec.userName.Length), "_userID.Length exceeds the max length for this value"); Debug.Assert(rec.credential == null || (rec.credential != null && TdsEnums.MAXLEN_USERNAME >= rec.credential.UserId.Length), "_credential.UserId.Length exceeds the max length for this value"); Debug.Assert(rec.password == null || (rec.password != null && TdsEnums.MAXLEN_PASSWORD>=rec.password.Length), "_password.Length exceeds the max length for this value"); Debug.Assert(rec.credential == null || (rec.credential != null && TdsEnums.MAXLEN_PASSWORD >= rec.credential.Password.Length), "_credential.Password.Length exceeds the max length for this value"); Debug.Assert(rec.credential != null || rec.userName != null || rec.password != null, "cannot mix the new secure password system and the connection string based password"); Debug.Assert(rec.newSecurePassword != null || rec.newPassword != null, "cannot have both new secure change password and string based change password"); Debug.Assert(TdsEnums.MAXLEN_APPNAME>=rec.applicationName.Length, "_applicationName.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_SERVERNAME>=rec.serverName.Length, "_dataSource.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_LANGUAGE>=rec.language.Length, "_currentLanguage .Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_DATABASE>=rec.database.Length, "_initialCatalog.Length exceeds the max length for this value"); Debug.Assert(TdsEnums.MAXLEN_ATTACHDBFILE>=rec.attachDBFilename.Length, "_attachDBFileName.Length exceeds the max length for this value"); Debug.Assert(_connHandler != null, "SqlConnectionInternalTds handler can not be null at this point."); _connHandler.TimeoutErrorInternal.EndPhase(SqlConnectionTimeoutErrorPhase.LoginBegin); _connHandler.TimeoutErrorInternal.SetAndBeginPhase(SqlConnectionTimeoutErrorPhase.ProcessConnectionAuth); // get the password up front to use in sspi logic below byte[] encryptedPassword = null; byte[] encryptedChangePassword = null; int encryptedPasswordLengthInBytes; int encryptedChangePasswordLengthInBytes; bool useFeatureExt = (requestedFeatures != TdsEnums.FeatureExtension.None); string userName; if (rec.credential != null) { userName = rec.credential.UserId; encryptedPasswordLengthInBytes = rec.credential.Password.Length * 2; } else { userName = rec.userName; encryptedPassword = TdsParserStaticMethods.EncryptPassword(rec.password); encryptedPasswordLengthInBytes = encryptedPassword.Length; // password in clear text is already encrypted and its length is in byte } if (rec.newSecurePassword != null) { encryptedChangePasswordLengthInBytes = rec.newSecurePassword.Length * 2; } else { encryptedChangePassword = TdsParserStaticMethods.EncryptPassword(rec.newPassword); encryptedChangePasswordLengthInBytes = encryptedChangePassword.Length; } // set the message type _physicalStateObj._outputMessageType = TdsEnums.MT_LOGIN7; // length in bytes int length = TdsEnums.YUKON_LOG_REC_FIXED_LEN; string clientInterfaceName = TdsEnums.SQL_PROVIDER_NAME; Debug.Assert(TdsEnums.MAXLEN_CLIENTINTERFACE >= clientInterfaceName.Length, "cchCltIntName can specify at most 128 unicode characters. See Tds spec"); // add up variable-len portions (multiply by 2 for byte len of char strings) // checked { length += (rec.hostName.Length + rec.applicationName.Length + rec.serverName.Length + clientInterfaceName.Length + rec.language.Length + rec.database.Length + rec.attachDBFilename.Length) * 2; if (useFeatureExt) { length += 4; } } // allocate memory for SSPI variables byte[] outSSPIBuff = null; UInt32 outSSPILength = 0; // only add lengths of password and username if not using SSPI or requesting federated authentication info if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) { checked { length += (userName.Length * 2) + encryptedPasswordLengthInBytes + encryptedChangePasswordLengthInBytes; } } else { if (rec.useSSPI) { // now allocate proper length of buffer, and set length outSSPIBuff = new byte[s_maxSSPILength]; outSSPILength = s_maxSSPILength; // Call helper function for SSPI data and actual length. // Since we don't have SSPI data from the server, send null for the // byte[] buffer and 0 for the int length. Debug.Assert(SniContext.Snix_Login==_physicalStateObj.SniContext, String.Format((IFormatProvider)null, "Unexpected SniContext. Expecting Snix_Login, actual value is '{0}'", _physicalStateObj.SniContext)); _physicalStateObj.SniContext = SniContext.Snix_LoginSspi; SSPIData(null, 0, outSSPIBuff, ref outSSPILength); if (outSSPILength > Int32.MaxValue) { throw SQL.InvalidSSPIPacketSize(); // SqlBu 332503 } _physicalStateObj.SniContext=SniContext.Snix_Login; checked { length += (Int32)outSSPILength; } } } int feOffset = length; if (useFeatureExt) { checked { if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) { length += WriteSessionRecoveryFeatureRequest(recoverySessionData, false); }; if ((requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0) { Debug.Assert(fedAuthFeatureExtensionData != null, "fedAuthFeatureExtensionData should not null."); length += WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData.Value, write:false); } if ((requestedFeatures & TdsEnums.FeatureExtension.Tce) != 0) { length += WriteTceFeatureRequest (false); } if ((requestedFeatures & TdsEnums.FeatureExtension.GlobalTransactions) != 0) { length += WriteGlobalTransactionsFeatureRequest(false); } length++; // for terminator } } try { WriteInt(length, _physicalStateObj); if (recoverySessionData == null) { WriteInt((TdsEnums.DENALI_MAJOR << 24) | (TdsEnums.DENALI_INCREMENT << 16) | TdsEnums.DENALI_MINOR, _physicalStateObj); } else { WriteUnsignedInt(recoverySessionData._tdsVersion, _physicalStateObj); } WriteInt(rec.packetSize, _physicalStateObj); WriteInt(TdsEnums.CLIENT_PROG_VER, _physicalStateObj); WriteInt(TdsParserStaticMethods.GetCurrentProcessIdForTdsLoginOnly(), _physicalStateObj); //MDAC 84718 WriteInt(0, _physicalStateObj); // connectionID is unused // Log7Flags (DWORD) int log7Flags = 0; /* Current snapshot from TDS spec with the offsets added: 0) fByteOrder:1, // byte order of numeric data types on client 1) fCharSet:1, // character set on client 2) fFloat:2, // Type of floating point on client 4) fDumpLoad:1, // Dump/Load and BCP enable 5) fUseDb:1, // USE notification 6) fDatabase:1, // Initial database fatal flag 7) fSetLang:1, // SET LANGUAGE notification 8) fLanguage:1, // Initial language fatal flag 9) fODBC:1, // Set if client is ODBC driver 10) fTranBoundary:1, // Transaction boundary notification 11) fDelegatedSec:1, // Security with delegation is available 12) fUserType:3, // Type of user 15) fIntegratedSecurity:1, // Set if client is using integrated security 16) fSQLType:4, // Type of SQL sent from client 20) fOLEDB:1, // Set if client is OLEDB driver 21) fSpare1:3, // first bit used for read-only intent, rest unused 24) fResetPassword:1, // set if client wants to reset password 25) fNoNBCAndSparse:1, // set if client does not support NBC and Sparse column 26) fUserInstance:1, // This connection wants to connect to a SQL "user instance" 27) fUnknownCollationHandling:1, // This connection can handle unknown collation correctly. 28) fExtension:1 // Extensions are used 32 - total */ // first byte log7Flags |= TdsEnums.USE_DB_ON << 5; log7Flags |= TdsEnums.INIT_DB_FATAL << 6; log7Flags |= TdsEnums.SET_LANG_ON << 7; // second byte log7Flags |= TdsEnums.INIT_LANG_FATAL << 8; log7Flags |= TdsEnums.ODBC_ON << 9; if (rec.useReplication) { log7Flags |= TdsEnums.REPL_ON << 12; } if (rec.useSSPI) { log7Flags |= TdsEnums.SSPI_ON << 15; } // third byte if (rec.readOnlyIntent) { log7Flags |= TdsEnums.READONLY_INTENT_ON << 21; // read-only intent flag is a first bit of fSpare1 } // 4th one if (!ADP.IsEmpty(rec.newPassword) || (rec.newSecurePassword != null && rec.newSecurePassword.Length != 0)) { log7Flags |= 1 << 24; } if (rec.userInstance) { log7Flags |= 1 << 26; } if (useFeatureExt) { log7Flags |= 1 << 28; } WriteInt(log7Flags, _physicalStateObj); if (Bid.AdvancedOn) { Bid.Trace("<sc.TdsParser.TdsLogin|ADV> %d#, TDS Login7 flags = %d:\n", ObjectID, log7Flags); } WriteInt(0, _physicalStateObj); // ClientTimeZone is not used WriteInt(0, _physicalStateObj); // LCID is unused by server // Start writing offset and length of variable length portions int offset = TdsEnums.YUKON_LOG_REC_FIXED_LEN; // write offset/length pairs // note that you must always set ibHostName since it indicaters the beginning of the variable length section of the login record WriteShort(offset, _physicalStateObj); // host name offset WriteShort(rec.hostName.Length, _physicalStateObj); offset += rec.hostName.Length * 2; // Only send user/password over if not fSSPI or fed auth ADAL... If both user/password and SSPI are in login // rec, only SSPI is used. Confirmed same bahavior as in luxor. if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) { WriteShort(offset, _physicalStateObj); // userName offset WriteShort(userName.Length, _physicalStateObj); offset += userName.Length * 2; // the encrypted password is a byte array - so length computations different than strings WriteShort(offset, _physicalStateObj); // password offset WriteShort(encryptedPasswordLengthInBytes / 2, _physicalStateObj); offset += encryptedPasswordLengthInBytes; } else { // case where user/password data is not used, send over zeros WriteShort(0, _physicalStateObj); // userName offset WriteShort(0, _physicalStateObj); WriteShort(0, _physicalStateObj); // password offset WriteShort(0, _physicalStateObj); } WriteShort(offset, _physicalStateObj); // app name offset WriteShort(rec.applicationName.Length, _physicalStateObj); offset += rec.applicationName.Length * 2; WriteShort(offset, _physicalStateObj); // server name offset WriteShort(rec.serverName.Length, _physicalStateObj); offset += rec.serverName.Length * 2; WriteShort(offset, _physicalStateObj); if (useFeatureExt) { WriteShort(4, _physicalStateObj); // length of ibFeatgureExtLong (which is a DWORD) offset += 4; } else { WriteShort(0, _physicalStateObj); // unused (was remote password ?) } WriteShort(offset, _physicalStateObj); // client interface name offset WriteShort(clientInterfaceName.Length, _physicalStateObj); offset += clientInterfaceName.Length * 2; WriteShort(offset, _physicalStateObj); // language name offset WriteShort(rec.language.Length, _physicalStateObj); offset += rec.language.Length * 2; WriteShort(offset, _physicalStateObj); // database name offset WriteShort(rec.database.Length, _physicalStateObj); offset += rec.database.Length * 2; // if (null == s_nicAddress) s_nicAddress = TdsParserStaticMethods.GetNetworkPhysicalAddressForTdsLoginOnly(); _physicalStateObj.WriteByteArray(s_nicAddress, s_nicAddress.Length, 0); WriteShort(offset, _physicalStateObj); // ibSSPI offset if (rec.useSSPI) { WriteShort((int)outSSPILength, _physicalStateObj); offset += (int)outSSPILength; } else { WriteShort(0, _physicalStateObj); } WriteShort(offset, _physicalStateObj); // DB filename offset WriteShort(rec.attachDBFilename.Length, _physicalStateObj); offset += rec.attachDBFilename.Length * 2; WriteShort(offset, _physicalStateObj); // reset password offset WriteShort(encryptedChangePasswordLengthInBytes / 2, _physicalStateObj); WriteInt(0, _physicalStateObj); // reserved for chSSPI // write variable length portion WriteString(rec.hostName, _physicalStateObj); // if we are using SSPI or fed auth ADAL, do not send over username/password, since we will use SSPI instead // same behavior as Luxor if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) { WriteString(userName, _physicalStateObj); // Cache offset in packet for tracing. _physicalStateObj._tracePasswordOffset = _physicalStateObj._outBytesUsed; _physicalStateObj._tracePasswordLength = encryptedPasswordLengthInBytes; if (rec.credential != null) { _physicalStateObj.WriteSecureString(rec.credential.Password); } else { _physicalStateObj.WriteByteArray(encryptedPassword, encryptedPasswordLengthInBytes, 0); } } WriteString(rec.applicationName, _physicalStateObj); WriteString(rec.serverName, _physicalStateObj); // write ibFeatureExtLong if (useFeatureExt) { WriteInt(feOffset, _physicalStateObj); } WriteString(clientInterfaceName, _physicalStateObj); WriteString(rec.language, _physicalStateObj); WriteString(rec.database, _physicalStateObj); // send over SSPI data if we are using SSPI if (rec.useSSPI) _physicalStateObj.WriteByteArray(outSSPIBuff, (int)outSSPILength, 0); WriteString(rec.attachDBFilename, _physicalStateObj); if (!rec.useSSPI && !(_connHandler._federatedAuthenticationInfoRequested || _connHandler._federatedAuthenticationRequested)) { // Cache offset in packet for tracing. _physicalStateObj._traceChangePasswordOffset = _physicalStateObj._outBytesUsed; _physicalStateObj._traceChangePasswordLength = encryptedChangePasswordLengthInBytes; if (rec.newSecurePassword != null) { _physicalStateObj.WriteSecureString(rec.newSecurePassword); } else { _physicalStateObj.WriteByteArray(encryptedChangePassword, encryptedChangePasswordLengthInBytes, 0); } } if (useFeatureExt) { if ((requestedFeatures & TdsEnums.FeatureExtension.SessionRecovery) != 0) { WriteSessionRecoveryFeatureRequest(recoverySessionData, true); }; if ((requestedFeatures & TdsEnums.FeatureExtension.FedAuth) != 0) { Bid.Trace("<sc.TdsParser.TdsLogin|SEC> Sending federated authentication feature request\n"); Debug.Assert(fedAuthFeatureExtensionData != null, "fedAuthFeatureExtensionData should not null."); WriteFedAuthFeatureRequest(fedAuthFeatureExtensionData.Value, write: true); }; if ((requestedFeatures & TdsEnums.FeatureExtension.Tce) != 0) { WriteTceFeatureRequest (true); }; if ((requestedFeatures & TdsEnums.FeatureExtension.GlobalTransactions) != 0) { WriteGlobalTransactionsFeatureRequest(true); }; _physicalStateObj.WriteByte(0xFF); // terminator } } // try catch (Exception e) { // if (ADP.IsCatchableExceptionType(e)) { // be sure to wipe out our buffer if we started sending stuff _physicalStateObj._outputPacketNumber = 1; // end of message - reset to 1 - per ramas _physicalStateObj.ResetBuffer(); } throw; } _physicalStateObj.WritePacket(TdsEnums.HARDFLUSH); _physicalStateObj.ResetSecurePasswordsInfomation(); // Password information is needed only from Login process; done with writing login packet and should clear information _physicalStateObj._pendingData = true; _physicalStateObj._messageStatus = 0; }// tdsLogin
private void Login(ServerInfo server, TimeoutTimer timeout, string newPassword, SecureString newSecurePassword) { // create a new login record SqlLogin login = new SqlLogin(); // gather all the settings the user set in the connection string or // properties and do the login CurrentDatabase = server.ResolvedDatabaseName; _currentPacketSize = ConnectionOptions.PacketSize; _currentLanguage = ConnectionOptions.CurrentLanguage; int timeoutInSeconds = 0; // If a timeout tick value is specified, compute the timeout based // upon the amount of time left in seconds. if (!timeout.IsInfinite) { long t = timeout.MillisecondsRemaining/1000; if ((long)Int32.MaxValue > t) { timeoutInSeconds = (int)t; } } login.timeout = timeoutInSeconds; login.userInstance = ConnectionOptions.UserInstance; login.hostName = ConnectionOptions.ObtainWorkstationId(); login.userName = ConnectionOptions.UserID; login.password = ConnectionOptions.Password; login.applicationName = ConnectionOptions.ApplicationName; login.language = _currentLanguage; if (!login.userInstance) { // Do not send attachdbfilename or database to SSE primary instance login.database = CurrentDatabase;; login.attachDBFilename = ConnectionOptions.AttachDBFilename; } // VSTS#795621 - Ensure ServerName is Sent During TdsLogin To Enable Sql Azure Connectivity. // Using server.UserServerName (versus ConnectionOptions.DataSource) since TdsLogin requires // serverName to always be non-null. login.serverName = server.UserServerName; login.useReplication = ConnectionOptions.Replication; login.useSSPI = ConnectionOptions.IntegratedSecurity; login.packetSize = _currentPacketSize; login.newPassword = newPassword; login.readOnlyIntent = ConnectionOptions.ApplicationIntent == ApplicationIntent.ReadOnly; login.credential = _credential; if (newSecurePassword != null) { login.newSecurePassword = newSecurePassword; } TdsEnums.FeatureExtension requestedFeatures = TdsEnums.FeatureExtension.None; if (ConnectionOptions.ConnectRetryCount>0) { requestedFeatures |= TdsEnums.FeatureExtension.SessionRecovery; _sessionRecoveryRequested = true; } // If the workflow being used is Active Directory Password or Active Directory Integrated and server's prelogin response // for FEDAUTHREQUIRED option indicates Federated Authentication is required, we have to insert FedAuth Feature Extension // in Login7, indicating the intent to use Active Directory Authentication Library for SQL Server. if (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryPassword || (ConnectionOptions.Authentication == SqlAuthenticationMethod.ActiveDirectoryIntegrated && _fedAuthRequired)) { requestedFeatures |= TdsEnums.FeatureExtension.FedAuth; _federatedAuthenticationInfoRequested = true; _fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData { libraryType = TdsEnums.FedAuthLibrary.ADAL, authentication = ConnectionOptions.Authentication, fedAuthRequiredPreLoginResponse = _fedAuthRequired }; } if (_accessTokenInBytes != null) { requestedFeatures |= TdsEnums.FeatureExtension.FedAuth; _fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData { libraryType = TdsEnums.FedAuthLibrary.SecurityToken, fedAuthRequiredPreLoginResponse = _fedAuthRequired, accessToken = _accessTokenInBytes }; // No need any further info from the server for token based authentication. So set _federatedAuthenticationRequested to true _federatedAuthenticationRequested = true; } // The TCE and GLOBALTRANSACTIONS feature are implicitly requested requestedFeatures |= TdsEnums.FeatureExtension.Tce | TdsEnums.FeatureExtension.GlobalTransactions; _parser.TdsLogin(login, requestedFeatures, _recoverySessionData, _fedAuthFeatureExtensionData); }
internal int WriteFedAuthFeatureRequest(FederatedAuthenticationFeatureExtensionData fedAuthFeatureData, bool write /* if false just calculates the length */) { Debug.Assert(fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.ADAL || fedAuthFeatureData.libraryType == TdsEnums.FedAuthLibrary.SecurityToken, "only fed auth library type ADAL and Security Token are supported in writing feature request"); int dataLen = 0; int totalLen = 0; // set dataLen and totalLen switch (fedAuthFeatureData.libraryType) { case TdsEnums.FedAuthLibrary.ADAL: dataLen = 2; // length of feature data = 1 byte for library and echo + 1 byte for workflow break; case TdsEnums.FedAuthLibrary.SecurityToken: Debug.Assert(fedAuthFeatureData.accessToken != null, "AccessToken should not be null."); dataLen = 1 + sizeof(int) + fedAuthFeatureData.accessToken.Length; // length of feature data = 1 byte for library and echo, security token length and sizeof(int) for token lengh itself break; default: Debug.Assert(false, "Unrecognized library type for fedauth feature extension request"); break; } totalLen = dataLen + 5; // length of feature id (1 byte), data length field (4 bytes), and feature data (dataLen) // write feature id if (write) { _physicalStateObj.WriteByte(TdsEnums.FEATUREEXT_FEDAUTH); // set options byte options = 0x00; // set upper 7 bits of options to indicate fed auth library type switch (fedAuthFeatureData.libraryType) { case TdsEnums.FedAuthLibrary.ADAL: Debug.Assert(_connHandler._federatedAuthenticationInfoRequested == true, "_federatedAuthenticationInfoRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_ADAL << 1; break; case TdsEnums.FedAuthLibrary.SecurityToken: Debug.Assert(_connHandler._federatedAuthenticationRequested == true, "_federatedAuthenticationRequested field should be true"); options |= TdsEnums.FEDAUTHLIB_SECURITYTOKEN << 1; break; default: Debug.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request"); break; } options |= (byte)(fedAuthFeatureData.fedAuthRequiredPreLoginResponse == true ? 0x01 : 0x00); // write dataLen and options WriteInt(dataLen, _physicalStateObj); _physicalStateObj.WriteByte(options); // write workflow for FedAuthLibrary.ADAL // write accessToken for FedAuthLibrary.SecurityToken switch (fedAuthFeatureData.libraryType) { case TdsEnums.FedAuthLibrary.ADAL: byte workflow = 0x00; switch (fedAuthFeatureData.authentication) { case SqlAuthenticationMethod.ActiveDirectoryPassword: workflow = TdsEnums.ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD; break; case SqlAuthenticationMethod.ActiveDirectoryIntegrated: workflow = TdsEnums.ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED; break; default: Debug.Assert(false, "Unrecognized Authentication type for fedauth ADAL request"); break; } _physicalStateObj.WriteByte(workflow); break; case TdsEnums.FedAuthLibrary.SecurityToken: WriteInt(fedAuthFeatureData.accessToken.Length, _physicalStateObj); _physicalStateObj.WriteByteArray(fedAuthFeatureData.accessToken, fedAuthFeatureData.accessToken.Length, 0); break; default: Debug.Assert(false, "Unrecognized FedAuthLibrary type for feature extension request"); break; } } return totalLen; }