1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME:
80**
81**      conv.c
82**
83**  FACILITY:
84**
85**      Conversation Manager (CONV)
86**
87**  ABSTRACT:
88**
89**  Implement conversation manager callback procedures for NCA RPC
90**  datagram protocol.
91**
92**
93*/
94
95#include <dg.h>
96#include <dgccall.h>
97#include <dgccallt.h>
98
99#include <dce/conv.h>
100#include <dgglob.h>
101
102/* ========================================================================= */
103
104INTERNAL boolean conv_common  (
105        idl_uuid_t * /*actuid*/,
106        unsigned32 /*boot_time*/,
107        rpc_dg_ccall_p_t * /*ccall*/,
108        unsigned32 * /*st*/
109    );
110
111/* ========================================================================= */
112
113/*
114 * C O N V _ C O M M O N
115 *
116 * Common routine used by conv_who_are_you() and conv_are_you_there()
117 * to find the client call handle associated with the incoming callback
118 * and verify that we are communicating with the expected instance of
119 * the server.
120 */
121
122INTERNAL boolean conv_common
123(
124    idl_uuid_t *actuid,
125    unsigned32 boot_time,
126    rpc_dg_ccall_p_t *ccall,
127    unsigned32 *st
128)
129{
130    /*
131     * Find the call he's asking about.
132     */
133
134    *ccall = rpc__dg_ccallt_lookup(actuid, RPC_C_DG_NO_HINT);
135
136    /*
137     * No ccall entry will exist if an old duplicate WAY callback request
138     * is received.  If there is no ccall entry corresponding to the
139     * incoming activity id then we return an error status code.  The
140     * server performing the WAY callback will detect the error and send
141     * a reject packet that will be dropped by the client.
142     */
143
144    if (*ccall == NULL)
145    {
146        *st = nca_s_bad_actid;
147        return (false);
148    }
149
150    /*
151     * If we don't know the boot time yet for this server, stash it away
152     * now.
153     */
154
155    if ((*ccall)->c.call_server_boot == 0)
156    {
157        (*ccall)->c.call_server_boot = boot_time;
158    }
159    else
160    {
161        /*
162         * We DO know the boot time.  If what the server's supplied boot
163         * time isn't what we think it should be, then we must be getting
164         * called back by a new instance of the server (i.e., which
165         * received a duplicate of an outstanding request of ours).  Since
166         * we can't know whether the original instance of the server
167         * executed our call or not, we return failure to the server
168         * to prevent IT from executing call (i.e., for a possible second
169         * time, violating the "at most once" rule).
170         */
171        if ((*ccall)->c.call_server_boot != boot_time)
172        {
173            *st = nca_s_you_crashed;
174            RPC_DG_CCALL_RELEASE(ccall);
175            return (false);
176        }
177    }
178
179    *st = rpc_s_ok;
180    return (true);
181}
182
183/*
184 * C O N V _ W H O _ A R E _ Y O U
185 *
186 * This procedure is called remotely by a server that has no "connection"
187 * information about a client from which it has just received a call.  This
188 * call executes in the context of that client (i.e. it is a "call back").
189 * We return the current sequence number of the client and his "identity".
190 * We accept the boot time from the server for later use in calls to the
191 * same server.
192 */
193
194PRIVATE void conv_who_are_you
195(
196    handle_t h ATTRIBUTE_UNUSED,       /* Not really */
197    idl_uuid_t *actuid,
198    unsigned32 boot_time,
199    unsigned32 *seq,
200    unsigned32 *st
201)
202{
203    rpc_dg_ccall_p_t ccall;
204
205    RPC_LOCK_ASSERT(0);
206
207    if (! conv_common(actuid, boot_time, &ccall, st))
208    {
209        return;
210    }
211
212    *seq = ccall->c.call_seq;
213    RPC_DG_CCALL_RELEASE(&ccall);
214}
215
216/*
217 * C O N V _ W H O _ A R E _ Y O U 2
218 *
219 * This procedure is called remotely by a server that needs to monitor the
220 * the liveness of this client.  We return to it a UUID unique to this
221 * particular instance of this particular application.  We also return all
222 * the information from a normal WAY callback, so that in the future servers
223 * will only need to make this call to get all client information.
224 */
225
226PRIVATE void conv_who_are_you2
227(
228    handle_t h,       /* Not really */
229    idl_uuid_t *actuid,
230    unsigned32 boot_time,
231    unsigned32 *seq,
232    idl_uuid_t *cas_uuid,
233    unsigned32 *st
234)
235{
236    conv_who_are_you(h, actuid, boot_time, seq, st);
237    *cas_uuid = rpc_g_dg_my_cas_uuid;
238}
239
240/*
241 * C O N V _ A R E _ Y O U _ T H E R E
242 *
243 * This procedure is called remotely by a server (while in the receive
244 * state) that is trying to verify whether or not it's client is still
245 * alive and sending input data.  This call executes in the context of
246 * that client (i.e. it is a "call back").  We check that the client
247 * call is still active and that the boot time from the server matches
248 * that the active CCALL
249 *
250 * Note that only the server side of this callback is currently
251 * implemented.
252 */
253
254PRIVATE void conv_are_you_there
255(
256    handle_t h ATTRIBUTE_UNUSED,       /* Not really */
257    idl_uuid_t *actuid,
258    unsigned32 boot_time,
259    unsigned32 *st
260)
261{
262    rpc_dg_ccall_p_t ccall;
263
264    RPC_LOCK_ASSERT(0);
265
266    if (! conv_common(actuid, boot_time, &ccall, st))
267    {
268        return;
269    }
270
271    RPC_DG_CCALL_RELEASE(&ccall);
272}
273
274/*
275 * C O N V _ W H O _ A R E _ Y O U _ A U T H
276 *
277 * This procedure is called remotely by a server that has no "connection"
278 * information about a client from which it has just received a call,
279 * when the incoming call is authenticated.  In addition to the
280 * information returned by conv_who_are_you, a challenge-response
281 * takes place to inform the server of the identity of the client.
282 */
283
284PRIVATE void conv_who_are_you_auth
285(
286    handle_t h ATTRIBUTE_UNUSED, /* not really */
287    idl_uuid_t *actuid,
288    unsigned32 boot_time,
289    ndr_byte *in_data,
290    signed32 in_len,
291    signed32 out_max_len,
292    unsigned32 *seq,
293    idl_uuid_t *cas_uuid,
294    ndr_byte *out_data,
295    signed32 *out_len,
296    unsigned32 *st
297)
298{
299    rpc_dg_ccall_p_t ccall;
300    rpc_dg_auth_epv_p_t epv;
301    ndr_byte *save_out_data = out_data;
302
303    RPC_LOCK_ASSERT(0);
304
305    if (! conv_common(actuid, boot_time, &ccall, st))
306    {
307        return;
308    }
309
310    *cas_uuid = rpc_g_dg_my_cas_uuid;
311    *seq = ccall->c.call_seq;
312
313    /*
314     * If there's already a credentials buffer associated with this
315     * call handle, free it.  We rely on the underlying security code
316     * to do cacheing if appropriate.
317     */
318    if (ccall->auth_way_info != NULL)
319    {
320        RPC_MEM_FREE(ccall->auth_way_info, RPC_C_MEM_DG_EPAC);
321        ccall->auth_way_info     = NULL;
322        ccall->auth_way_info_len = 0;
323    }
324
325    /*
326     * Make sure that we really have an authenticated call here,
327     * lest we dereference null and blow up the process.
328     */
329    epv = ccall->c.auth_epv;
330    if (epv == NULL)
331    {
332        *st = rpc_s_binding_has_no_auth;
333    }
334    else
335    {
336	RPC_DG_CALL_UNLOCK(&(ccall->c));
337	RPC_UNLOCK(0);
338
339	(*epv->way_handler) (ccall->c.key_info, in_data, in_len,
340            out_max_len, &out_data, out_len, st);
341
342	RPC_LOCK(0);
343	RPC_DG_CALL_LOCK(&(ccall->c));
344
345        if (*out_len > out_max_len)
346        {
347            /*
348             * If the credentials did not fit in the buffer provided,
349             * the WAY handler will have alloced up a buffer big enough
350             * to hold them, and returned a pointer to that storage in
351             * out_data.
352             *
353             * Stash a pointer to this buffer in the call handle, copy
354             * as much of the credentials as will fit in the real response
355             * packet, and return a status that indicates that the caller
356             * needs to fetch the rest of the credentials.
357             */
358            ccall->auth_way_info = out_data;
359            ccall->auth_way_info_len = *out_len;
360
361            memcpy(save_out_data, out_data, out_max_len);
362            *out_len = out_max_len;
363
364            *st = rpc_s_partial_credentials;
365        }
366    }
367    RPC_DG_CCALL_RELEASE(&ccall);
368}
369
370/*
371 * C O N V _ W H O _ A R E _ Y O U _ A U T H _ M O R E
372 *
373 * This routine is used, in conjunction with the conv_who_are_you_auth()
374 * operation, for retrieving oversized PACs.  In the event that a client's
375 * credentials are too large to fit within a single DG packet, the server
376 * can retrieve the PAC, packet by packet, by repeated calls on this
377 * operation.
378 *
379 * Note that because the "conv" interface is serviced directly by the
380 * the DG protocol (as opposed to being handled by a call executor thread),
381 * there is no additional client overhead with retrieving the PAC by
382 * multiple single-packet calls, rather than a single multiple-packet call.
383 * The small amount of overhead induced on the server side is more than
384 * compensated for by being able to avoid adding flow-control/windowing
385 * to the DG protocol's internal handling of the conv interface.
386 *
387 * Logically, this call returns
388 *
389 *        client_credentials[index ... (index+out_max_len-1)]
390 */
391
392PRIVATE void conv_who_are_you_auth_more (h, actuid, boot_time, index,
393                                         out_max_len, out_data, out_len, st)
394handle_t h ATTRIBUTE_UNUSED; /* not really */
395idl_uuid_t *actuid;
396unsigned32 boot_time;
397signed32 index;
398signed32 out_max_len;
399ndr_byte *out_data;
400signed32 *out_len;
401unsigned32 *st;
402{
403    rpc_dg_ccall_p_t ccall;
404    rpc_dg_auth_epv_p_t epv ATTRIBUTE_UNUSED;
405
406    RPC_LOCK_ASSERT(0);
407
408    if (! conv_common(actuid, boot_time, &ccall, st))
409    {
410        return;
411    }
412
413    if ((unsigned32)(index + out_max_len) >= ccall->auth_way_info_len)
414    {
415        *out_len = ccall->auth_way_info_len - index;
416        *st = rpc_s_ok;
417    }
418    else
419    {
420        *out_len = out_max_len;
421        *st = rpc_s_partial_credentials;
422    }
423
424    memcpy(out_data, ccall->auth_way_info + index, *out_len);
425    RPC_DG_CCALL_RELEASE(&ccall);
426}
427