/* * * (c) Copyright 1989 OPEN SOFTWARE FOUNDATION, INC. * (c) Copyright 1989 HEWLETT-PACKARD COMPANY * (c) Copyright 1989 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. * */ /* */ /* ** ** NAME: ** ** conv.c ** ** FACILITY: ** ** Conversation Manager (CONV) ** ** ABSTRACT: ** ** Implement conversation manager callback procedures for NCA RPC ** datagram protocol. ** ** */ #include #include #include #include #include /* ========================================================================= */ INTERNAL boolean conv_common _DCE_PROTOTYPE_ (( 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 #ifdef _DCE_PROTO_ ( uuid_t *actuid, unsigned32 boot_time, rpc_dg_ccall_p_t *ccall, unsigned32 *st ) #else (actuid, boot_time, ccall, st) uuid_t *actuid; unsigned32 boot_time; rpc_dg_ccall_p_t *ccall; unsigned32 *st; #endif { /* * 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 #ifdef _DCE_PROTO_ ( handle_t h ATTRIBUTE_UNUSED, /* Not really */ uuid_t *actuid, unsigned32 boot_time, unsigned32 *seq, unsigned32 *st ) #else (h, actuid, boot_time, seq, st) handle_t h; /* Not really */ uuid_t *actuid; unsigned32 boot_time; unsigned32 *seq; unsigned32 *st; #endif { 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 #ifdef _DCE_PROTO_ ( handle_t h, /* Not really */ uuid_t *actuid, unsigned32 boot_time, unsigned32 *seq, uuid_t *cas_uuid, unsigned32 *st ) #else (h, actuid, boot_time, seq, cas_uuid, st) handle_t h; /* Not really */ uuid_t *actuid; unsigned32 boot_time; unsigned32 *seq; uuid_t *cas_uuid; unsigned32 *st; #endif { 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 #ifdef _DCE_PROTO_ ( handle_t h ATTRIBUTE_UNUSED, /* Not really */ uuid_t *actuid, unsigned32 boot_time, unsigned32 *st ) #else (h, actuid, boot_time, st) handle_t h; /* Not really */ uuid_t *actuid; unsigned32 boot_time; unsigned32 *st; #endif { 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 #ifdef _DCE_PROTO_ ( handle_t h ATTRIBUTE_UNUSED, /* not really */ uuid_t *actuid, unsigned32 boot_time, ndr_byte *in_data, signed32 in_len, signed32 out_max_len, unsigned32 *seq, uuid_t *cas_uuid, ndr_byte *out_data, signed32 *out_len, unsigned32 *st ) #else (h, actuid, boot_time, in_data, in_len, out_max_len, seq, cas_uuid, out_data, out_len, st) handle_t h; /* not really */ uuid_t *actuid; unsigned32 boot_time; ndr_byte *in_data; signed32 in_len; signed32 out_max_len; unsigned32 *seq; uuid_t *cas_uuid; ndr_byte *out_data; signed32 *out_len; unsigned32 *st; #endif { 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 */ 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); }