Connection Failure with MSSQL: Special Character Password
Summary
I'm encountering a connection issue when attempting to tiberius to connect to a Microsoft SQL Server. This issue does not arise when connecting via php or sqlcmd with the same credentials.
Detailed Description
I have successfully connected to the MSSQL server using both php and sqlcmd commands, indicating that the server is accessible and the credentials are correct. The connection details include a password that contains the special character $ followed by numbers, which I suspect might be related to the issue. Here are examples of the successful connection attempts for reference:
- Using
sqlcmd:
sqlcmd -S "sql.foo.com,999" -d "Database" -u "User" -P "Password`$1337"
- Using
php:
// PHP 7.2, ODBC 17
$conn=odbc_connect(Server=sql.foo.com,999;Database=Database","User","Password$1337");
However, attempting the same connection using this library:
use anyhow::Result;
use tiberius::Client;
use tiberius::Config;
use tokio::net::TcpStream;
use tokio_util::compat::TokioAsyncWriteCompatExt;
const CONNECTION_STRING: &str = r"Server=sql.foo.com,999;Database=Database;UID='User';PWD='Password$1337'";
#[tokio::main]
async fn main() -> Result<()> {
let mut config = Config::from_ado_string(CONNECTION_STRING)?;
config.trust_cert();
let tcp = TcpStream::connect(config.get_addr()).await?;
tcp.set_nodelay(true)?;
let _ = Client::connect(config, tcp.compat_write()).await?;
Ok(())
}
Fails with the following error message:
Error: Token error: 'Login failed for user 'User'.' on server SERVER executing on line 1 (code: 18456, state: 1, class: 14)
Environment
Server: Microsoft SQL Server 2019 (RTM-CU19) (KB5023049 - 15.0.4298.1 (X64))
Issue Analysis
The error suggests a login failure, which is peculiar given that the same credentials work with other methods. The inclusion of a special character in the password and its handling within the Rust environment might be contributing factors but I'm not sure.
Does it work if you urlencode the special character?
By hand:
use anyhow::Result;
use tiberius::Client;
use tiberius::Config;
use tokio::net::TcpStream;
use tokio_util::compat::TokioAsyncWriteCompatExt;
const CONNECTION_STRING: &str = r"Server=sql.foo.com,999;Database=Database;UID='User';PWD='Password%241337'";
#[tokio::main]
async fn main() -> Result<()> {
let mut config = Config::from_ado_string(CONNECTION_STRING)?;
config.trust_cert();
let tcp = TcpStream::connect(config.get_addr()).await?;
tcp.set_nodelay(true)?;
let _ = Client::connect(config, tcp.compat_write()).await?;
Ok(())
}
It does not.
Update: By using wireshark and setting encryption to NotSupported I can confirm that the login packet contains the correct password. I can't compare packets because SqlCmd does encrypt the login.
What is interesting is that SqlCmd uses an older version of the pre login message.
SqlCmd uses version: 0.0.1537 Tiberius uses version: 0.2.3072
Should this be a cause for concern?