/* * Copyright (c) 2010 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Inc. ("Apple") nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Portions of this software have been released under the following terms: * * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION * * To anyone who acknowledges that this file is provided "AS IS" * without any express or implied warranty: * permission to use, copy, modify, and distribute this file for any * purpose is hereby granted without fee, provided that the above * copyright notices and this notice appears in all source code copies, * and that none of the names of Open Software Foundation, Inc., Hewlett- * Packard Company or Digital Equipment Corporation be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Neither Open Software * Foundation, Inc., Hewlett-Packard Company nor Digital * Equipment Corporation makes any representations about the suitability * of this software for any purpose. * * Copyright (c) 2007, Novell, Inc. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Novell Inc. nor the names of its contributors * may be used to endorse or promote products derived from this * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * @APPLE_LICENSE_HEADER_END@ */ /* ** ** NAME: ** ** conv.c ** ** FACILITY: ** ** Conversation Manager (CONV) ** ** ABSTRACT: ** ** Implement conversation manager callback procedures for NCA RPC ** datagram protocol. ** ** */ #include <dg.h> #include <dgccall.h> #include <dgccallt.h> #include <dce/conv.h> #include <dgglob.h> /* ========================================================================= */ INTERNAL boolean conv_common ( idl_uuid_t * /*actuid*/, unsigned32 /*boot_time*/, rpc_dg_ccall_p_t * /*ccall*/, unsigned32 * /*st*/ ); /* ========================================================================= */ /* * C O N V _ C O M M O N * * Common routine used by conv_who_are_you() and conv_are_you_there() * to find the client call handle associated with the incoming callback * and verify that we are communicating with the expected instance of * the server. */ INTERNAL boolean conv_common ( idl_uuid_t *actuid, unsigned32 boot_time, rpc_dg_ccall_p_t *ccall, unsigned32 *st ) { /* * Find the call he's asking about. */ *ccall = rpc__dg_ccallt_lookup(actuid, RPC_C_DG_NO_HINT); /* * No ccall entry will exist if an old duplicate WAY callback request * is received. If there is no ccall entry corresponding to the * incoming activity id then we return an error status code. The * server performing the WAY callback will detect the error and send * a reject packet that will be dropped by the client. */ if (*ccall == NULL) { *st = nca_s_bad_actid; return (false); } /* * If we don't know the boot time yet for this server, stash it away * now. */ if ((*ccall)->c.call_server_boot == 0) { (*ccall)->c.call_server_boot = boot_time; } else { /* * We DO know the boot time. If what the server's supplied boot * time isn't what we think it should be, then we must be getting * called back by a new instance of the server (i.e., which * received a duplicate of an outstanding request of ours). Since * we can't know whether the original instance of the server * executed our call or not, we return failure to the server * to prevent IT from executing call (i.e., for a possible second * time, violating the "at most once" rule). */ if ((*ccall)->c.call_server_boot != boot_time) { *st = nca_s_you_crashed; RPC_DG_CCALL_RELEASE(ccall); return (false); } } *st = rpc_s_ok; return (true); } /* * C O N V _ W H O _ A R E _ Y O U * * This procedure is called remotely by a server that has no "connection" * information about a client from which it has just received a call. This * call executes in the context of that client (i.e. it is a "call back"). * We return the current sequence number of the client and his "identity". * We accept the boot time from the server for later use in calls to the * same server. */ PRIVATE void conv_who_are_you ( handle_t h ATTRIBUTE_UNUSED, /* Not really */ idl_uuid_t *actuid, unsigned32 boot_time, unsigned32 *seq, unsigned32 *st ) { rpc_dg_ccall_p_t ccall; RPC_LOCK_ASSERT(0); if (! conv_common(actuid, boot_time, &ccall, st)) { return; } *seq = ccall->c.call_seq; RPC_DG_CCALL_RELEASE(&ccall); } /* * C O N V _ W H O _ A R E _ Y O U 2 * * This procedure is called remotely by a server that needs to monitor the * the liveness of this client. We return to it a UUID unique to this * particular instance of this particular application. We also return all * the information from a normal WAY callback, so that in the future servers * will only need to make this call to get all client information. */ PRIVATE void conv_who_are_you2 ( handle_t h, /* Not really */ idl_uuid_t *actuid, unsigned32 boot_time, unsigned32 *seq, idl_uuid_t *cas_uuid, unsigned32 *st ) { conv_who_are_you(h, actuid, boot_time, seq, st); *cas_uuid = rpc_g_dg_my_cas_uuid; } /* * C O N V _ A R E _ Y O U _ T H E R E * * This procedure is called remotely by a server (while in the receive * state) that is trying to verify whether or not it's client is still * alive and sending input data. This call executes in the context of * that client (i.e. it is a "call back"). We check that the client * call is still active and that the boot time from the server matches * that the active CCALL * * Note that only the server side of this callback is currently * implemented. */ PRIVATE void conv_are_you_there ( handle_t h ATTRIBUTE_UNUSED, /* Not really */ idl_uuid_t *actuid, unsigned32 boot_time, unsigned32 *st ) { rpc_dg_ccall_p_t ccall; RPC_LOCK_ASSERT(0); if (! conv_common(actuid, boot_time, &ccall, st)) { return; } RPC_DG_CCALL_RELEASE(&ccall); } /* * C O N V _ W H O _ A R E _ Y O U _ A U T H * * This procedure is called remotely by a server that has no "connection" * information about a client from which it has just received a call, * when the incoming call is authenticated. In addition to the * information returned by conv_who_are_you, a challenge-response * takes place to inform the server of the identity of the client. */ PRIVATE void conv_who_are_you_auth ( handle_t h ATTRIBUTE_UNUSED, /* not really */ idl_uuid_t *actuid, unsigned32 boot_time, ndr_byte *in_data, signed32 in_len, signed32 out_max_len, unsigned32 *seq, idl_uuid_t *cas_uuid, ndr_byte *out_data, signed32 *out_len, unsigned32 *st ) { rpc_dg_ccall_p_t ccall; rpc_dg_auth_epv_p_t epv; ndr_byte *save_out_data = out_data; RPC_LOCK_ASSERT(0); if (! conv_common(actuid, boot_time, &ccall, st)) { return; } *cas_uuid = rpc_g_dg_my_cas_uuid; *seq = ccall->c.call_seq; /* * If there's already a credentials buffer associated with this * call handle, free it. We rely on the underlying security code * to do cacheing if appropriate. */ if (ccall->auth_way_info != NULL) { RPC_MEM_FREE(ccall->auth_way_info, RPC_C_MEM_DG_EPAC); ccall->auth_way_info = NULL; ccall->auth_way_info_len = 0; } /* * Make sure that we really have an authenticated call here, * lest we dereference null and blow up the process. */ epv = ccall->c.auth_epv; if (epv == NULL) { *st = rpc_s_binding_has_no_auth; } else { RPC_DG_CALL_UNLOCK(&(ccall->c)); RPC_UNLOCK(0); (*epv->way_handler) (ccall->c.key_info, in_data, in_len, out_max_len, &out_data, out_len, st); RPC_LOCK(0); RPC_DG_CALL_LOCK(&(ccall->c)); if (*out_len > out_max_len) { /* * If the credentials did not fit in the buffer provided, * the WAY handler will have alloced up a buffer big enough * to hold them, and returned a pointer to that storage in * out_data. * * Stash a pointer to this buffer in the call handle, copy * as much of the credentials as will fit in the real response * packet, and return a status that indicates that the caller * needs to fetch the rest of the credentials. */ ccall->auth_way_info = out_data; ccall->auth_way_info_len = *out_len; memcpy(save_out_data, out_data, out_max_len); *out_len = out_max_len; *st = rpc_s_partial_credentials; } } RPC_DG_CCALL_RELEASE(&ccall); } /* * C O N V _ W H O _ A R E _ Y O U _ A U T H _ M O R E * * This routine is used, in conjunction with the conv_who_are_you_auth() * operation, for retrieving oversized PACs. In the event that a client's * credentials are too large to fit within a single DG packet, the server * can retrieve the PAC, packet by packet, by repeated calls on this * operation. * * Note that because the "conv" interface is serviced directly by the * the DG protocol (as opposed to being handled by a call executor thread), * there is no additional client overhead with retrieving the PAC by * multiple single-packet calls, rather than a single multiple-packet call. * The small amount of overhead induced on the server side is more than * compensated for by being able to avoid adding flow-control/windowing * to the DG protocol's internal handling of the conv interface. * * Logically, this call returns * * client_credentials[index ... (index+out_max_len-1)] */ PRIVATE void conv_who_are_you_auth_more (h, actuid, boot_time, index, out_max_len, out_data, out_len, st) handle_t h ATTRIBUTE_UNUSED; /* not really */ idl_uuid_t *actuid; unsigned32 boot_time; signed32 index; signed32 out_max_len; ndr_byte *out_data; signed32 *out_len; unsigned32 *st; { rpc_dg_ccall_p_t ccall; rpc_dg_auth_epv_p_t epv ATTRIBUTE_UNUSED; RPC_LOCK_ASSERT(0); if (! conv_common(actuid, boot_time, &ccall, st)) { return; } if ((unsigned32)(index + out_max_len) >= ccall->auth_way_info_len) { *out_len = ccall->auth_way_info_len - index; *st = rpc_s_ok; } else { *out_len = out_max_len; *st = rpc_s_partial_credentials; } memcpy(out_data, ccall->auth_way_info + index, *out_len); RPC_DG_CCALL_RELEASE(&ccall); }