1 /*
2  * NDR data marshalling
3  *
4  * Copyright 2006 Mike McCormack (for CodeWeavers)
5  * Copyright 2006-2007 Robert Shearman (for CodeWeavers)
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include "ndr_misc.h"
23 #include "rpc_assoc.h"
24 #include "rpcndr.h"
25 
26 #include "wine/rpcfc.h"
27 
28 #include "wine/debug.h"
29 #include "wine/list.h"
30 
31 WINE_DEFAULT_DEBUG_CHANNEL(ole);
32 
33 #define NDR_CONTEXT_HANDLE_MAGIC 0x4352444e
34 
35 typedef struct ndr_context_handle
36 {
37     ULONG      attributes;
38     GUID       uuid;
39 } ndr_context_handle;
40 
41 struct context_handle_entry
42 {
43     struct list entry;
44     DWORD magic;
45     RPC_BINDING_HANDLE handle;
46     ndr_context_handle wire_data;
47 };
48 
49 static struct list context_handle_list = LIST_INIT(context_handle_list);
50 
51 static CRITICAL_SECTION ndr_context_cs;
52 static CRITICAL_SECTION_DEBUG ndr_context_debug =
53 {
54     0, 0, &ndr_context_cs,
55     { &ndr_context_debug.ProcessLocksList, &ndr_context_debug.ProcessLocksList },
56       0, 0, { (DWORD_PTR)(__FILE__ ": ndr_context") }
57 };
58 static CRITICAL_SECTION ndr_context_cs = { &ndr_context_debug, -1, 0, 0, 0, 0 };
59 
60 static struct context_handle_entry *get_context_entry(NDR_CCONTEXT CContext)
61 {
62     struct context_handle_entry *che = CContext;
63 
64     if (che->magic != NDR_CONTEXT_HANDLE_MAGIC)
65         return NULL;
66     return che;
67 }
68 
69 static struct context_handle_entry *context_entry_from_guid(LPCGUID uuid)
70 {
71     struct context_handle_entry *che;
72     LIST_FOR_EACH_ENTRY(che, &context_handle_list, struct context_handle_entry, entry)
73         if (IsEqualGUID(&che->wire_data.uuid, uuid))
74             return che;
75     return NULL;
76 }
77 
78 RPC_BINDING_HANDLE WINAPI NDRCContextBinding(NDR_CCONTEXT CContext)
79 {
80     struct context_handle_entry *che;
81     RPC_BINDING_HANDLE handle = NULL;
82 
83     TRACE("%p\n", CContext);
84 
85     EnterCriticalSection(&ndr_context_cs);
86     che = get_context_entry(CContext);
87     if (che)
88         handle = che->handle;
89     LeaveCriticalSection(&ndr_context_cs);
90 
91     if (!handle)
92     {
93         ERR("invalid handle %p\n", CContext);
94         RpcRaiseException(RPC_X_SS_CONTEXT_MISMATCH);
95     }
96     return handle;
97 }
98 
99 void WINAPI NDRCContextMarshall(NDR_CCONTEXT CContext, void *pBuff)
100 {
101     struct context_handle_entry *che;
102 
103     TRACE("%p %p\n", CContext, pBuff);
104 
105     if (CContext)
106     {
107         EnterCriticalSection(&ndr_context_cs);
108         che = get_context_entry(CContext);
109         memcpy(pBuff, &che->wire_data, sizeof (ndr_context_handle));
110         LeaveCriticalSection(&ndr_context_cs);
111     }
112     else
113     {
114         ndr_context_handle *wire_data = pBuff;
115         wire_data->attributes = 0;
116         wire_data->uuid = GUID_NULL;
117     }
118 }
119 
120 /***********************************************************************
121  *           RpcSmDestroyClientContext [RPCRT4.@]
122  */
123 RPC_STATUS WINAPI RpcSmDestroyClientContext(void **ContextHandle)
124 {
125     RPC_STATUS status = RPC_X_SS_CONTEXT_MISMATCH;
126     struct context_handle_entry *che = NULL;
127 
128     TRACE("(%p)\n", ContextHandle);
129 
130     EnterCriticalSection(&ndr_context_cs);
131     che = get_context_entry(*ContextHandle);
132     *ContextHandle = NULL;
133     if (che)
134     {
135         status = RPC_S_OK;
136         list_remove(&che->entry);
137     }
138 
139     LeaveCriticalSection(&ndr_context_cs);
140 
141     if (che)
142     {
143         RpcBindingFree(&che->handle);
144         HeapFree(GetProcessHeap(), 0, che);
145     }
146 
147     return status;
148 }
149 
150 /***********************************************************************
151  *           RpcSsDestroyClientContext [RPCRT4.@]
152  */
153 void WINAPI RpcSsDestroyClientContext(void **ContextHandle)
154 {
155     RPC_STATUS status = RpcSmDestroyClientContext(ContextHandle);
156     if (status != RPC_S_OK)
157         RpcRaiseException(status);
158 }
159 
160 /***********************************************************************
161  *           RpcSsDontSerializeContext [RPCRT4.@]
162  */
163  void WINAPI RpcSsDontSerializeContext(void)
164 {
165     FIXME("stub\n");
166 }
167 
168 static RPC_STATUS ndr_update_context_handle(NDR_CCONTEXT *CContext,
169                                       RPC_BINDING_HANDLE hBinding,
170                                       const ndr_context_handle *chi)
171 {
172     struct context_handle_entry *che = NULL;
173 
174     /* a null UUID means we should free the context handle */
175     if (IsEqualGUID(&chi->uuid, &GUID_NULL))
176     {
177         if (*CContext)
178         {
179             che = get_context_entry(*CContext);
180             if (!che)
181                 return RPC_X_SS_CONTEXT_MISMATCH;
182             list_remove(&che->entry);
183             RpcBindingFree(&che->handle);
184             HeapFree(GetProcessHeap(), 0, che);
185             che = NULL;
186         }
187     }
188     /* if there's no existing entry matching the GUID, allocate one */
189     else if (!(che = context_entry_from_guid(&chi->uuid)))
190     {
191         che = HeapAlloc(GetProcessHeap(), 0, sizeof *che);
192         if (!che)
193             return RPC_X_NO_MEMORY;
194         che->magic = NDR_CONTEXT_HANDLE_MAGIC;
195         RpcBindingCopy(hBinding, &che->handle);
196         list_add_tail(&context_handle_list, &che->entry);
197         che->wire_data = *chi;
198     }
199 
200     *CContext = che;
201 
202     return RPC_S_OK;
203 }
204 
205 /***********************************************************************
206  *           NDRCContextUnmarshall [RPCRT4.@]
207  */
208 void WINAPI NDRCContextUnmarshall(NDR_CCONTEXT *CContext,
209                                   RPC_BINDING_HANDLE hBinding,
210                                   void *pBuff, ULONG DataRepresentation)
211 {
212     RPC_STATUS status;
213 
214     TRACE("*%p=(%p) %p %p %08x\n",
215           CContext, *CContext, hBinding, pBuff, DataRepresentation);
216 
217     EnterCriticalSection(&ndr_context_cs);
218     status = ndr_update_context_handle(CContext, hBinding, pBuff);
219     LeaveCriticalSection(&ndr_context_cs);
220     if (status)
221         RpcRaiseException(status);
222 }
223 
224 /***********************************************************************
225  *           NDRSContextMarshall [RPCRT4.@]
226  */
227 void WINAPI NDRSContextMarshall(NDR_SCONTEXT SContext,
228                                void *pBuff,
229                                NDR_RUNDOWN userRunDownIn)
230 {
231     TRACE("(%p %p %p)\n", SContext, pBuff, userRunDownIn);
232     NDRSContextMarshall2(I_RpcGetCurrentCallHandle(), SContext, pBuff,
233                          userRunDownIn, NULL, RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
234 }
235 
236 /***********************************************************************
237  *           NDRSContextMarshallEx [RPCRT4.@]
238  */
239 void WINAPI NDRSContextMarshallEx(RPC_BINDING_HANDLE hBinding,
240                                   NDR_SCONTEXT SContext,
241                                   void *pBuff,
242                                   NDR_RUNDOWN userRunDownIn)
243 {
244     TRACE("(%p %p %p %p)\n", hBinding, SContext, pBuff, userRunDownIn);
245     NDRSContextMarshall2(hBinding, SContext, pBuff, userRunDownIn, NULL,
246                          RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
247 }
248 
249 /***********************************************************************
250  *           NDRSContextMarshall2 [RPCRT4.@]
251  */
252 void WINAPI NDRSContextMarshall2(RPC_BINDING_HANDLE hBinding,
253                                  NDR_SCONTEXT SContext,
254                                  void *pBuff,
255                                  NDR_RUNDOWN userRunDownIn,
256                                  void *CtxGuard, ULONG Flags)
257 {
258     RpcBinding *binding = hBinding;
259     RPC_STATUS status;
260     ndr_context_handle *ndr = pBuff;
261 
262     TRACE("(%p %p %p %p %p %u)\n",
263           hBinding, SContext, pBuff, userRunDownIn, CtxGuard, Flags);
264 
265     if (!binding->server || !binding->Assoc)
266         RpcRaiseException(RPC_S_INVALID_BINDING);
267 
268     if (Flags & RPC_CONTEXT_HANDLE_FLAGS)
269         FIXME("unimplemented flags: 0x%x\n", Flags & RPC_CONTEXT_HANDLE_FLAGS);
270 
271     if (SContext->userContext)
272     {
273         status = RpcServerAssoc_UpdateContextHandle(binding->Assoc, SContext, CtxGuard, userRunDownIn);
274         if (status != RPC_S_OK)
275             RpcRaiseException(status);
276         ndr->attributes = 0;
277         RpcContextHandle_GetUuid(SContext, &ndr->uuid);
278 
279         RPCRT4_RemoveThreadContextHandle(SContext);
280         RpcServerAssoc_ReleaseContextHandle(binding->Assoc, SContext, TRUE);
281     }
282     else
283     {
284         if (!RpcContextHandle_IsGuardCorrect(SContext, CtxGuard))
285             RpcRaiseException(RPC_X_SS_CONTEXT_MISMATCH);
286         memset(ndr, 0, sizeof(*ndr));
287 
288         RPCRT4_RemoveThreadContextHandle(SContext);
289         /* Note: release the context handle twice in this case to release
290          * one ref being kept around for the data and one ref for the
291          * unmarshall/marshall sequence */
292         if (!RpcServerAssoc_ReleaseContextHandle(binding->Assoc, SContext, TRUE))
293             return; /* this is to cope with the case of the data not being valid
294                      * before and so not having a further reference */
295         RpcServerAssoc_ReleaseContextHandle(binding->Assoc, SContext, FALSE);
296     }
297 }
298 
299 /***********************************************************************
300  *           NDRSContextUnmarshall [RPCRT4.@]
301  */
302 NDR_SCONTEXT WINAPI NDRSContextUnmarshall(void *pBuff,
303                                           ULONG DataRepresentation)
304 {
305     TRACE("(%p %08x)\n", pBuff, DataRepresentation);
306     return NDRSContextUnmarshall2(I_RpcGetCurrentCallHandle(), pBuff,
307                                   DataRepresentation, NULL,
308                                   RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
309 }
310 
311 /***********************************************************************
312  *           NDRSContextUnmarshallEx [RPCRT4.@]
313  */
314 NDR_SCONTEXT WINAPI NDRSContextUnmarshallEx(RPC_BINDING_HANDLE hBinding,
315                                             void *pBuff,
316                                             ULONG DataRepresentation)
317 {
318     TRACE("(%p %p %08x)\n", hBinding, pBuff, DataRepresentation);
319     return NDRSContextUnmarshall2(hBinding, pBuff, DataRepresentation, NULL,
320                                   RPC_CONTEXT_HANDLE_DEFAULT_FLAGS);
321 }
322 
323 /***********************************************************************
324  *           NDRSContextUnmarshall2 [RPCRT4.@]
325  */
326 NDR_SCONTEXT WINAPI NDRSContextUnmarshall2(RPC_BINDING_HANDLE hBinding,
327                                            void *pBuff,
328                                            ULONG DataRepresentation,
329                                            void *CtxGuard, ULONG Flags)
330 {
331     RpcBinding *binding = hBinding;
332     NDR_SCONTEXT SContext;
333     RPC_STATUS status;
334     const ndr_context_handle *context_ndr = pBuff;
335 
336     TRACE("(%p %p %08x %p %u)\n",
337           hBinding, pBuff, DataRepresentation, CtxGuard, Flags);
338 
339     if (!binding->server || !binding->Assoc)
340         RpcRaiseException(RPC_S_INVALID_BINDING);
341 
342     if (Flags & RPC_CONTEXT_HANDLE_FLAGS)
343         FIXME("unimplemented flags: 0x%x\n", Flags & RPC_CONTEXT_HANDLE_FLAGS);
344 
345     if (!pBuff || (!context_ndr->attributes &&
346                    UuidIsNil((UUID *)&context_ndr->uuid, &status)))
347         status = RpcServerAssoc_AllocateContextHandle(binding->Assoc, CtxGuard,
348                                                       &SContext);
349     else
350     {
351         if (context_ndr->attributes)
352         {
353             ERR("non-null attributes 0x%x\n", context_ndr->attributes);
354             status = RPC_X_SS_CONTEXT_MISMATCH;
355         }
356         else
357             status = RpcServerAssoc_FindContextHandle(binding->Assoc,
358                                                       &context_ndr->uuid,
359                                                       CtxGuard, Flags,
360                                                       &SContext);
361     }
362 
363     if (status != RPC_S_OK)
364         RpcRaiseException(status);
365 
366     RPCRT4_PushThreadContextHandle(SContext);
367     return SContext;
368 }
369