I'm trying to get SSPI authentication to play well with two-phase transactions via MSDTC, using PostgreSQL with psqlODBC. I can't figure out how it can work securely - so I'm hoping someone can explain how it works with MS SQL Server.
I'm coming from a PostgreSQL background, so please forgive any major misunderstandings of MS SQL.
The TL;DR is: To complete prepared transaction recovery after an app exits or the server crashes, MSDTC.exe running as NETWORKSERVICE
has to be able to connect to the database and commit prepared transactions on behalf of the original user. When authentication was done over SSPI originally I don't understand how that's possible.
Background: SSPI, DTC, ODBC and XA
A user establishes one or more psqlODBC connections to PostgreSQL, and authenticates to PostgreSQL with SSPI. Windows confirms to PostgreSQL that the requested PostgreSQL username is the same as the current Windows username, and access is granted.
The user binds the connections into a DTC session with the MSDTC transaction manager and begins a distributed transaction (which begins a tx on the connections being managed by MSDTC). When they're done with their work on the connections they request a distributed commit on the DTC transaction. This does a two-phase commit - in PostgreSQL, calling PREPARE TRANSACTION
on both as phase 1, then when phase 1 confirms success calling COMMIT PREPARED
on each as phase 2.
Standard-ish stuff so far, just Microsoft's version of XA.
XA recovery
Part of the point of XA and MSDTC is that if the client crashes after phase 1 commit confirms, the transaction is guaranteed to get committed.
For that to happen something must keep track of outstanding prepared transactions and commit them. That's the job of the MSDTC.exe
, the microsoft distributed transaction co-ordinator daemon.
At least with psqlODBC, when an abandoned prepared transaction is recovered, MSDTC loads the XA DLL for psqlODBC and calls xa_recover
, which makes a connection to PostgreSQL using the credentials of the user who began the DTC transaction. It then calls COMMIT PREPARED
and closes the connection.
MSDTC.exe
runs as NETWORKSERVICE
. That's the where the problem starts, because those stored credentials are not valid for NETWORKSERVICE
if the original connection wasn't also by the user NETWORKSERVICE
. SSPI will reject the connection because of a username mismatch.
How does MS SQL handle this?
MS SQL Server is heavily used with SSPI, and MSDTC is designed in part around the needs of MS SQL Server. So presumably, MS SQL Server has a secure way to complete distributed transaction recovery for a connection that was made using SSPI by a user other than NETWORKSERVICE
even though msdtc.exe
runs as NETWORKSERVICE
.
I don't understand how it could, though.
The obvious options are all insecure, like:
Configure and store credentials in the registry, accessible by
NETWORKSERVICE
, somsdtc.exe
can commit/rollback any user's prepared transactions;Trust
NETWORKSERVICE
to connect to MS-SQL without credentials for the purpose of prepared transaction management;
and in the end, I don't see how MS SQL can prevent other services running as NETWORKSERVICE
from messing with its prepared transactions.
Is this just one of those things where people accept somewhat weaker security? You're expected to reasonably trust everything running as NETWORKSERVICE
and MS SQL just allows MSDTC to connect without additional checks?
Or is there something I'm missing?
The only way I can think of to do this fairly securely (allowing for the inherent limitations of running as NETWORKSERVICE
) is to generate a one-time cookie with each prepared transaction, which cannot be discovered by listing the transactions. Then allow anyone in NETWORKSERVICE
to make a database connection that can do nothing except commit/rollback prepared transactions, and only if it knows the transaction's cookie. That seems awfully fiddly though.
Does MS-SQL just assume that NETWORKSERVICE
is trusted?