Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

832 lines
23 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
userkey.c
Abstract:
Implentation of the functions that get and generate user session keys
RtlCalculateUserSessionKeyLm
RtlCalculateUserSessionKeyNt
RtlGetUserSessionKeyClient
RtlGetUserSessionKeyServer
Author:
David Chalmers (Davidc) 10-21-91
Revision History:
--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntddnfs.h>
#include <rpc.h>
#include <rpcndr.h>
#include <crypt.h>
#include <srvfsctl.h> // Server definitions
#include <status.h> // Server return codes
//
// Define this if you want to know all about user session keys
//
// #define DEBUG_USER_SESSION_KEYS
#define REDIRECTOR_DEVICENAME L"\\Device\\LanmanRedirector\\"
#define REDIRECTOR_IPC_FILENAME L"\\IPC$"
//
// Define the user session key to be used for local connections
// Make sure the initial data fills the structure completely !
//
USER_SESSION_KEY LocalSessionKey = { 'S', 'y', 's', 't', 'e', 'm', 'L', 'i',
'b', 'r', 'a', 'r', 'y', 'D', 'T', 'C'
};
//
// Define the user session key that represents an error.
// This value will be generated by other parts of the system on failure.
// We will check for it in our query code and return an error if it's found.
//
USER_SESSION_KEY ErrorSessionKey = { 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
NTSTATUS
RtlCalculateUserSessionKeyLm(
IN PLM_RESPONSE LmResponse,
IN PLM_OWF_PASSWORD LmOwfPassword,
OUT PUSER_SESSION_KEY UserSessionKey)
/*++
Routine Description:
Takes the passed Response and OwfPassword and generates a UserSessionKey.
The current implementation takes the one-way-function of the OwfPassword
and returns this as the key.
Arguments:
LmResponse - The response sent during session setup.
LmOwfPassword - The hashed version of the user's password.
Return Values:
STATUS_SUCCESS - The function was completed successfully.
The UserSessionKey is in UserSessionKey.
STATUS_UNSUCCESSFUL - Something failed. The UserSessionKey is undefined.
--*/
{
NTSTATUS Status;
NT_PASSWORD NtPassword;
//
// Make the Owf password look like an NT password
//
NtPassword.Buffer = (PWSTR)LmOwfPassword; // We can do this cast because we
// know the OWF routine treats this
// pointer as a byte pointer.
NtPassword.Length = sizeof(*LmOwfPassword);
NtPassword.MaximumLength = sizeof(*LmOwfPassword);
//
// Calculate the OWF of the OwfPassword
//
ASSERT(sizeof(NT_OWF_PASSWORD) == sizeof(*UserSessionKey));
Status = RtlCalculateNtOwfPassword( &NtPassword,
(PNT_OWF_PASSWORD)UserSessionKey
);
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlCalculateUserSessionKeyLm : OWF calculation failed, status = 0x%lx\n", Status));
return(Status);
}
//
// Check if we've generated the error session key
//
if (RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
sizeof(*UserSessionKey)) == sizeof(*UserSessionKey)) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlCalculateSessionKeyLm - generated error session key, modifying it\n"));
#endif
//
// Move away from the error session key
//
UserSessionKey->data[0].data[0] ++;
ASSERT(RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
sizeof(*UserSessionKey)) != sizeof(*UserSessionKey));
}
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlCalculateUserSessionKeyLm : Key = 0x%lx : %lx : %lx : %lx\n",
((PULONG)UserSessionKey)[0], ((PULONG)UserSessionKey)[1],
((PULONG)UserSessionKey)[2], ((PULONG)UserSessionKey)[3]));
#endif
return(STATUS_SUCCESS);
UNREFERENCED_PARAMETER(LmResponse);
}
NTSTATUS
RtlCalculateUserSessionKeyNt(
IN PNT_RESPONSE NtResponse,
IN PNT_OWF_PASSWORD NtOwfPassword,
OUT PUSER_SESSION_KEY UserSessionKey)
/*++
Routine Description:
Takes the passed Response and OwfPassword and generates a UserSessionKey.
Arguments:
NtResponse - The response sent during session setup.
NtOwfPassword - The hashed version of the user's password.
Return Values:
STATUS_SUCCESS - The function was completed successfully.
The UserSessionKey is in UserSessionKey.
STATUS_UNSUCCESSFUL - Something failed. The UserSessionKey is undefined.
--*/
{
// Just call the LM version
ASSERT(sizeof(NT_RESPONSE) == sizeof(LM_RESPONSE));
ASSERT(sizeof(NT_OWF_PASSWORD) == sizeof(LM_OWF_PASSWORD));
return(RtlCalculateUserSessionKeyLm((PLM_RESPONSE)NtResponse,
(PLM_OWF_PASSWORD)NtOwfPassword,
UserSessionKey));
}
NTSTATUS
RtlGetUserSessionKeyClient(
IN PVOID RpcContextHandle,
OUT PUSER_SESSION_KEY UserSessionKey)
/*++
Routine Description:
Returns the user session key associated with an rpc connection.
This function should be called by the client side of the connection only.
Arguments:
RpcContextHandle - The rpc connection we're interested in
UserSessionKey - The user session key is returned here
Return Values:
STATUS_SUCCESS - The function was completed successfully.
The UserSessionKey is in UserSessionKey.
STATUS_LOCAL_USER_SESSION_KEY - An informational status value.
- The rpc connection is local, the usersessionkey returned
- is constant and not unique to this connection.
- There is little to be gained by encrypting data over
- this connection
STATUS_NO_USER_SESSION_KEY - No session key exists for this session.
------ these come from parsebinding -------
RPC_NT_OUT_OF_MEMORY - Insufficent memory is available to allocate
space for the fields of the string binding.
RPC_NT_INVALID_STRING_BINDING - The string binding is syntactically
invalid.
RPC_NT_INVALID_ARG - The string binding is not specified
(ie. ARGUMENT_PRESENT(StringBinding) is false).
--*/
{
NTSTATUS Status, IgnoreStatus;
RPC_BINDING_HANDLE RpcBindingHandle;
WCHAR *StringBinding;
WCHAR *ServerNameZ;
WCHAR *BareServerNameZ; // Points to server name minus leading '\'s
OBJECT_ATTRIBUTES Attributes;
UNICODE_STRING ServerName;
UNICODE_STRING RedirDevice;
UNICODE_STRING IpcFileName;
UNICODE_STRING ServerIpcFileName;
USHORT LengthRequired;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE RedirHandle;
LMR_REQUEST_PACKET RdrRequestPacket;
LMR_CONNECTION_INFO_2 ConnectionInfo;
//
// Get the binding handle for this connection
//
RpcBindingHandle = NDRCContextBinding((NDR_CCONTEXT)RpcContextHandle);
//
// Get the string description of the binding from the rpc handle
//
Status = (NTSTATUS)I_RpcMapWin32Status(
RpcBindingToStringBindingW(RpcBindingHandle, &StringBinding));
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyClient - failed to get stringbinding, Status = 0x%lx\n\r", Status));
ASSERT(FALSE);
return(Status);
}
//
// Parse the stringbinding to get the server name
//
Status = (NTSTATUS)I_RpcMapWin32Status(RpcStringBindingParseW(
StringBinding,
NULL, // object uid
NULL, // protseq !
&ServerNameZ, // network address
NULL, // endpoint
NULL // network options
));
//
// We're finished with the string binding
//
IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&StringBinding));
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Check the result of binding parse
//
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyClient - failed to parse stringbinding, status = 0x%lx\n\r", Status));
ASSERT(FALSE);
return(Status);
}
//
// Check for a local connection
//
if ( (ServerNameZ == NULL) || (ServerNameZ[0] == UNICODE_NULL) ) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyClient - server name is NULL, returning local key\n"));
#endif
//
// Use a constant, default session key
//
*UserSessionKey = LocalSessionKey;
IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
ASSERT(NT_SUCCESS(IgnoreStatus));
return(STATUS_LOCAL_USER_SESSION_KEY);
}
//
// Strip the leading '\'s from the server name
//
BareServerNameZ = ServerNameZ;
while (*BareServerNameZ == L'\\') {
BareServerNameZ ++;
}
//
// Set up a counted string for out server name
//
RtlInitUnicodeString(&ServerName, BareServerNameZ);
//
// Check for the local server name '.'
//
if ( (ServerName.Length == sizeof(*ServerName.Buffer)) &&
(ServerName.Buffer[0] == L'.') ) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyClient - server name is '.', returning local key\n"));
#endif
//
// Use a constant, default session key
//
*UserSessionKey = LocalSessionKey;
IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
ASSERT(NT_SUCCESS(IgnoreStatus));
return(STATUS_LOCAL_USER_SESSION_KEY);
}
//
// Create a redirector ipc file name for the referenced server
//
RtlInitUnicodeString(&RedirDevice, REDIRECTOR_DEVICENAME);
RtlInitUnicodeString(&IpcFileName, REDIRECTOR_IPC_FILENAME);
LengthRequired = RedirDevice.Length + ServerName.Length + IpcFileName.Length;
//
// Allocate space for the ipc file name we will create
//
ServerIpcFileName.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, LengthRequired);
if (ServerIpcFileName.Buffer == NULL) {
KdPrint(("RtlGetUserSessionKeyClient - failed to allocate space for server name (%d bytes)\n", LengthRequired));
IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
ASSERT(NT_SUCCESS(IgnoreStatus));
return(STATUS_INSUFFICIENT_RESOURCES);
}
ServerIpcFileName.Length = 0;
ServerIpcFileName.MaximumLength = LengthRequired;
//
// ServerIpcFileName = \Device\LanmanRedirector\ + servername + \ipc$
//
RtlCopyUnicodeString(&ServerIpcFileName, &RedirDevice);
IgnoreStatus = RtlAppendUnicodeStringToString(&ServerIpcFileName, &ServerName);
ASSERT(NT_SUCCESS(IgnoreStatus));
IgnoreStatus = RtlAppendUnicodeStringToString(&ServerIpcFileName, &IpcFileName);
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Don't need the server name any more
//
IgnoreStatus = I_RpcMapWin32Status(RpcStringFreeW(&ServerNameZ));
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Open up the redirector ipc file
//
InitializeObjectAttributes( &Attributes,
&ServerIpcFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenFile( &RedirHandle,
FILE_READ_DATA, // access required to get connection info
&Attributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_CREATE_TREE_CONNECTION );
//
// We're finished with the ipc filename
//
RtlFreeHeap( RtlProcessHeap(), 0, ServerIpcFileName.Buffer );
ServerIpcFileName.Buffer = NULL;
//
// Check the result of the open
//
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyClient - failed to open redirector, status = 0x%lx\n\r", Status));
ASSERT(FALSE);
return(Status);
}
//
// Get the connection info for this link
//
RdrRequestPacket.Version = REQUEST_PACKET_VERSION;
RdrRequestPacket.Level = 2; // We want the session key.
Status = NtFsControlFile( RedirHandle,
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock,
FSCTL_LMR_GET_CONNECTION_INFO,
&RdrRequestPacket, // Input buffer
sizeof(RdrRequestPacket), // Input buffer length
&ConnectionInfo, // Output buffer
sizeof(ConnectionInfo) // Output buffer length
);
//
// We're done with the redirector handle
//
IgnoreStatus = NtClose(RedirHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Check the result of the control file call
//
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyClient - failed to get connection info, status = 0x%lx\n\r", Status));
ASSERT(FALSE);
return(Status);
}
//
// Copy the session key into the passed buffer
//
*UserSessionKey = *(PUSER_SESSION_KEY)(ConnectionInfo.UserSessionKey);
//
// Check for the error session key
//
if (RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
sizeof(*UserSessionKey)) == sizeof(*UserSessionKey)) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyClient - got error session key, returning error\n"));
#endif
Status = STATUS_NO_USER_SESSION_KEY;
}
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyClient : Key = 0x%lx : %lx : %lx : %lx\n",
((PULONG)UserSessionKey)[0], ((PULONG)UserSessionKey)[1],
((PULONG)UserSessionKey)[2], ((PULONG)UserSessionKey)[3]));
#endif
return(Status);
}
NTSTATUS
RtlGetUserSessionKeyServer(
IN PVOID RpcContextHandle OPTIONAL,
OUT PUSER_SESSION_KEY UserSessionKey)
/*++
Routine Description:
Returns the user session key associated with an rpc connection.
This function should be called by the server side of the connection only.
Arguments:
RpcBindingHandle - The rpc connection we're interested in
- Note this parameter is ignored for now
UserSessionKey - The user session key is returned here
Return Values:
STATUS_SUCCESS - The function was completed successfully.
The UserSessionKey is in UserSessionKey.
STATUS_LOCAL_USER_SESSION_KEY - An informational status value.
- The rpc connection is local, the usersessionkey returned
- is constant and not unique to this connection.
- There is little to be gained by encrypting data over
- this connection
STATUS_NO_USER_SESSION_KEY - No session key exists for this session.
--*/
{
NTSTATUS Status, IgnoreStatus;
HANDLE TokenHandle;
TOKEN_STATISTICS TokenInfo;
ULONG ReturnedLength;
UNICODE_STRING ServerDevice;
ANSI_STRING AnsiString;
OBJECT_ATTRIBUTES Attributes;
IO_STATUS_BLOCK IoStatusBlock;
HANDLE ServerHandle;
RPC_BINDING_HANDLE RpcBindingHandle;
unsigned int RpcClientLocalFlag;
//
// Get the binding handle for this connection
//
// LATER RpcBindingHandle = (RPC_BINDING_HANDLE) RpcContextHandle;
RpcBindingHandle = NULL;
//
// If this is a local connection then we can immediately
// return the local session key.
//
Status = I_RpcBindingIsClientLocal(RpcBindingHandle, &RpcClientLocalFlag);
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyServer: RpcBindingIsClientLocal failed, status = 0x%lx\n", Status));
return(Status);
}
if (RpcClientLocalFlag != 0) {
*UserSessionKey = LocalSessionKey;
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyServer: client is local, returning local key\n"));
#endif
return (STATUS_LOCAL_USER_SESSION_KEY);
}
//
// Get a handle to the client's token
//
Status = NtOpenThreadToken(NtCurrentThread(),
TOKEN_QUERY,
TRUE,
&TokenHandle);
//
// If we couldn't open the thread token because we weren't impersonating
// then impersonate and try again.
//
if (!NT_SUCCESS(Status)) {
//
// Check we failed only because we weren't impersonating
//
if (Status != STATUS_NO_TOKEN) {
KdPrint(("RtlGetUserSessionKeyServer - failed to open thread token, status = 0x%lx\n", Status));
ASSERT(FALSE);
return(Status);
}
//
// Impersonate the client ourselves
//
Status = I_RpcMapWin32Status(RpcImpersonateClient(RpcBindingHandle));
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyServer - RpcImpersonateClient failed, status = 0x%lx\n", Status));
ASSERT(FALSE);
return(Status);
}
//
// Try to get a token handle now we're impersonating
//
Status = NtOpenThreadToken(NtCurrentThread(),
TOKEN_QUERY,
TRUE,
&TokenHandle);
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyServer - failed to open thread token after impersonating, status = 0x%lx\n", Status));
ASSERT(FALSE);
IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf());
ASSERT(NT_SUCCESS(IgnoreStatus));
return(Status);
}
//
// We've got a token handle, stop impersonating
//
Status = I_RpcMapWin32Status(RpcRevertToSelf());
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyServer - RpcRevertToSelf failed, status = 0x%lx\n", Status));
ASSERT(FALSE);
IgnoreStatus = NtClose(TokenHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
return(Status);
}
}
//
// We've now got a token handle, get the authentication id from it.
//
Status = NtQueryInformationToken(
TokenHandle,
TokenStatistics,
&TokenInfo,
sizeof(TokenInfo),
&ReturnedLength
);
//
// We're done with the token
//
IgnoreStatus = NtClose(TokenHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// Check result of token query
//
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyServer - Failed to query token statistics from token, status = 0x%lx\n", Status));
ASSERT(FALSE);
return(Status);
}
//
// Open the server device
//
RtlInitAnsiString(&AnsiString, SERVER_DEVICE_NAME);
Status = RtlAnsiStringToUnicodeString(&ServerDevice, &AnsiString, TRUE);
if (!NT_SUCCESS(Status)) {
KdPrint(("RtlGetUserSessionKeyServer - RtlAnsiToUnicodeString failed, status = 0x%lx\n", Status));
ASSERT(FALSE);
return(Status);
}
InitializeObjectAttributes( &Attributes,
&ServerDevice,
OBJ_CASE_INSENSITIVE,
NULL,
NULL );
Status = NtOpenFile( &ServerHandle,
GENERIC_READ | GENERIC_WRITE, // LATER use correct access
&Attributes,
&IoStatusBlock,
FILE_SHARE_READ,
0 );
RtlFreeUnicodeString(&ServerDevice);
if (!NT_SUCCESS(Status)) {
//
// Check for the case when the server driver is not present
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyServer - server driver not present, returning local key\n"));
#endif
*UserSessionKey = LocalSessionKey;
Status = STATUS_LOCAL_USER_SESSION_KEY;
} else {
KdPrint(("RtlGetUserSessionKeyServer - Failed to open the server, status = 0x%lx\n", Status));
ASSERT(FALSE);
}
return(Status);
}
//
// Get the session key for this client from the server
//
Status = NtFsControlFile( ServerHandle,
NULL, // Event
NULL, // APC
NULL, // APC Context
&IoStatusBlock,
FSCTL_SRV_GET_CHALLENGE,
&TokenInfo.AuthenticationId,
sizeof(TokenInfo.AuthenticationId),
(PVOID)UserSessionKey,
sizeof(*UserSessionKey));
//
// We're done with the file handle
//
IgnoreStatus = NtClose(ServerHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
if (NT_SUCCESS(Status)) {
//
// Check for the error session key
//
if (RtlCompareMemory(UserSessionKey, &ErrorSessionKey,
sizeof(*UserSessionKey)) == sizeof(*UserSessionKey)) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyServer - got error session key, returning error\n"));
#endif
Status = STATUS_NO_USER_SESSION_KEY;
}
} else {
//
// If the server is not started or the token couldn't be found in the
// list of server connections, then assume it's a local connection
//
if ( (Status == STATUS_SERVER_NOT_STARTED) ||
(Status == STATUS_NO_TOKEN) ) {
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyServer - server not started or logon id not found (Status = 0x%lx), returning local key\n", Status));
#endif
*UserSessionKey = LocalSessionKey;
Status = STATUS_LOCAL_USER_SESSION_KEY;
} else {
KdPrint(("RtlGetUserSessionKeyServer - Failed to query the user session key from the server, status = 0x%lx\n", Status));
ASSERT(FALSE);
}
}
#ifdef DEBUG_USER_SESSION_KEYS
KdPrint(("RtlGetUserSessionKeyServer : Key = 0x%lx : %lx : %lx : %lx, status = 0x%lx\n",
((PULONG)UserSessionKey)[0], ((PULONG)UserSessionKey)[1],
((PULONG)UserSessionKey)[2], ((PULONG)UserSessionKey)[3], Status));
#endif
return(Status);
}