/*++ 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 #include #include #include #include #include #include #include // Server definitions #include // 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); }