What SMB2 needs from a security package
pktmon ships with Windows. These commands capture an SMB2 connection that authenticates with a custom security package:
pktmon filter add -p 445 pktmon start --etw -f C:\temp\capture.etl <reproduce the mount> pktmon stop pktmon pcapng C:\temp\capture.etl -o capture.pcapng pktmon filter remove
capture.pcapng opened in Wireshark: Negotiate Protocol succeeds, the first Session Setup round returns the normal STATUS_MORE_PROCESSING_REQUIRED, then a later round returns Error: STATUS_INVALID_PARAMETER (0xc000000d). The client sends an RST right away and reconnects.STATUS_MORE_PROCESSING_REQUIRED is expected in a multi-round NegoEx exchange. STATUS_INVALID_PARAMETER is not, and it is neither a network nor a credential problem. The security package returned the wrong answer to one query.
In each session setup round, the SMB2 NegoEx fast path calls QueryContextAttributes several times. In kernel mode, that reaches the package's own dispatch (SpGetExtendedInformation/EIDQueryAttributes). The attributes seen in a live trace, most frequent first:
SECPKG_ATTR_SIZES,SECPKG_ATTR_NAMES,SECPKG_ATTR_PACKAGE_INFO,SECPKG_ATTR_NEGOTIATION_INFO,SECPKG_ATTR_USER_FLAGS,SECPKG_ATTR_LOGOFF_TIME: asked every round, mostly bookkeeping.SECPKG_ATTR_SESSION_KEY: asked several times per round. The SMB2 signing key derives from it.SECPKG_ATTR_NEGO_KEYS: server side only.negoexts.dlluses it to check the NegoEx MIC. The client never asks for it and computes its MIC from the outer session key.negoexts!WSTRetrieveSessionKeysaccepts one failure here,STATUS_NOT_FOUND("use the outer key"). Any other error fails the session on the client.SECPKG_ATTR_FLAGSandSECPKG_ATTR_IS_LOOPBACK: see below.
A package that doesn't know one of these attributes still has to answer something. The wrong answer is what broke the session above.
The package's default case for an unknown ContextAttribute was:
default:
Status = SEC_E_INVALID_PARAMETER; // 0x8009035D
Most SSPI code does this for an attribute it doesn't implement. But the tolerance in negoexts.dll (the STATUS_NOT_FOUND case for nego keys, above) is its own logic. The kernel fast path under it doesn't share it. ksecdd.sys and srv2.sys pass the package's SEC_E_INVALID_PARAMETER through unchanged. It reaches the wire as STATUS_INVALID_PARAMETER, and the whole session fails, not just that query.
So an attribute that looks optional still needs a valid answer.
The query behind the capture above is SECPKG_ATTR_IS_LOOPBACK, id 37 (0x25). It is a recent addition to sspi.h and is missing from older copies of the headers, so the package had no case for it and fell into default:. The live trace shows it:
KspQueryAttributes(173) : Enter ContextId = ... attribute 0x25, then EIDQueryAttributes(472) : ContextAttribute=0x00000025 SEC_E_INVALID_PARAMETER. 0x25 is 37, SECPKG_ATTR_IS_LOOPBACK. The next query, SECPKG_ATTR_NAMES (attribute 0x1), returns Status = 0x00000000.The attribute only asks whether the authentication targets the local machine. The fast path still fails the session when the package returns an error. The fix:
case SECPKG_ATTR_IS_LOOPBACK:
// Real struct layout isn't publicly documented; writing just the
// leading BOOLEAN is safe regardless of the caller's actual buffer size.
*((PBOOLEAN) pBuffer) = FALSE;
Status = STATUS_SUCCESS;
break;
The answer is always FALSE: this package only authenticates remote peers, never a loopback connection. With this case in place, the session setup completes.
SMB2 can also hit a kernel crash in ksecpkg!NegoExtsDeleteKernelContext. That one comes from the package lookup, not from an attribute answer. It is covered, with its own capture, in Load a kernel-mode security package without a reboot.