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