1 /* 2 * PROJECT: ReactOS Kernel 3 * LICENSE: GPL - See COPYING in the top level directory 4 * FILE: ntoskrnl/lpc/port.c 5 * PURPOSE: Local Procedure Call: Port Management 6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org) 7 */ 8 9 /* INCLUDES ******************************************************************/ 10 11 #include <ntoskrnl.h> 12 #define NDEBUG 13 #include <debug.h> 14 15 /* GLOBALS *******************************************************************/ 16 17 POBJECT_TYPE LpcPortObjectType, LpcWaitablePortObjectType; 18 ULONG LpcpMaxMessageSize; 19 PAGED_LOOKASIDE_LIST LpcpMessagesLookaside; 20 KGUARDED_MUTEX LpcpLock; 21 ULONG LpcpTraceLevel = 0; 22 ULONG LpcpNextMessageId = 1, LpcpNextCallbackId = 1; 23 24 static GENERIC_MAPPING LpcpPortMapping = 25 { 26 READ_CONTROL | PORT_CONNECT, 27 DELETE | PORT_CONNECT, 28 0, 29 PORT_ALL_ACCESS 30 }; 31 32 /* PRIVATE FUNCTIONS *********************************************************/ 33 34 CODE_SEG("INIT") 35 BOOLEAN 36 NTAPI 37 LpcInitSystem(VOID) 38 { 39 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer; 40 UNICODE_STRING Name; 41 42 /* Setup the LPC Lock */ 43 KeInitializeGuardedMutex(&LpcpLock); 44 45 /* Create the Port Object Type */ 46 RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer)); 47 RtlInitUnicodeString(&Name, L"Port"); 48 ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer); 49 ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(LPCP_NONPAGED_PORT_QUEUE); 50 ObjectTypeInitializer.DefaultPagedPoolCharge = FIELD_OFFSET(LPCP_PORT_OBJECT, WaitEvent); 51 ObjectTypeInitializer.GenericMapping = LpcpPortMapping; 52 ObjectTypeInitializer.PoolType = PagedPool; 53 ObjectTypeInitializer.UseDefaultObject = TRUE; 54 ObjectTypeInitializer.CloseProcedure = LpcpClosePort; 55 ObjectTypeInitializer.DeleteProcedure = LpcpDeletePort; 56 ObjectTypeInitializer.ValidAccessMask = PORT_ALL_ACCESS; 57 ObjectTypeInitializer.InvalidAttributes = OBJ_VALID_ATTRIBUTES & ~OBJ_CASE_INSENSITIVE; 58 ObCreateObjectType(&Name, 59 &ObjectTypeInitializer, 60 NULL, 61 &LpcPortObjectType); 62 63 /* Create the Waitable Port Object Type */ 64 RtlInitUnicodeString(&Name, L"WaitablePort"); 65 ObjectTypeInitializer.PoolType = NonPagedPool; 66 ObjectTypeInitializer.DefaultNonPagedPoolCharge += sizeof(LPCP_PORT_OBJECT); 67 ObjectTypeInitializer.DefaultPagedPoolCharge = 0; 68 ObjectTypeInitializer.UseDefaultObject = FALSE; 69 ObCreateObjectType(&Name, 70 &ObjectTypeInitializer, 71 NULL, 72 &LpcWaitablePortObjectType); 73 74 /* Allocate the LPC lookaside list */ 75 LpcpMaxMessageSize = LPCP_MAX_MESSAGE_SIZE; 76 ExInitializePagedLookasideList(&LpcpMessagesLookaside, 77 NULL, 78 NULL, 79 0, 80 LpcpMaxMessageSize, 81 'McpL', 82 32); 83 84 /* We're done */ 85 return TRUE; 86 } 87 88 BOOLEAN 89 NTAPI 90 LpcpValidateClientPort( 91 PETHREAD ClientThread, 92 PLPCP_PORT_OBJECT Port) 93 { 94 PLPCP_PORT_OBJECT ThreadPort; 95 96 /* Get the thread's port */ 97 ThreadPort = LpcpGetPortFromThread(ClientThread); 98 if (ThreadPort == NULL) 99 { 100 return FALSE; 101 } 102 103 /* Check if the port matches directly */ 104 if ((Port == ThreadPort) || 105 (Port == ThreadPort->ConnectionPort) || 106 (Port == ThreadPort->ConnectedPort)) 107 { 108 return TRUE; 109 } 110 111 /* Check if this is a communication port and the connection port matches */ 112 if (((Port->Flags & LPCP_PORT_TYPE_MASK) == LPCP_COMMUNICATION_PORT) && 113 (Port->ConnectionPort == ThreadPort)) 114 { 115 return TRUE; 116 } 117 118 return FALSE; 119 } 120 121 122 /* PUBLIC FUNCTIONS **********************************************************/ 123 124 NTSTATUS 125 NTAPI 126 NtImpersonateClientOfPort(IN HANDLE PortHandle, 127 IN PPORT_MESSAGE ClientMessage) 128 { 129 NTSTATUS Status; 130 KPROCESSOR_MODE PreviousMode = KeGetPreviousMode(); 131 CLIENT_ID ClientId; 132 ULONG MessageId; 133 PLPCP_PORT_OBJECT Port = NULL, ConnectedPort = NULL; 134 PETHREAD ClientThread = NULL; 135 SECURITY_CLIENT_CONTEXT ClientContext; 136 137 PAGED_CODE(); 138 139 /* Check if the call comes from user mode */ 140 if (PreviousMode != KernelMode) 141 { 142 _SEH2_TRY 143 { 144 ProbeForRead(ClientMessage, sizeof(*ClientMessage), sizeof(PVOID)); 145 ClientId = ((volatile PORT_MESSAGE*)ClientMessage)->ClientId; 146 MessageId = ((volatile PORT_MESSAGE*)ClientMessage)->MessageId; 147 } 148 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) 149 { 150 _SEH2_YIELD(return _SEH2_GetExceptionCode()); 151 } 152 _SEH2_END; 153 } 154 else 155 { 156 ClientId = ClientMessage->ClientId; 157 MessageId = ClientMessage->MessageId; 158 } 159 160 /* Reference the port handle */ 161 Status = ObReferenceObjectByHandle(PortHandle, 162 PORT_ALL_ACCESS, 163 LpcPortObjectType, 164 PreviousMode, 165 (PVOID*)&Port, 166 NULL); 167 if (!NT_SUCCESS(Status)) 168 { 169 DPRINT1("Failed to reference port handle: 0x%ls\n", Status); 170 return Status; 171 } 172 173 /* Make sure this is a connection port */ 174 if ((Port->Flags & LPCP_PORT_TYPE_MASK) != LPCP_COMMUNICATION_PORT) 175 { 176 /* It isn't, fail */ 177 DPRINT1("Port is not a communication port\n"); 178 Status = STATUS_INVALID_PORT_HANDLE; 179 goto Cleanup; 180 } 181 182 /* Look up the client thread */ 183 Status = PsLookupProcessThreadByCid(&ClientId, NULL, &ClientThread); 184 if (!NT_SUCCESS(Status)) 185 { 186 DPRINT1("Failed to lookup client thread: 0x%ls\n", Status); 187 goto Cleanup; 188 } 189 190 /* Acquire the lock */ 191 KeAcquireGuardedMutex(&LpcpLock); 192 193 /* Get the connected port and try to reference it */ 194 ConnectedPort = Port->ConnectedPort; 195 if ((ConnectedPort == NULL) || !ObReferenceObjectSafe(ConnectedPort)) 196 { 197 DPRINT1("Failed to reference the connected port\n"); 198 ConnectedPort = NULL; 199 Status = STATUS_PORT_DISCONNECTED; 200 goto CleanupWithLock; 201 } 202 203 /* Check for no-impersonation flag */ 204 if ((ULONG_PTR)ClientThread->LpcReplyMessage & LPCP_THREAD_FLAG_NO_IMPERSONATION) 205 { 206 DPRINT1("Reply message has no impersonation flag set\n"); 207 Status = STATUS_ACCESS_DENIED; 208 goto CleanupWithLock; 209 } 210 211 /* Check for message id mismatch */ 212 if ((ClientThread->LpcReplyMessageId != MessageId) || (MessageId == 0)) 213 { 214 DPRINT1("LpcReplyMessageId mismatch: 0x%lx/0x%lx.\n", 215 ClientThread->LpcReplyMessageId, MessageId); 216 Status = STATUS_REPLY_MESSAGE_MISMATCH; 217 goto CleanupWithLock; 218 } 219 220 /* Validate the port */ 221 if (!LpcpValidateClientPort(ClientThread, Port)) 222 { 223 DPRINT1("LpcpValidateClientPort failed\n"); 224 Status = STATUS_REPLY_MESSAGE_MISMATCH; 225 goto CleanupWithLock; 226 } 227 228 /* Release the lock */ 229 KeReleaseGuardedMutex(&LpcpLock); 230 231 /* Check if security is static */ 232 if (!(ConnectedPort->Flags & LPCP_SECURITY_DYNAMIC)) 233 { 234 /* Use the static security for impersonation */ 235 Status = SeImpersonateClientEx(&ConnectedPort->StaticSecurity, NULL); 236 goto Cleanup; 237 } 238 239 /* Create new dynamic security */ 240 Status = SeCreateClientSecurity(ClientThread, 241 &ConnectedPort->SecurityQos, 242 FALSE, 243 &ClientContext); 244 if (!NT_SUCCESS(Status)) 245 { 246 DPRINT1("SeCreateClientSecurity failed\n"); 247 goto Cleanup; 248 } 249 250 /* Use dynamic security for impersonation */ 251 Status = SeImpersonateClientEx(&ClientContext, NULL); 252 253 /* Get rid of the security context */ 254 SeDeleteClientSecurity(&ClientContext); 255 256 Cleanup: 257 258 if (ConnectedPort != NULL) 259 ObDereferenceObject(ConnectedPort); 260 261 if (ClientThread != NULL) 262 ObDereferenceObject(ClientThread); 263 264 ObDereferenceObject(Port); 265 266 return Status; 267 268 CleanupWithLock: 269 270 /* Release the lock */ 271 KeReleaseGuardedMutex(&LpcpLock); 272 goto Cleanup; 273 } 274 275 NTSTATUS 276 NTAPI 277 NtQueryPortInformationProcess(VOID) 278 { 279 /* This is all this function does */ 280 return STATUS_UNSUCCESSFUL; 281 } 282 283 NTSTATUS 284 NTAPI 285 NtQueryInformationPort(IN HANDLE PortHandle, 286 IN PORT_INFORMATION_CLASS PortInformationClass, 287 OUT PVOID PortInformation, 288 IN ULONG PortInformationLength, 289 OUT PULONG ReturnLength) 290 { 291 UNIMPLEMENTED; 292 return STATUS_NOT_IMPLEMENTED; 293 } 294 295 /* EOF */ 296