13299f39fSGordon Ross /*
23299f39fSGordon Ross  * CDDL HEADER START
33299f39fSGordon Ross  *
43299f39fSGordon Ross  * The contents of this file are subject to the terms of the
53299f39fSGordon Ross  * Common Development and Distribution License (the "License").
63299f39fSGordon Ross  * You may not use this file except in compliance with the License.
73299f39fSGordon Ross  *
83299f39fSGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93299f39fSGordon Ross  * or http://www.opensolaris.org/os/licensing.
103299f39fSGordon Ross  * See the License for the specific language governing permissions
113299f39fSGordon Ross  * and limitations under the License.
123299f39fSGordon Ross  *
133299f39fSGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
143299f39fSGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153299f39fSGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
163299f39fSGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
173299f39fSGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
183299f39fSGordon Ross  *
193299f39fSGordon Ross  * CDDL HEADER END
203299f39fSGordon Ross  */
213299f39fSGordon Ross 
223299f39fSGordon Ross /*
233299f39fSGordon Ross  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
243299f39fSGordon Ross  * Use is subject to license terms.
253299f39fSGordon Ross  *
26*ce8560eeSMatt Barden  * Copyright 2020 Tintri by DDN, Inc. All rights reserved.
273299f39fSGordon Ross  */
283299f39fSGordon Ross 
293299f39fSGordon Ross /*
303299f39fSGordon Ross  * ML-RPC Client handle interface and support functions.
313299f39fSGordon Ross  */
323299f39fSGordon Ross 
333299f39fSGordon Ross #include <sys/types.h>
343299f39fSGordon Ross #include <sys/fcntl.h>
353299f39fSGordon Ross #include <sys/poll.h>
363299f39fSGordon Ross 
373299f39fSGordon Ross #include <errno.h>
383299f39fSGordon Ross #include <strings.h>
393299f39fSGordon Ross #include <unistd.h>
403299f39fSGordon Ross 
413299f39fSGordon Ross #include <netsmb/smbfs_api.h>
423299f39fSGordon Ross #include <smb/ntstatus.h>
433299f39fSGordon Ross #include <libmlrpc.h>
443299f39fSGordon Ross 
453299f39fSGordon Ross #include <assert.h>
463299f39fSGordon Ross 
473299f39fSGordon Ross static int ndr_xa_init(ndr_client_t *, ndr_xa_t *);
483299f39fSGordon Ross static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *);
493299f39fSGordon Ross static int ndr_xa_read(ndr_client_t *, ndr_xa_t *);
503299f39fSGordon Ross static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *);
513299f39fSGordon Ross static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *);
523299f39fSGordon Ross static void ndr_xa_release(ndr_client_t *);
533299f39fSGordon Ross 
543299f39fSGordon Ross /* See notes in mlrpc_clh_bind */
553299f39fSGordon Ross int rpc_pipe_open_retries = 10;
563299f39fSGordon Ross 
573299f39fSGordon Ross /*
583299f39fSGordon Ross  * Create an RPC client binding handle using the given smb_ctx.
593299f39fSGordon Ross  * That context must already have a session and tree connected.
603299f39fSGordon Ross  *
613299f39fSGordon Ross  * Returns zero or an errno value.
623299f39fSGordon Ross  */
633299f39fSGordon Ross int
mlrpc_clh_create(mlrpc_handle_t * handle,void * ctx)643299f39fSGordon Ross mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx)
653299f39fSGordon Ross {
663299f39fSGordon Ross 	ndr_client_t	*clnt = NULL;
673299f39fSGordon Ross 
683299f39fSGordon Ross 	if (ctx == NULL)
693299f39fSGordon Ross 		return (EINVAL);
703299f39fSGordon Ross 
713299f39fSGordon Ross 	/*
723299f39fSGordon Ross 	 * Allocate...
733299f39fSGordon Ross 	 */
74*ce8560eeSMatt Barden 	if ((clnt = calloc(1, sizeof (*clnt))) == NULL)
753299f39fSGordon Ross 		return (ENOMEM);
763299f39fSGordon Ross 
773299f39fSGordon Ross 	clnt->xa_fd = -1;
783299f39fSGordon Ross 
793299f39fSGordon Ross 	/*
803299f39fSGordon Ross 	 * Setup the transport functions.
813299f39fSGordon Ross 	 * Always a named pipe (for now).
823299f39fSGordon Ross 	 */
833299f39fSGordon Ross 	clnt->xa_private = ctx;
843299f39fSGordon Ross 	clnt->xa_init = ndr_xa_init;
853299f39fSGordon Ross 	clnt->xa_exchange = ndr_xa_exchange;
863299f39fSGordon Ross 	clnt->xa_read = ndr_xa_read;
873299f39fSGordon Ross 	clnt->xa_preserve = ndr_xa_preserve;
883299f39fSGordon Ross 	clnt->xa_destruct = ndr_xa_destruct;
893299f39fSGordon Ross 	clnt->xa_release = ndr_xa_release;
903299f39fSGordon Ross 
913299f39fSGordon Ross 	/* See _is_bind_handle */
923299f39fSGordon Ross 	clnt->handle = &handle->handle;
933299f39fSGordon Ross 
943299f39fSGordon Ross 	ndr_svc_binding_pool_init(&clnt->binding_list,
953299f39fSGordon Ross 	    clnt->binding_pool, NDR_N_BINDING_POOL);
963299f39fSGordon Ross 
973299f39fSGordon Ross 	if ((clnt->heap = ndr_heap_create()) == NULL)
983299f39fSGordon Ross 		goto nomem;
993299f39fSGordon Ross 
1003299f39fSGordon Ross 	/* success! */
1013299f39fSGordon Ross 	bzero(handle, sizeof (*handle));
1023299f39fSGordon Ross 	handle->clnt = clnt;
1033299f39fSGordon Ross 	return (0);
1043299f39fSGordon Ross 
1053299f39fSGordon Ross nomem:
1063299f39fSGordon Ross 	free(clnt);
1073299f39fSGordon Ross 	return (ENOMEM);
1083299f39fSGordon Ross }
1093299f39fSGordon Ross 
110*ce8560eeSMatt Barden /*
111*ce8560eeSMatt Barden  * Set up this handle to perform RPC-level authentication.
112*ce8560eeSMatt Barden  */
113*ce8560eeSMatt Barden uint32_t
mlrpc_clh_set_auth(mlrpc_handle_t * handle,ndr_auth_ctx_t * auth_ctx)114*ce8560eeSMatt Barden mlrpc_clh_set_auth(mlrpc_handle_t *handle, ndr_auth_ctx_t *auth_ctx)
115*ce8560eeSMatt Barden {
116*ce8560eeSMatt Barden 	ndr_client_t		*clnt = NULL;
117*ce8560eeSMatt Barden 
118*ce8560eeSMatt Barden 	if ((clnt = handle->clnt) == NULL)
119*ce8560eeSMatt Barden 		return (NT_STATUS_INTERNAL_ERROR);
120*ce8560eeSMatt Barden 
121*ce8560eeSMatt Barden 	if (auth_ctx != NULL) {
122*ce8560eeSMatt Barden 		/* struct copy */
123*ce8560eeSMatt Barden 		clnt->auth_ctx = *auth_ctx;
124*ce8560eeSMatt Barden 	}
125*ce8560eeSMatt Barden 
126*ce8560eeSMatt Barden 	return (NT_STATUS_SUCCESS);
127*ce8560eeSMatt Barden }
1283299f39fSGordon Ross 
1293299f39fSGordon Ross /*
1303299f39fSGordon Ross  * This call must be made to initialize an RPC client structure and bind
1313299f39fSGordon Ross  * to the remote service before any RPCs can be exchanged with that service.
1323299f39fSGordon Ross  *
1333299f39fSGordon Ross  * The mlrpc_handle_t is a wrapper that is used to associate an RPC handle
1343299f39fSGordon Ross  * with the client context for an instance of the interface.  The handle
1353299f39fSGordon Ross  * is zeroed to ensure that it doesn't look like a valid handle -
1363299f39fSGordon Ross  * handle content is provided by the remove service.
1373299f39fSGordon Ross  *
1383299f39fSGordon Ross  * The client points to this top-level handle so that we know when to
1393299f39fSGordon Ross  * unbind and teardown the connection.  As each handle is initialized it
1403299f39fSGordon Ross  * will inherit a reference to the client context.
1413299f39fSGordon Ross  *
1423299f39fSGordon Ross  *
1433299f39fSGordon Ross  * Similar to MSRPC RpcBindingBind()
1443299f39fSGordon Ross  *
1453299f39fSGordon Ross  * Returns 0 or an NT_STATUS:		(failed in...)
1463299f39fSGordon Ross  *
1473299f39fSGordon Ross  *	RPC_NT_SERVER_TOO_BUSY		(open pipe)
1483299f39fSGordon Ross  *	RPC_NT_SERVER_UNAVAILABLE	(open pipe)
1493299f39fSGordon Ross  *	NT_STATUS_ACCESS_DENIED		(open pipe)
1503299f39fSGordon Ross  *	NT_STATUS_INVALID_PARAMETER	(rpc bind)
1513299f39fSGordon Ross  *	NT_STATUS_INTERNAL_ERROR	(bad args etc)
1523299f39fSGordon Ross  *	NT_STATUS_NO_MEMORY
1533299f39fSGordon Ross  */
1543299f39fSGordon Ross uint32_t
mlrpc_clh_bind(mlrpc_handle_t * handle,ndr_service_t * svc)1553299f39fSGordon Ross mlrpc_clh_bind(mlrpc_handle_t *handle, ndr_service_t *svc)
1563299f39fSGordon Ross {
1573299f39fSGordon Ross 	ndr_client_t		*clnt = NULL;
1583299f39fSGordon Ross 	struct smb_ctx		*ctx = NULL;
1593299f39fSGordon Ross 	uint32_t		status = 0;
1603299f39fSGordon Ross 	int			fd = -1;
1613299f39fSGordon Ross 	int			rc, retries;
1623299f39fSGordon Ross 
1633299f39fSGordon Ross 	if ((clnt = handle->clnt) == NULL)
1643299f39fSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
1653299f39fSGordon Ross 	if ((ctx = clnt->xa_private) == NULL)
1663299f39fSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
1673299f39fSGordon Ross 	if (clnt->xa_fd != -1)
1683299f39fSGordon Ross 		return (NT_STATUS_INTERNAL_ERROR);
1693299f39fSGordon Ross 
1703299f39fSGordon Ross 	/*
1713299f39fSGordon Ross 	 * Open the named pipe.
1723299f39fSGordon Ross 	 *
1733299f39fSGordon Ross 	 * Sometimes a DC may return NT_STATUS_PIPE_NOT_AVAILABLE for
1743299f39fSGordon Ross 	 * the first few seconds during service auto-start.  The client
1753299f39fSGordon Ross 	 * translates that to EBUSY, so when we see that, wait a bit
1763299f39fSGordon Ross 	 * and retry the open for up to rpc_pipe_open_retries.  If we
1773299f39fSGordon Ross 	 * fail even after retries, return RPC_NT_SERVER_TOO_BUSY,
1783299f39fSGordon Ross 	 * which is how callers of this layer expect that reported.
1793299f39fSGordon Ross 	 * We try up to 10 times, with a 0.5 sec. wait after each
1803299f39fSGordon Ross 	 * BUSY failure, giving a total wait here of 5 sec.
1813299f39fSGordon Ross 	 */
1823299f39fSGordon Ross 	retries = rpc_pipe_open_retries;
1833299f39fSGordon Ross retry_open:
1843299f39fSGordon Ross 	fd = smb_fh_open(ctx, svc->endpoint, O_RDWR);
1853299f39fSGordon Ross 	if (fd < 0) {
1863299f39fSGordon Ross 		rc = errno;
1873299f39fSGordon Ross 		switch (rc) {
1883299f39fSGordon Ross 		case EBUSY:
1893299f39fSGordon Ross 			if (--retries > 0) {
1903299f39fSGordon Ross 				(void) poll(NULL, 0, 500);
1913299f39fSGordon Ross 				goto retry_open;
1923299f39fSGordon Ross 			}
1933299f39fSGordon Ross 			status = RPC_NT_SERVER_TOO_BUSY;
1943299f39fSGordon Ross 			break;
1953299f39fSGordon Ross 		case EACCES:
1963299f39fSGordon Ross 			status = NT_STATUS_ACCESS_DENIED;
1973299f39fSGordon Ross 			break;
1983299f39fSGordon Ross 		default:
1993299f39fSGordon Ross 			status = RPC_NT_SERVER_UNAVAILABLE;
2003299f39fSGordon Ross 			break;
2013299f39fSGordon Ross 		}
2023299f39fSGordon Ross 		return (status);
2033299f39fSGordon Ross 	}
2043299f39fSGordon Ross 
2053299f39fSGordon Ross 	clnt->xa_fd = fd;
2063299f39fSGordon Ross 
2073299f39fSGordon Ross 	/* Paranoia, in case of re-bind. */
2083299f39fSGordon Ross 	bzero(&handle->handle, sizeof (ndr_hdid_t));
2093299f39fSGordon Ross 
2103299f39fSGordon Ross 	/*
2113299f39fSGordon Ross 	 * Do the OtW RPC bind.
2123299f39fSGordon Ross 	 */
2133299f39fSGordon Ross 	rc = ndr_clnt_bind(clnt, svc, &clnt->binding);
2143299f39fSGordon Ross 	switch (rc) {
2153299f39fSGordon Ross 	case NDR_DRC_FAULT_OUT_OF_MEMORY:
2163299f39fSGordon Ross 		status = NT_STATUS_NO_MEMORY;
2173299f39fSGordon Ross 		break;
2183299f39fSGordon Ross 	case NDR_DRC_FAULT_API_SERVICE_INVALID:
2193299f39fSGordon Ross 		/* svc->..._uuid parse errors */
2203299f39fSGordon Ross 		status = NT_STATUS_INTERNAL_ERROR;
2213299f39fSGordon Ross 		break;
2223299f39fSGordon Ross 	default:
2233299f39fSGordon Ross 		if (NDR_DRC_IS_FAULT(rc)) {
2243299f39fSGordon Ross 			status = RPC_NT_PROTOCOL_ERROR;
2253299f39fSGordon Ross 			break;
2263299f39fSGordon Ross 		}
2273299f39fSGordon Ross 		/* FALLTHROUGH */
2283299f39fSGordon Ross 	case NDR_DRC_OK:
2293299f39fSGordon Ross 		status = NT_STATUS_SUCCESS;
2303299f39fSGordon Ross 	}
2313299f39fSGordon Ross 
2323299f39fSGordon Ross 	if (status != 0) {
2333299f39fSGordon Ross 		if (fd != -1)
2343299f39fSGordon Ross 			(void) smb_fh_close(fd);
2353299f39fSGordon Ross 		clnt->xa_fd = -1;
2363299f39fSGordon Ross 	}
2373299f39fSGordon Ross 
2383299f39fSGordon Ross 	return (status);
2393299f39fSGordon Ross }
2403299f39fSGordon Ross 
2413299f39fSGordon Ross /*
2423299f39fSGordon Ross  * Unbind and close the pipe to an RPC service.
2433299f39fSGordon Ross  *
2443299f39fSGordon Ross  * Similar to MSRPC RpcBindingUnbind()
2453299f39fSGordon Ross  * This should be called after a dropped connection.
2463299f39fSGordon Ross  */
2473299f39fSGordon Ross void
mlrpc_clh_unbind(mlrpc_handle_t * handle)2483299f39fSGordon Ross mlrpc_clh_unbind(mlrpc_handle_t *handle)
2493299f39fSGordon Ross {
2503299f39fSGordon Ross 	ndr_client_t *clnt = handle->clnt;
2513299f39fSGordon Ross 
2523299f39fSGordon Ross 	if (clnt->xa_fd != -1) {
2533299f39fSGordon Ross 		(void) smb_fh_close(clnt->xa_fd);
2543299f39fSGordon Ross 		clnt->xa_fd = -1;
2553299f39fSGordon Ross 	}
2563299f39fSGordon Ross }
2573299f39fSGordon Ross 
2583299f39fSGordon Ross /*
2593299f39fSGordon Ross  * If the heap has been preserved we need to go through an xa release.
2603299f39fSGordon Ross  * The heap is preserved during an RPC call because that's where data
2613299f39fSGordon Ross  * returned from the server is stored.
2623299f39fSGordon Ross  *
2633299f39fSGordon Ross  * Otherwise we destroy the heap directly.
2643299f39fSGordon Ross  *
2653299f39fSGordon Ross  * Returns the xa_private pointer (if non-NULL) to inform the caller
2663299f39fSGordon Ross  * that it can now be destroyed.
2673299f39fSGordon Ross  */
2683299f39fSGordon Ross void *
mlrpc_clh_free(mlrpc_handle_t * handle)2693299f39fSGordon Ross mlrpc_clh_free(mlrpc_handle_t *handle)
2703299f39fSGordon Ross {
2713299f39fSGordon Ross 	ndr_client_t *clnt = handle->clnt;
2723299f39fSGordon Ross 	void *private;
2733299f39fSGordon Ross 
2743299f39fSGordon Ross 	if (clnt == NULL)
2753299f39fSGordon Ross 		return (NULL);
2763299f39fSGordon Ross 
2773299f39fSGordon Ross 	/*
2783299f39fSGordon Ross 	 * Should never get an unbind on inherited handles.
2793299f39fSGordon Ross 	 * Callers of ndr_inherit_handle() check handles
2803299f39fSGordon Ross 	 * with ndr_is_bind_handle() before calling this.
2813299f39fSGordon Ross 	 *
2823299f39fSGordon Ross 	 * Maybe make this function more tolerant?
2833299f39fSGordon Ross 	 */
2843299f39fSGordon Ross 	assert(handle->clnt->handle == &handle->handle);
2853299f39fSGordon Ross 
2863299f39fSGordon Ross 	mlrpc_clh_unbind(handle);
2873299f39fSGordon Ross 
2883299f39fSGordon Ross 	if (clnt->heap_preserved)
2893299f39fSGordon Ross 		ndr_clnt_free_heap(clnt); /* xa_release */
2903299f39fSGordon Ross 	else
2913299f39fSGordon Ross 		ndr_heap_destroy(clnt->heap);
2923299f39fSGordon Ross 
2933299f39fSGordon Ross 	/*
2943299f39fSGordon Ross 	 * Note: Caller will free the smb_ctx stored in
2953299f39fSGordon Ross 	 * clnt->xa_private (or possibly reuse it).
2963299f39fSGordon Ross 	 */
2973299f39fSGordon Ross 	private = clnt->xa_private;
2983299f39fSGordon Ross 	free(clnt);
2993299f39fSGordon Ross 	bzero(handle, sizeof (*handle));
3003299f39fSGordon Ross 	return (private);
3013299f39fSGordon Ross }
3023299f39fSGordon Ross 
3033299f39fSGordon Ross /*
3043299f39fSGordon Ross  * Call the RPC function identified by opnum.  The remote service is
3053299f39fSGordon Ross  * identified by the handle, which should have been initialized by
3063299f39fSGordon Ross  * ndr_rpc_bind.
3073299f39fSGordon Ross  *
3083299f39fSGordon Ross  * If the RPC call is successful (returns 0), the caller must call
3093299f39fSGordon Ross  * ndr_rpc_release to release the heap.  Otherwise, we release the
3103299f39fSGordon Ross  * heap here.
3113299f39fSGordon Ross  */
3123299f39fSGordon Ross int
ndr_rpc_call(mlrpc_handle_t * handle,int opnum,void * params)3133299f39fSGordon Ross ndr_rpc_call(mlrpc_handle_t *handle, int opnum, void *params)
3143299f39fSGordon Ross {
3153299f39fSGordon Ross 	ndr_client_t *clnt = handle->clnt;
3163299f39fSGordon Ross 	int rc;
3173299f39fSGordon Ross 
3183299f39fSGordon Ross 	if (ndr_rpc_get_heap(handle) == NULL)
3193299f39fSGordon Ross 		return (-1);
3203299f39fSGordon Ross 
3213299f39fSGordon Ross 	rc = ndr_clnt_call(clnt->binding, opnum, params);
3223299f39fSGordon Ross 
3233299f39fSGordon Ross 	/*
3243299f39fSGordon Ross 	 * Always clear the nonull flag to ensure
3253299f39fSGordon Ross 	 * it is not applied to subsequent calls.
3263299f39fSGordon Ross 	 */
3273299f39fSGordon Ross 	clnt->nonull = B_FALSE;
3283299f39fSGordon Ross 
3293299f39fSGordon Ross 	if (NDR_DRC_IS_FAULT(rc)) {
3303299f39fSGordon Ross 		ndr_rpc_release(handle);
3313299f39fSGordon Ross 		return (-1);
3323299f39fSGordon Ross 	}
3333299f39fSGordon Ross 
3343299f39fSGordon Ross 	return (0);
3353299f39fSGordon Ross }
3363299f39fSGordon Ross 
3373299f39fSGordon Ross /*
3383299f39fSGordon Ross  * Outgoing strings should not be null terminated.
3393299f39fSGordon Ross  */
3403299f39fSGordon Ross void
ndr_rpc_set_nonull(mlrpc_handle_t * handle)3413299f39fSGordon Ross ndr_rpc_set_nonull(mlrpc_handle_t *handle)
3423299f39fSGordon Ross {
3433299f39fSGordon Ross 	handle->clnt->nonull = B_TRUE;
3443299f39fSGordon Ross }
3453299f39fSGordon Ross 
3463299f39fSGordon Ross /*
3473299f39fSGordon Ross  * Get the session key from a bound RPC client handle.
3483299f39fSGordon Ross  *
3493299f39fSGordon Ross  * The key returned is the 16-byte "user session key"
3503299f39fSGordon Ross  * established by the underlying authentication protocol
3513299f39fSGordon Ross  * (either Kerberos or NTLM).  This key is needed for
3523299f39fSGordon Ross  * SAM RPC calls such as SamrSetInformationUser, etc.
3533299f39fSGordon Ross  * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25.
3543299f39fSGordon Ross  *
3553299f39fSGordon Ross  * Returns zero (success) or an errno.
3563299f39fSGordon Ross  */
3573299f39fSGordon Ross int
ndr_rpc_get_ssnkey(mlrpc_handle_t * handle,uchar_t * key,size_t len)3583299f39fSGordon Ross ndr_rpc_get_ssnkey(mlrpc_handle_t *handle, uchar_t *key, size_t len)
3593299f39fSGordon Ross {
3603299f39fSGordon Ross 	ndr_client_t *clnt = handle->clnt;
3613299f39fSGordon Ross 
3623299f39fSGordon Ross 	if (clnt == NULL || clnt->xa_fd == -1)
3633299f39fSGordon Ross 		return (EINVAL);
3643299f39fSGordon Ross 
3653299f39fSGordon Ross 	return (smb_fh_getssnkey(clnt->xa_fd, key, len));
3663299f39fSGordon Ross }
3673299f39fSGordon Ross 
3683299f39fSGordon Ross void *
ndr_rpc_malloc(mlrpc_handle_t * handle,size_t size)3693299f39fSGordon Ross ndr_rpc_malloc(mlrpc_handle_t *handle, size_t size)
3703299f39fSGordon Ross {
3713299f39fSGordon Ross 	ndr_heap_t *heap;
3723299f39fSGordon Ross 
3733299f39fSGordon Ross 	if ((heap = ndr_rpc_get_heap(handle)) == NULL)
3743299f39fSGordon Ross 		return (NULL);
3753299f39fSGordon Ross 
3763299f39fSGordon Ross 	return (ndr_heap_malloc(heap, size));
3773299f39fSGordon Ross }
3783299f39fSGordon Ross 
3793299f39fSGordon Ross ndr_heap_t *
ndr_rpc_get_heap(mlrpc_handle_t * handle)3803299f39fSGordon Ross ndr_rpc_get_heap(mlrpc_handle_t *handle)
3813299f39fSGordon Ross {
3823299f39fSGordon Ross 	ndr_client_t *clnt = handle->clnt;
3833299f39fSGordon Ross 
3843299f39fSGordon Ross 	if (clnt->heap == NULL)
3853299f39fSGordon Ross 		clnt->heap = ndr_heap_create();
3863299f39fSGordon Ross 
3873299f39fSGordon Ross 	return (clnt->heap);
3883299f39fSGordon Ross }
3893299f39fSGordon Ross 
3903299f39fSGordon Ross /*
3913299f39fSGordon Ross  * Must be called by RPC clients to free the heap after a successful RPC
3923299f39fSGordon Ross  * call, i.e. ndr_rpc_call returned 0.  The caller should take a copy
3933299f39fSGordon Ross  * of any data returned by the RPC prior to calling this function because
3943299f39fSGordon Ross  * returned data is in the heap.
3953299f39fSGordon Ross  */
3963299f39fSGordon Ross void
ndr_rpc_release(mlrpc_handle_t * handle)3973299f39fSGordon Ross ndr_rpc_release(mlrpc_handle_t *handle)
3983299f39fSGordon Ross {
3993299f39fSGordon Ross 	ndr_client_t *clnt = handle->clnt;
4003299f39fSGordon Ross 
4013299f39fSGordon Ross 	if (clnt->heap_preserved)
4023299f39fSGordon Ross 		ndr_clnt_free_heap(clnt);
4033299f39fSGordon Ross 	else
4043299f39fSGordon Ross 		ndr_heap_destroy(clnt->heap);
4053299f39fSGordon Ross 
4063299f39fSGordon Ross 	clnt->heap = NULL;
4073299f39fSGordon Ross }
4083299f39fSGordon Ross 
4093299f39fSGordon Ross /*
4103299f39fSGordon Ross  * Returns true if the handle is null.
4113299f39fSGordon Ross  * Otherwise returns false.
4123299f39fSGordon Ross  */
4133299f39fSGordon Ross boolean_t
ndr_is_null_handle(mlrpc_handle_t * handle)4143299f39fSGordon Ross ndr_is_null_handle(mlrpc_handle_t *handle)
4153299f39fSGordon Ross {
4163299f39fSGordon Ross 	static const ndr_hdid_t hdid0 = {0};
4173299f39fSGordon Ross 
4183299f39fSGordon Ross 	if (handle == NULL || handle->clnt == NULL)
4193299f39fSGordon Ross 		return (B_TRUE);
4203299f39fSGordon Ross 
4213299f39fSGordon Ross 	if (!memcmp(&handle->handle, &hdid0, sizeof (hdid0)))
4223299f39fSGordon Ross 		return (B_TRUE);
4233299f39fSGordon Ross 
4243299f39fSGordon Ross 	return (B_FALSE);
4253299f39fSGordon Ross }
4263299f39fSGordon Ross 
4273299f39fSGordon Ross /*
4283299f39fSGordon Ross  * Returns true if the handle is the top level bind handle.
4293299f39fSGordon Ross  * Otherwise returns false.
4303299f39fSGordon Ross  */
4313299f39fSGordon Ross boolean_t
ndr_is_bind_handle(mlrpc_handle_t * handle)4323299f39fSGordon Ross ndr_is_bind_handle(mlrpc_handle_t *handle)
4333299f39fSGordon Ross {
4343299f39fSGordon Ross 	return (handle->clnt->handle == &handle->handle);
4353299f39fSGordon Ross }
4363299f39fSGordon Ross 
4373299f39fSGordon Ross /*
4383299f39fSGordon Ross  * Pass the client reference from parent to child.
4393299f39fSGordon Ross  */
4403299f39fSGordon Ross void
ndr_inherit_handle(mlrpc_handle_t * child,mlrpc_handle_t * parent)4413299f39fSGordon Ross ndr_inherit_handle(mlrpc_handle_t *child, mlrpc_handle_t *parent)
4423299f39fSGordon Ross {
4433299f39fSGordon Ross 	child->clnt = parent->clnt;
4443299f39fSGordon Ross }
4453299f39fSGordon Ross 
4463299f39fSGordon Ross /*
4473299f39fSGordon Ross  * ndr_rpc_status remains in libmlsvc mlsvc_client.c
4483299f39fSGordon Ross  */
4493299f39fSGordon Ross 
4503299f39fSGordon Ross /*
4513299f39fSGordon Ross  * The following functions provide the client callback interface.
4523299f39fSGordon Ross  * If the caller hasn't provided a heap, create one here.
4533299f39fSGordon Ross  */
4543299f39fSGordon Ross static int
ndr_xa_init(ndr_client_t * clnt,ndr_xa_t * mxa)4553299f39fSGordon Ross ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa)
4563299f39fSGordon Ross {
4573299f39fSGordon Ross 	ndr_stream_t *recv_nds = &mxa->recv_nds;
4583299f39fSGordon Ross 	ndr_stream_t *send_nds = &mxa->send_nds;
4593299f39fSGordon Ross 	ndr_heap_t *heap = clnt->heap;
4603299f39fSGordon Ross 	int		rc;
4613299f39fSGordon Ross 
4623299f39fSGordon Ross 	if (heap == NULL) {
4633299f39fSGordon Ross 		if ((heap = ndr_heap_create()) == NULL)
4643299f39fSGordon Ross 			return (-1);
4653299f39fSGordon Ross 
4663299f39fSGordon Ross 		clnt->heap = heap;
4673299f39fSGordon Ross 	}
4683299f39fSGordon Ross 
4693299f39fSGordon Ross 	mxa->heap = heap;
4703299f39fSGordon Ross 
4713299f39fSGordon Ross 	rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap);
4723299f39fSGordon Ross 	if (rc == 0)
4733299f39fSGordon Ross 		rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT,
4743299f39fSGordon Ross 		    NDR_MODE_RETURN_RECV, heap);
4753299f39fSGordon Ross 
4763299f39fSGordon Ross 	if (rc != 0) {
4773299f39fSGordon Ross 		nds_destruct(&mxa->recv_nds);
4783299f39fSGordon Ross 		nds_destruct(&mxa->send_nds);
4793299f39fSGordon Ross 		ndr_heap_destroy(mxa->heap);
4803299f39fSGordon Ross 		mxa->heap = NULL;
4813299f39fSGordon Ross 		clnt->heap = NULL;
4823299f39fSGordon Ross 		return (-1);
4833299f39fSGordon Ross 	}
4843299f39fSGordon Ross 
4853299f39fSGordon Ross 	if (clnt->nonull)
4863299f39fSGordon Ross 		NDS_SETF(send_nds, NDS_F_NONULL);
4873299f39fSGordon Ross 
4883299f39fSGordon Ross 	return (0);
4893299f39fSGordon Ross }
4903299f39fSGordon Ross 
4913299f39fSGordon Ross /*
4923299f39fSGordon Ross  * This is the entry pointy for an RPC client call exchange with
4933299f39fSGordon Ross  * a server, which will result in an smbrdr SmbTransact request.
4943299f39fSGordon Ross  *
4953299f39fSGordon Ross  * SmbTransact should return the number of bytes received, which
4963299f39fSGordon Ross  * we record as the PDU size, or a negative error code.
4973299f39fSGordon Ross  */
4983299f39fSGordon Ross static int
ndr_xa_exchange(ndr_client_t * clnt,ndr_xa_t * mxa)4993299f39fSGordon Ross ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa)
5003299f39fSGordon Ross {
5013299f39fSGordon Ross 	ndr_stream_t *recv_nds = &mxa->recv_nds;
5023299f39fSGordon Ross 	ndr_stream_t *send_nds = &mxa->send_nds;
5033299f39fSGordon Ross 	int err, more, nbytes;
5043299f39fSGordon Ross 
5053299f39fSGordon Ross 	nbytes = recv_nds->pdu_max_size;
5063299f39fSGordon Ross 	err = smb_fh_xactnp(clnt->xa_fd,
5073299f39fSGordon Ross 	    send_nds->pdu_size, (char *)send_nds->pdu_base_offset,
5083299f39fSGordon Ross 	    &nbytes, (char *)recv_nds->pdu_base_offset, &more);
5093299f39fSGordon Ross 	if (err) {
5103299f39fSGordon Ross 		recv_nds->pdu_size = 0;
5113299f39fSGordon Ross 		return (-1);
5123299f39fSGordon Ross 	}
5133299f39fSGordon Ross 
5143299f39fSGordon Ross 	recv_nds->pdu_size = nbytes;
5153299f39fSGordon Ross 	return (0);
5163299f39fSGordon Ross }
5173299f39fSGordon Ross 
5183299f39fSGordon Ross /*
5193299f39fSGordon Ross  * This entry point will be invoked if the xa-exchange response contained
5203299f39fSGordon Ross  * only the first fragment of a multi-fragment response.  The RPC client
5213299f39fSGordon Ross  * code will then make repeated xa-read requests to obtain the remaining
5223299f39fSGordon Ross  * fragments, which will result in smbrdr SmbReadX requests.
5233299f39fSGordon Ross  *
5243299f39fSGordon Ross  * SmbReadX should return the number of bytes received, in which case we
5253299f39fSGordon Ross  * expand the PDU size to include the received data, or a negative error
5263299f39fSGordon Ross  * code.
5273299f39fSGordon Ross  */
5283299f39fSGordon Ross static int
ndr_xa_read(ndr_client_t * clnt,ndr_xa_t * mxa)5293299f39fSGordon Ross ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa)
5303299f39fSGordon Ross {
5313299f39fSGordon Ross 	ndr_stream_t *nds = &mxa->recv_nds;
5323299f39fSGordon Ross 	int len;
5333299f39fSGordon Ross 	int nbytes;
5343299f39fSGordon Ross 
5353299f39fSGordon Ross 	if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0)
5363299f39fSGordon Ross 		return (-1);
5373299f39fSGordon Ross 
5383299f39fSGordon Ross 	nbytes = smb_fh_read(clnt->xa_fd, 0, len,
5393299f39fSGordon Ross 	    (char *)nds->pdu_base_offset + nds->pdu_size);
5403299f39fSGordon Ross 
5413299f39fSGordon Ross 	if (nbytes < 0)
5423299f39fSGordon Ross 		return (-1);
5433299f39fSGordon Ross 
5443299f39fSGordon Ross 	nds->pdu_size += nbytes;
5453299f39fSGordon Ross 
5463299f39fSGordon Ross 	if (nds->pdu_size > nds->pdu_max_size) {
5473299f39fSGordon Ross 		nds->pdu_size = nds->pdu_max_size;
5483299f39fSGordon Ross 		return (-1);
5493299f39fSGordon Ross 	}
5503299f39fSGordon Ross 
5513299f39fSGordon Ross 	return (nbytes);
5523299f39fSGordon Ross }
5533299f39fSGordon Ross 
5543299f39fSGordon Ross /*
5553299f39fSGordon Ross  * Preserve the heap so that the client application has access to data
5563299f39fSGordon Ross  * returned from the server after an RPC call.
5573299f39fSGordon Ross  */
5583299f39fSGordon Ross static void
ndr_xa_preserve(ndr_client_t * clnt,ndr_xa_t * mxa)5593299f39fSGordon Ross ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa)
5603299f39fSGordon Ross {
5613299f39fSGordon Ross 	assert(clnt->heap == mxa->heap);
5623299f39fSGordon Ross 
5633299f39fSGordon Ross 	clnt->heap_preserved = B_TRUE;
5643299f39fSGordon Ross 	mxa->heap = NULL;
5653299f39fSGordon Ross }
5663299f39fSGordon Ross 
5673299f39fSGordon Ross /*
5683299f39fSGordon Ross  * Dispose of the transaction streams.  If the heap has not been
5693299f39fSGordon Ross  * preserved, we can destroy it here.
5703299f39fSGordon Ross  */
5713299f39fSGordon Ross static void
ndr_xa_destruct(ndr_client_t * clnt,ndr_xa_t * mxa)5723299f39fSGordon Ross ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa)
5733299f39fSGordon Ross {
5743299f39fSGordon Ross 	nds_destruct(&mxa->recv_nds);
5753299f39fSGordon Ross 	nds_destruct(&mxa->send_nds);
5763299f39fSGordon Ross 
5773299f39fSGordon Ross 	if (!clnt->heap_preserved) {
5783299f39fSGordon Ross 		ndr_heap_destroy(mxa->heap);
5793299f39fSGordon Ross 		mxa->heap = NULL;
5803299f39fSGordon Ross 		clnt->heap = NULL;
5813299f39fSGordon Ross 	}
5823299f39fSGordon Ross }
5833299f39fSGordon Ross 
5843299f39fSGordon Ross /*
5853299f39fSGordon Ross  * Dispose of a preserved heap.
5863299f39fSGordon Ross  */
5873299f39fSGordon Ross static void
ndr_xa_release(ndr_client_t * clnt)5883299f39fSGordon Ross ndr_xa_release(ndr_client_t *clnt)
5893299f39fSGordon Ross {
5903299f39fSGordon Ross 	if (clnt->heap_preserved) {
5913299f39fSGordon Ross 		ndr_heap_destroy(clnt->heap);
5923299f39fSGordon Ross 		clnt->heap = NULL;
5933299f39fSGordon Ross 		clnt->heap_preserved = B_FALSE;
5943299f39fSGordon Ross 	}
5953299f39fSGordon Ross }
596