xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c (revision 11337:1f8fe42c7b83)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
229914Samw@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw /*
278334SJose.Borrego@Sun.COM  * Client NDR RPC interface.
285331Samw  */
295331Samw 
3010717Samw@Sun.COM #include <sys/types.h>
315331Samw #include <sys/errno.h>
3210717Samw@Sun.COM #include <time.h>
335331Samw #include <strings.h>
348334SJose.Borrego@Sun.COM #include <assert.h>
3510717Samw@Sun.COM #include <thread.h>
3610717Samw@Sun.COM #include <synch.h>
375331Samw #include <smbsrv/libsmb.h>
385331Samw #include <smbsrv/libsmbrdr.h>
398334SJose.Borrego@Sun.COM #include <smbsrv/libmlrpc.h>
408334SJose.Borrego@Sun.COM #include <smbsrv/libmlsvc.h>
41*11337SWilliam.Krier@Sun.COM #include <smbsrv/ndl/srvsvc.ndl>
425331Samw 
4310717Samw@Sun.COM /*
4410717Samw@Sun.COM  * Server info cache entry expiration in seconds.
4510717Samw@Sun.COM  */
4610717Samw@Sun.COM #define	NDR_SVINFO_TIMEOUT	1800
4710717Samw@Sun.COM 
4810717Samw@Sun.COM typedef struct ndr_svinfo {
4910717Samw@Sun.COM 	list_node_t		svi_lnd;
5010717Samw@Sun.COM 	time_t			svi_tcached;
5110717Samw@Sun.COM 	char			svi_server[MAXNAMELEN];
5210717Samw@Sun.COM 	char			svi_domain[MAXNAMELEN];
5310717Samw@Sun.COM 	srvsvc_server_info_t	svi_svinfo;
5410717Samw@Sun.COM } ndr_svinfo_t;
5510717Samw@Sun.COM 
5610717Samw@Sun.COM typedef struct ndr_svlist {
5710717Samw@Sun.COM 	list_t		svl_list;
5810717Samw@Sun.COM 	mutex_t		svl_mtx;
5910717Samw@Sun.COM 	boolean_t	svl_init;
6010717Samw@Sun.COM } ndr_svlist_t;
6110717Samw@Sun.COM 
6210717Samw@Sun.COM static ndr_svlist_t ndr_svlist;
6310717Samw@Sun.COM 
648334SJose.Borrego@Sun.COM static int ndr_xa_init(ndr_client_t *, ndr_xa_t *);
658334SJose.Borrego@Sun.COM static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *);
668334SJose.Borrego@Sun.COM static int ndr_xa_read(ndr_client_t *, ndr_xa_t *);
678334SJose.Borrego@Sun.COM static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *);
688334SJose.Borrego@Sun.COM static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *);
698334SJose.Borrego@Sun.COM static void ndr_xa_release(ndr_client_t *);
705331Samw 
7110717Samw@Sun.COM static int ndr_svinfo_lookup(char *, char *, srvsvc_server_info_t *);
7210717Samw@Sun.COM static boolean_t ndr_svinfo_match(const char *, const char *, const
7310717Samw@Sun.COM     ndr_svinfo_t *);
7410717Samw@Sun.COM static boolean_t ndr_svinfo_expired(ndr_svinfo_t *);
7510717Samw@Sun.COM 
7610717Samw@Sun.COM /*
7710717Samw@Sun.COM  * Initialize the RPC client interface: create the server info cache.
7810717Samw@Sun.COM  */
7910717Samw@Sun.COM void
8010717Samw@Sun.COM ndr_rpc_init(void)
8110717Samw@Sun.COM {
8210717Samw@Sun.COM 	(void) mutex_lock(&ndr_svlist.svl_mtx);
8310717Samw@Sun.COM 
8410717Samw@Sun.COM 	if (!ndr_svlist.svl_init) {
8510717Samw@Sun.COM 		list_create(&ndr_svlist.svl_list, sizeof (ndr_svinfo_t),
8610717Samw@Sun.COM 		    offsetof(ndr_svinfo_t, svi_lnd));
8710717Samw@Sun.COM 		ndr_svlist.svl_init = B_TRUE;
8810717Samw@Sun.COM 	}
8910717Samw@Sun.COM 
9010717Samw@Sun.COM 	(void) mutex_unlock(&ndr_svlist.svl_mtx);
9110717Samw@Sun.COM }
9210717Samw@Sun.COM 
9310717Samw@Sun.COM /*
9410717Samw@Sun.COM  * Terminate the RPC client interface: flush and destroy the server info
9510717Samw@Sun.COM  * cache.
9610717Samw@Sun.COM  */
9710717Samw@Sun.COM void
9810717Samw@Sun.COM ndr_rpc_fini(void)
9910717Samw@Sun.COM {
10010717Samw@Sun.COM 	ndr_svinfo_t *svi;
10110717Samw@Sun.COM 
10210717Samw@Sun.COM 	(void) mutex_lock(&ndr_svlist.svl_mtx);
10310717Samw@Sun.COM 
10410717Samw@Sun.COM 	if (ndr_svlist.svl_init) {
10510717Samw@Sun.COM 		while ((svi = list_head(&ndr_svlist.svl_list)) != NULL) {
10610717Samw@Sun.COM 			list_remove(&ndr_svlist.svl_list, svi);
10710717Samw@Sun.COM 			free(svi->svi_svinfo.sv_name);
10810717Samw@Sun.COM 			free(svi->svi_svinfo.sv_comment);
10910717Samw@Sun.COM 			free(svi);
11010717Samw@Sun.COM 		}
11110717Samw@Sun.COM 
11210717Samw@Sun.COM 		list_destroy(&ndr_svlist.svl_list);
11310717Samw@Sun.COM 		ndr_svlist.svl_init = B_FALSE;
11410717Samw@Sun.COM 	}
11510717Samw@Sun.COM 
11610717Samw@Sun.COM 	(void) mutex_unlock(&ndr_svlist.svl_mtx);
11710717Samw@Sun.COM }
11810717Samw@Sun.COM 
1195331Samw /*
1208334SJose.Borrego@Sun.COM  * This call must be made to initialize an RPC client structure and bind
1218334SJose.Borrego@Sun.COM  * to the remote service before any RPCs can be exchanged with that service.
1225331Samw  *
1238334SJose.Borrego@Sun.COM  * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle
1248334SJose.Borrego@Sun.COM  * with the client context for an instance of the interface.  The handle
1258334SJose.Borrego@Sun.COM  * is zeroed to ensure that it doesn't look like a valid handle -
1268334SJose.Borrego@Sun.COM  * handle content is provided by the remove service.
1275331Samw  *
1288334SJose.Borrego@Sun.COM  * The client points to this top-level handle so that we know when to
1298334SJose.Borrego@Sun.COM  * unbind and teardown the connection.  As each handle is initialized it
1308334SJose.Borrego@Sun.COM  * will inherit a reference to the client context.
1315331Samw  */
1325331Samw int
1338334SJose.Borrego@Sun.COM ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
1348334SJose.Borrego@Sun.COM     char *username, const char *service)
1355331Samw {
1368334SJose.Borrego@Sun.COM 	ndr_client_t		*clnt;
1378334SJose.Borrego@Sun.COM 	ndr_service_t		*svc;
13810717Samw@Sun.COM 	srvsvc_server_info_t	svinfo;
1398334SJose.Borrego@Sun.COM 	int			fid;
1408334SJose.Borrego@Sun.COM 	int			rc;
1415331Samw 
1428334SJose.Borrego@Sun.COM 	if (handle == NULL || server == NULL ||
1438334SJose.Borrego@Sun.COM 	    domain == NULL || username == NULL)
1445331Samw 		return (-1);
1455331Samw 
1468334SJose.Borrego@Sun.COM 	if ((svc = ndr_svc_lookup_name(service)) == NULL)
1478334SJose.Borrego@Sun.COM 		return (-1);
1485331Samw 
14910717Samw@Sun.COM 	/*
15010717Samw@Sun.COM 	 * Set the default based on the assumption that most
15110717Samw@Sun.COM 	 * servers will be Windows 2000 or later.
15210717Samw@Sun.COM 	 * Don't lookup the svinfo if this is a SRVSVC request
15310717Samw@Sun.COM 	 * because the SRVSVC is used to get the server info.
154*11337SWilliam.Krier@Sun.COM 	 * None of the SRVSVC calls depend on the server info.
15510717Samw@Sun.COM 	 */
156*11337SWilliam.Krier@Sun.COM 	bzero(&svinfo, sizeof (srvsvc_server_info_t));
157*11337SWilliam.Krier@Sun.COM 	svinfo.sv_platform_id = SV_PLATFORM_ID_NT;
158*11337SWilliam.Krier@Sun.COM 	svinfo.sv_version_major = 5;
159*11337SWilliam.Krier@Sun.COM 	svinfo.sv_version_minor = 0;
160*11337SWilliam.Krier@Sun.COM 	svinfo.sv_type = SV_TYPE_DEFAULT;
161*11337SWilliam.Krier@Sun.COM 	svinfo.sv_os = NATIVE_OS_WIN2000;
16210717Samw@Sun.COM 
163*11337SWilliam.Krier@Sun.COM 	if (strcasecmp(service, "SRVSVC") != 0)
164*11337SWilliam.Krier@Sun.COM 		(void) ndr_svinfo_lookup(server, domain, &svinfo);
16510717Samw@Sun.COM 
1668334SJose.Borrego@Sun.COM 	if ((clnt = malloc(sizeof (ndr_client_t))) == NULL)
1678334SJose.Borrego@Sun.COM 		return (-1);
1685331Samw 
1698334SJose.Borrego@Sun.COM 	fid = smbrdr_open_pipe(server, domain, username, svc->endpoint);
1708334SJose.Borrego@Sun.COM 	if (fid < 0) {
1718334SJose.Borrego@Sun.COM 		free(clnt);
1725331Samw 		return (-1);
1735331Samw 	}
1745331Samw 
1758334SJose.Borrego@Sun.COM 	bzero(clnt, sizeof (ndr_client_t));
1768334SJose.Borrego@Sun.COM 	clnt->handle = &handle->handle;
1778334SJose.Borrego@Sun.COM 	clnt->fid = fid;
1788334SJose.Borrego@Sun.COM 
1798334SJose.Borrego@Sun.COM 	ndr_svc_binding_pool_init(&clnt->binding_list,
1808334SJose.Borrego@Sun.COM 	    clnt->binding_pool, NDR_N_BINDING_POOL);
1818334SJose.Borrego@Sun.COM 
1828334SJose.Borrego@Sun.COM 	clnt->xa_init = ndr_xa_init;
1838334SJose.Borrego@Sun.COM 	clnt->xa_exchange = ndr_xa_exchange;
1848334SJose.Borrego@Sun.COM 	clnt->xa_read = ndr_xa_read;
1858334SJose.Borrego@Sun.COM 	clnt->xa_preserve = ndr_xa_preserve;
1868334SJose.Borrego@Sun.COM 	clnt->xa_destruct = ndr_xa_destruct;
1878334SJose.Borrego@Sun.COM 	clnt->xa_release = ndr_xa_release;
1885331Samw 
1898334SJose.Borrego@Sun.COM 	bzero(&handle->handle, sizeof (ndr_hdid_t));
1908334SJose.Borrego@Sun.COM 	handle->clnt = clnt;
191*11337SWilliam.Krier@Sun.COM 	bcopy(&svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t));
1925331Samw 
1938334SJose.Borrego@Sun.COM 	if (ndr_rpc_get_heap(handle) == NULL) {
1948334SJose.Borrego@Sun.COM 		free(clnt);
1955331Samw 		return (-1);
1968334SJose.Borrego@Sun.COM 	}
1978334SJose.Borrego@Sun.COM 
1988334SJose.Borrego@Sun.COM 	rc = ndr_clnt_bind(clnt, service, &clnt->binding);
1998334SJose.Borrego@Sun.COM 	if (NDR_DRC_IS_FAULT(rc)) {
2008334SJose.Borrego@Sun.COM 		(void) smbrdr_close_pipe(fid);
2018334SJose.Borrego@Sun.COM 		ndr_heap_destroy(clnt->heap);
2028334SJose.Borrego@Sun.COM 		free(clnt);
2038334SJose.Borrego@Sun.COM 		handle->clnt = NULL;
2048334SJose.Borrego@Sun.COM 		return (-1);
2058334SJose.Borrego@Sun.COM 	}
2065331Samw 
2075331Samw 	return (0);
2085331Samw }
2095331Samw 
2105331Samw /*
2118334SJose.Borrego@Sun.COM  * Unbind and close the pipe to an RPC service.
2128334SJose.Borrego@Sun.COM  *
2138334SJose.Borrego@Sun.COM  * If the heap has been preserved we need to go through an xa release.
2148334SJose.Borrego@Sun.COM  * The heap is preserved during an RPC call because that's where data
2158334SJose.Borrego@Sun.COM  * returned from the server is stored.
2165331Samw  *
2178334SJose.Borrego@Sun.COM  * Otherwise we destroy the heap directly.
2188334SJose.Borrego@Sun.COM  */
2198334SJose.Borrego@Sun.COM void
2208334SJose.Borrego@Sun.COM ndr_rpc_unbind(mlsvc_handle_t *handle)
2218334SJose.Borrego@Sun.COM {
2228334SJose.Borrego@Sun.COM 	ndr_client_t *clnt = handle->clnt;
2238334SJose.Borrego@Sun.COM 
2248334SJose.Borrego@Sun.COM 	if (clnt->heap_preserved)
2258334SJose.Borrego@Sun.COM 		ndr_clnt_free_heap(clnt);
2268334SJose.Borrego@Sun.COM 	else
2278334SJose.Borrego@Sun.COM 		ndr_heap_destroy(clnt->heap);
2288334SJose.Borrego@Sun.COM 
2298334SJose.Borrego@Sun.COM 	(void) smbrdr_close_pipe(clnt->fid);
2308334SJose.Borrego@Sun.COM 	free(handle->clnt);
2318334SJose.Borrego@Sun.COM 	bzero(handle, sizeof (mlsvc_handle_t));
2328334SJose.Borrego@Sun.COM }
2338334SJose.Borrego@Sun.COM 
2348334SJose.Borrego@Sun.COM /*
2358334SJose.Borrego@Sun.COM  * Call the RPC function identified by opnum.  The remote service is
2368334SJose.Borrego@Sun.COM  * identified by the handle, which should have been initialized by
2378334SJose.Borrego@Sun.COM  * ndr_rpc_bind.
2388334SJose.Borrego@Sun.COM  *
2398334SJose.Borrego@Sun.COM  * If the RPC call is successful (returns 0), the caller must call
2408334SJose.Borrego@Sun.COM  * ndr_rpc_release to release the heap.  Otherwise, we release the
2418334SJose.Borrego@Sun.COM  * heap here.
2425331Samw  */
2435331Samw int
2448334SJose.Borrego@Sun.COM ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params)
2458334SJose.Borrego@Sun.COM {
2468334SJose.Borrego@Sun.COM 	ndr_client_t *clnt = handle->clnt;
2478334SJose.Borrego@Sun.COM 	int rc;
2488334SJose.Borrego@Sun.COM 
2498334SJose.Borrego@Sun.COM 	if (ndr_rpc_get_heap(handle) == NULL)
2508334SJose.Borrego@Sun.COM 		return (-1);
2518334SJose.Borrego@Sun.COM 
2528334SJose.Borrego@Sun.COM 	rc = ndr_clnt_call(clnt->binding, opnum, params);
2538334SJose.Borrego@Sun.COM 
254*11337SWilliam.Krier@Sun.COM 	/*
255*11337SWilliam.Krier@Sun.COM 	 * Always clear the nonull flag to ensure
256*11337SWilliam.Krier@Sun.COM 	 * it is not applied to subsequent calls.
257*11337SWilliam.Krier@Sun.COM 	 */
258*11337SWilliam.Krier@Sun.COM 	clnt->nonull = B_FALSE;
259*11337SWilliam.Krier@Sun.COM 
2608334SJose.Borrego@Sun.COM 	if (NDR_DRC_IS_FAULT(rc)) {
2618334SJose.Borrego@Sun.COM 		ndr_rpc_release(handle);
2628334SJose.Borrego@Sun.COM 		return (-1);
2638334SJose.Borrego@Sun.COM 	}
2648334SJose.Borrego@Sun.COM 
2658334SJose.Borrego@Sun.COM 	return (0);
2668334SJose.Borrego@Sun.COM }
2678334SJose.Borrego@Sun.COM 
2688334SJose.Borrego@Sun.COM /*
269*11337SWilliam.Krier@Sun.COM  * Outgoing strings should not be null terminated.
270*11337SWilliam.Krier@Sun.COM  */
271*11337SWilliam.Krier@Sun.COM void
272*11337SWilliam.Krier@Sun.COM ndr_rpc_set_nonull(mlsvc_handle_t *handle)
273*11337SWilliam.Krier@Sun.COM {
274*11337SWilliam.Krier@Sun.COM 	handle->clnt->nonull = B_TRUE;
275*11337SWilliam.Krier@Sun.COM }
276*11337SWilliam.Krier@Sun.COM 
277*11337SWilliam.Krier@Sun.COM /*
278*11337SWilliam.Krier@Sun.COM  * Return a reference to the server info.
279*11337SWilliam.Krier@Sun.COM  */
280*11337SWilliam.Krier@Sun.COM const srvsvc_server_info_t *
281*11337SWilliam.Krier@Sun.COM ndr_rpc_server_info(mlsvc_handle_t *handle)
282*11337SWilliam.Krier@Sun.COM {
283*11337SWilliam.Krier@Sun.COM 	return (&handle->svinfo);
284*11337SWilliam.Krier@Sun.COM }
285*11337SWilliam.Krier@Sun.COM 
286*11337SWilliam.Krier@Sun.COM /*
287*11337SWilliam.Krier@Sun.COM  * Return the RPC server OS level.
2888334SJose.Borrego@Sun.COM  */
28910717Samw@Sun.COM uint32_t
2908334SJose.Borrego@Sun.COM ndr_rpc_server_os(mlsvc_handle_t *handle)
2915331Samw {
292*11337SWilliam.Krier@Sun.COM 	return (handle->svinfo.sv_os);
2938334SJose.Borrego@Sun.COM }
2948334SJose.Borrego@Sun.COM 
29510504SKeyur.Desai@Sun.COM /*
29610504SKeyur.Desai@Sun.COM  * Get the session key from a bound RPC client handle.
29710504SKeyur.Desai@Sun.COM  *
29810504SKeyur.Desai@Sun.COM  * The key returned is the 16-byte "user session key"
29910504SKeyur.Desai@Sun.COM  * established by the underlying authentication protocol
30010504SKeyur.Desai@Sun.COM  * (either Kerberos or NTLM).  This key is needed for
30110504SKeyur.Desai@Sun.COM  * SAM RPC calls such as SamrSetInformationUser, etc.
30210504SKeyur.Desai@Sun.COM  * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25.
30310504SKeyur.Desai@Sun.COM  *
30410504SKeyur.Desai@Sun.COM  * Returns zero (success) or an errno.
30510504SKeyur.Desai@Sun.COM  */
30610504SKeyur.Desai@Sun.COM int
30710504SKeyur.Desai@Sun.COM ndr_rpc_get_ssnkey(mlsvc_handle_t *handle,
30810504SKeyur.Desai@Sun.COM 	unsigned char *ssn_key, size_t len)
30910504SKeyur.Desai@Sun.COM {
31010504SKeyur.Desai@Sun.COM 	ndr_client_t *clnt = handle->clnt;
31110504SKeyur.Desai@Sun.COM 	int rc;
31210504SKeyur.Desai@Sun.COM 
31310504SKeyur.Desai@Sun.COM 	if (clnt == NULL)
31410504SKeyur.Desai@Sun.COM 		return (EINVAL);
31510504SKeyur.Desai@Sun.COM 
31610504SKeyur.Desai@Sun.COM 	rc = smbrdr_get_ssnkey(clnt->fid, ssn_key, len);
31710504SKeyur.Desai@Sun.COM 	return (rc);
31810504SKeyur.Desai@Sun.COM }
31910504SKeyur.Desai@Sun.COM 
3208334SJose.Borrego@Sun.COM void *
3218334SJose.Borrego@Sun.COM ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size)
3228334SJose.Borrego@Sun.COM {
3238334SJose.Borrego@Sun.COM 	ndr_heap_t *heap;
3248334SJose.Borrego@Sun.COM 
3258334SJose.Borrego@Sun.COM 	if ((heap = ndr_rpc_get_heap(handle)) == NULL)
3268334SJose.Borrego@Sun.COM 		return (NULL);
3278334SJose.Borrego@Sun.COM 
3288334SJose.Borrego@Sun.COM 	return (ndr_heap_malloc(heap, size));
3298334SJose.Borrego@Sun.COM }
3308334SJose.Borrego@Sun.COM 
3318334SJose.Borrego@Sun.COM ndr_heap_t *
3328334SJose.Borrego@Sun.COM ndr_rpc_get_heap(mlsvc_handle_t *handle)
3338334SJose.Borrego@Sun.COM {
3348334SJose.Borrego@Sun.COM 	ndr_client_t *clnt = handle->clnt;
3358334SJose.Borrego@Sun.COM 
3368334SJose.Borrego@Sun.COM 	if (clnt->heap == NULL)
3378334SJose.Borrego@Sun.COM 		clnt->heap = ndr_heap_create();
3388334SJose.Borrego@Sun.COM 
3398334SJose.Borrego@Sun.COM 	return (clnt->heap);
3405331Samw }
3415331Samw 
3425331Samw /*
3438334SJose.Borrego@Sun.COM  * Must be called by RPC clients to free the heap after a successful RPC
3448334SJose.Borrego@Sun.COM  * call, i.e. ndr_rpc_call returned 0.  The caller should take a copy
3458334SJose.Borrego@Sun.COM  * of any data returned by the RPC prior to calling this function because
3468334SJose.Borrego@Sun.COM  * returned data is in the heap.
3475331Samw  */
3485331Samw void
3498334SJose.Borrego@Sun.COM ndr_rpc_release(mlsvc_handle_t *handle)
3508334SJose.Borrego@Sun.COM {
3518334SJose.Borrego@Sun.COM 	ndr_client_t *clnt = handle->clnt;
3528334SJose.Borrego@Sun.COM 
3538334SJose.Borrego@Sun.COM 	if (clnt->heap_preserved)
3548334SJose.Borrego@Sun.COM 		ndr_clnt_free_heap(clnt);
3558334SJose.Borrego@Sun.COM 	else
3568334SJose.Borrego@Sun.COM 		ndr_heap_destroy(clnt->heap);
3578334SJose.Borrego@Sun.COM 
3588334SJose.Borrego@Sun.COM 	clnt->heap = NULL;
3598334SJose.Borrego@Sun.COM }
3608334SJose.Borrego@Sun.COM 
3618334SJose.Borrego@Sun.COM /*
3628334SJose.Borrego@Sun.COM  * Returns true if the handle is null.
3638334SJose.Borrego@Sun.COM  * Otherwise returns false.
3648334SJose.Borrego@Sun.COM  */
3658334SJose.Borrego@Sun.COM boolean_t
3668334SJose.Borrego@Sun.COM ndr_is_null_handle(mlsvc_handle_t *handle)
3675331Samw {
3688334SJose.Borrego@Sun.COM 	static ndr_hdid_t zero_handle;
3698334SJose.Borrego@Sun.COM 
3708334SJose.Borrego@Sun.COM 	if (handle == NULL || handle->clnt == NULL)
3718334SJose.Borrego@Sun.COM 		return (B_TRUE);
3728334SJose.Borrego@Sun.COM 
3738334SJose.Borrego@Sun.COM 	if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t)))
3748334SJose.Borrego@Sun.COM 		return (B_TRUE);
3758334SJose.Borrego@Sun.COM 
3768334SJose.Borrego@Sun.COM 	return (B_FALSE);
3778334SJose.Borrego@Sun.COM }
3788334SJose.Borrego@Sun.COM 
3798334SJose.Borrego@Sun.COM /*
3808334SJose.Borrego@Sun.COM  * Returns true if the handle is the top level bind handle.
3818334SJose.Borrego@Sun.COM  * Otherwise returns false.
3828334SJose.Borrego@Sun.COM  */
3838334SJose.Borrego@Sun.COM boolean_t
3848334SJose.Borrego@Sun.COM ndr_is_bind_handle(mlsvc_handle_t *handle)
3858334SJose.Borrego@Sun.COM {
3868334SJose.Borrego@Sun.COM 	return (handle->clnt->handle == &handle->handle);
3875331Samw }
3885331Samw 
3895331Samw /*
3908334SJose.Borrego@Sun.COM  * Pass the client reference from parent to child.
3915331Samw  */
3928334SJose.Borrego@Sun.COM void
3938334SJose.Borrego@Sun.COM ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent)
3948334SJose.Borrego@Sun.COM {
3958334SJose.Borrego@Sun.COM 	child->clnt = parent->clnt;
396*11337SWilliam.Krier@Sun.COM 	bcopy(&parent->svinfo, &child->svinfo, sizeof (srvsvc_server_info_t));
3978334SJose.Borrego@Sun.COM }
3988334SJose.Borrego@Sun.COM 
3998334SJose.Borrego@Sun.COM void
4008334SJose.Borrego@Sun.COM ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
4015331Samw {
4028334SJose.Borrego@Sun.COM 	ndr_service_t *svc;
4038334SJose.Borrego@Sun.COM 	char *name = "NDR RPC";
4048334SJose.Borrego@Sun.COM 	char *s = "unknown";
4058334SJose.Borrego@Sun.COM 
4068334SJose.Borrego@Sun.COM 	if (status == 0)
4078334SJose.Borrego@Sun.COM 		s = "success";
4088334SJose.Borrego@Sun.COM 	else if (NT_SC_IS_ERROR(status))
4098334SJose.Borrego@Sun.COM 		s = "error";
4108334SJose.Borrego@Sun.COM 	else if (NT_SC_IS_WARNING(status))
4118334SJose.Borrego@Sun.COM 		s = "warning";
4128334SJose.Borrego@Sun.COM 	else if (NT_SC_IS_INFO(status))
4138334SJose.Borrego@Sun.COM 		s = "info";
4148334SJose.Borrego@Sun.COM 
4158334SJose.Borrego@Sun.COM 	if (handle) {
4168334SJose.Borrego@Sun.COM 		svc = handle->clnt->binding->service;
4178334SJose.Borrego@Sun.COM 		name = svc->name;
4188334SJose.Borrego@Sun.COM 	}
4195331Samw 
4208334SJose.Borrego@Sun.COM 	smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
4218334SJose.Borrego@Sun.COM 	    name, opnum, s, xlate_nt_status(status), status);
4228334SJose.Borrego@Sun.COM }
4238334SJose.Borrego@Sun.COM 
4248334SJose.Borrego@Sun.COM /*
4258334SJose.Borrego@Sun.COM  * The following functions provide the client callback interface.
4268334SJose.Borrego@Sun.COM  * If the caller hasn't provided a heap, create one here.
4278334SJose.Borrego@Sun.COM  */
4288334SJose.Borrego@Sun.COM static int
4298334SJose.Borrego@Sun.COM ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa)
4308334SJose.Borrego@Sun.COM {
431*11337SWilliam.Krier@Sun.COM 	ndr_stream_t	*recv_nds = &mxa->recv_nds;
432*11337SWilliam.Krier@Sun.COM 	ndr_stream_t	*send_nds = &mxa->send_nds;
433*11337SWilliam.Krier@Sun.COM 	ndr_heap_t	*heap = clnt->heap;
434*11337SWilliam.Krier@Sun.COM 	int		rc;
4358334SJose.Borrego@Sun.COM 
4368334SJose.Borrego@Sun.COM 	if (heap == NULL) {
4378334SJose.Borrego@Sun.COM 		if ((heap = ndr_heap_create()) == NULL)
4385331Samw 			return (-1);
4398334SJose.Borrego@Sun.COM 
4408334SJose.Borrego@Sun.COM 		clnt->heap = heap;
4415331Samw 	}
4425331Samw 
4435331Samw 	mxa->heap = heap;
4445331Samw 
445*11337SWilliam.Krier@Sun.COM 	rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap);
446*11337SWilliam.Krier@Sun.COM 	if (rc == 0)
447*11337SWilliam.Krier@Sun.COM 		rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT,
448*11337SWilliam.Krier@Sun.COM 		    NDR_MODE_RETURN_RECV, heap);
449*11337SWilliam.Krier@Sun.COM 
450*11337SWilliam.Krier@Sun.COM 	if (rc != 0) {
451*11337SWilliam.Krier@Sun.COM 		nds_destruct(&mxa->recv_nds);
452*11337SWilliam.Krier@Sun.COM 		nds_destruct(&mxa->send_nds);
453*11337SWilliam.Krier@Sun.COM 		ndr_heap_destroy(mxa->heap);
454*11337SWilliam.Krier@Sun.COM 		mxa->heap = NULL;
455*11337SWilliam.Krier@Sun.COM 		clnt->heap = NULL;
456*11337SWilliam.Krier@Sun.COM 		return (-1);
457*11337SWilliam.Krier@Sun.COM 	}
458*11337SWilliam.Krier@Sun.COM 
459*11337SWilliam.Krier@Sun.COM 	if (clnt->nonull)
460*11337SWilliam.Krier@Sun.COM 		NDS_SETF(send_nds, NDS_F_NONULL);
461*11337SWilliam.Krier@Sun.COM 
4625331Samw 	return (0);
4635331Samw }
4645331Samw 
4655331Samw /*
4665331Samw  * This is the entry pointy for an RPC client call exchange with
4675331Samw  * a server, which will result in an smbrdr SmbTransact request.
4685331Samw  *
4695331Samw  * SmbTransact should return the number of bytes received, which
4705331Samw  * we record as the PDU size, or a negative error code.
4715331Samw  */
4725331Samw static int
4738334SJose.Borrego@Sun.COM ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa)
4745331Samw {
4758334SJose.Borrego@Sun.COM 	ndr_stream_t *recv_nds = &mxa->recv_nds;
4768334SJose.Borrego@Sun.COM 	ndr_stream_t *send_nds = &mxa->send_nds;
4778334SJose.Borrego@Sun.COM 	int nbytes;
4785331Samw 
4798334SJose.Borrego@Sun.COM 	nbytes = smbrdr_transact(clnt->fid,
4808334SJose.Borrego@Sun.COM 	    (char *)send_nds->pdu_base_offset, send_nds->pdu_size,
4818334SJose.Borrego@Sun.COM 	    (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size);
4825331Samw 
4838334SJose.Borrego@Sun.COM 	if (nbytes < 0) {
4848334SJose.Borrego@Sun.COM 		recv_nds->pdu_size = 0;
4858334SJose.Borrego@Sun.COM 		return (-1);
4868334SJose.Borrego@Sun.COM 	}
4875331Samw 
4888334SJose.Borrego@Sun.COM 	recv_nds->pdu_size = nbytes;
4898334SJose.Borrego@Sun.COM 	return (nbytes);
4905331Samw }
4915331Samw 
4925331Samw /*
4935331Samw  * This entry point will be invoked if the xa-exchange response contained
4945331Samw  * only the first fragment of a multi-fragment response.  The RPC client
4955331Samw  * code will then make repeated xa-read requests to obtain the remaining
4965331Samw  * fragments, which will result in smbrdr SmbReadX requests.
4975331Samw  *
4985331Samw  * SmbReadX should return the number of bytes received, in which case we
4995331Samw  * expand the PDU size to include the received data, or a negative error
5005331Samw  * code.
5015331Samw  */
5025331Samw static int
5038334SJose.Borrego@Sun.COM ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa)
5045331Samw {
5058334SJose.Borrego@Sun.COM 	ndr_stream_t *nds = &mxa->recv_nds;
5065331Samw 	int len;
5078334SJose.Borrego@Sun.COM 	int nbytes;
5085331Samw 
5098334SJose.Borrego@Sun.COM 	if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0)
5105331Samw 		return (-1);
5115331Samw 
5128334SJose.Borrego@Sun.COM 	nbytes = smbrdr_readx(clnt->fid,
5138334SJose.Borrego@Sun.COM 	    (char *)nds->pdu_base_offset + nds->pdu_size, len);
5145331Samw 
5158334SJose.Borrego@Sun.COM 	if (nbytes < 0)
5165331Samw 		return (-1);
5175331Samw 
5188334SJose.Borrego@Sun.COM 	nds->pdu_size += nbytes;
5195331Samw 
5208334SJose.Borrego@Sun.COM 	if (nds->pdu_size > nds->pdu_max_size) {
5218334SJose.Borrego@Sun.COM 		nds->pdu_size = nds->pdu_max_size;
5225331Samw 		return (-1);
5235331Samw 	}
5245331Samw 
5258334SJose.Borrego@Sun.COM 	return (nbytes);
5265331Samw }
5275331Samw 
5285331Samw /*
5298334SJose.Borrego@Sun.COM  * Preserve the heap so that the client application has access to data
5308334SJose.Borrego@Sun.COM  * returned from the server after an RPC call.
5315331Samw  */
5328334SJose.Borrego@Sun.COM static void
5338334SJose.Borrego@Sun.COM ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa)
5345331Samw {
5358334SJose.Borrego@Sun.COM 	assert(clnt->heap == mxa->heap);
5365331Samw 
5378334SJose.Borrego@Sun.COM 	clnt->heap_preserved = B_TRUE;
5385331Samw 	mxa->heap = NULL;
5395331Samw }
5405331Samw 
5415331Samw /*
5428334SJose.Borrego@Sun.COM  * Dispose of the transaction streams.  If the heap has not been
5438334SJose.Borrego@Sun.COM  * preserved, we can destroy it here.
5445331Samw  */
5458334SJose.Borrego@Sun.COM static void
5468334SJose.Borrego@Sun.COM ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa)
5475331Samw {
5488334SJose.Borrego@Sun.COM 	nds_destruct(&mxa->recv_nds);
5498334SJose.Borrego@Sun.COM 	nds_destruct(&mxa->send_nds);
5508334SJose.Borrego@Sun.COM 
5518334SJose.Borrego@Sun.COM 	if (!clnt->heap_preserved) {
5528334SJose.Borrego@Sun.COM 		ndr_heap_destroy(mxa->heap);
5538334SJose.Borrego@Sun.COM 		mxa->heap = NULL;
5548334SJose.Borrego@Sun.COM 		clnt->heap = NULL;
5555331Samw 	}
5565331Samw }
5575331Samw 
5585331Samw /*
5598334SJose.Borrego@Sun.COM  * Dispose of a preserved heap.
5605331Samw  */
5615331Samw static void
5628334SJose.Borrego@Sun.COM ndr_xa_release(ndr_client_t *clnt)
5635331Samw {
5648334SJose.Borrego@Sun.COM 	if (clnt->heap_preserved) {
5658334SJose.Borrego@Sun.COM 		ndr_heap_destroy(clnt->heap);
5668334SJose.Borrego@Sun.COM 		clnt->heap = NULL;
5678334SJose.Borrego@Sun.COM 		clnt->heap_preserved = B_FALSE;
5685331Samw 	}
5695331Samw }
57010717Samw@Sun.COM 
57110717Samw@Sun.COM /*
57210717Samw@Sun.COM  * Lookup platform, type and version information about a server.
57310717Samw@Sun.COM  * If the cache doesn't already contain the data, contact the server and
57410717Samw@Sun.COM  * cache the response before returning the server info to the caller.
575*11337SWilliam.Krier@Sun.COM  *
576*11337SWilliam.Krier@Sun.COM  * We don't provide the name or comment for now, which avoids the need
577*11337SWilliam.Krier@Sun.COM  * to deal with unnecessary memory management.
57810717Samw@Sun.COM  */
57910717Samw@Sun.COM static int
58010717Samw@Sun.COM ndr_svinfo_lookup(char *server, char *domain, srvsvc_server_info_t *svinfo)
58110717Samw@Sun.COM {
58210717Samw@Sun.COM 	ndr_svinfo_t *svi;
58310717Samw@Sun.COM 
58410717Samw@Sun.COM 	(void) mutex_lock(&ndr_svlist.svl_mtx);
58510717Samw@Sun.COM 	assert(ndr_svlist.svl_init == B_TRUE);
58610717Samw@Sun.COM 
58710717Samw@Sun.COM 	svi = list_head(&ndr_svlist.svl_list);
58810717Samw@Sun.COM 	while (svi != NULL) {
58910717Samw@Sun.COM 		if (ndr_svinfo_expired(svi)) {
59010717Samw@Sun.COM 			svi = list_head(&ndr_svlist.svl_list);
59110717Samw@Sun.COM 			continue;
59210717Samw@Sun.COM 		}
59310717Samw@Sun.COM 
59410717Samw@Sun.COM 		if (ndr_svinfo_match(server, domain, svi)) {
59510717Samw@Sun.COM 			bcopy(&svi->svi_svinfo, svinfo,
59610717Samw@Sun.COM 			    sizeof (srvsvc_server_info_t));
597*11337SWilliam.Krier@Sun.COM 			svinfo->sv_name = NULL;
598*11337SWilliam.Krier@Sun.COM 			svinfo->sv_comment = NULL;
59910717Samw@Sun.COM 			(void) mutex_unlock(&ndr_svlist.svl_mtx);
60010717Samw@Sun.COM 			return (0);
60110717Samw@Sun.COM 		}
60210717Samw@Sun.COM 
60310717Samw@Sun.COM 		svi = list_next(&ndr_svlist.svl_list, svi);
60410717Samw@Sun.COM 	}
60510717Samw@Sun.COM 
60610717Samw@Sun.COM 	if ((svi = malloc(sizeof (ndr_svinfo_t))) == NULL) {
60710717Samw@Sun.COM 		(void) mutex_unlock(&ndr_svlist.svl_mtx);
60810717Samw@Sun.COM 		return (-1);
60910717Samw@Sun.COM 	}
61010717Samw@Sun.COM 
61110717Samw@Sun.COM 	if (srvsvc_net_server_getinfo(server, domain, &svi->svi_svinfo) < 0) {
61210717Samw@Sun.COM 		(void) mutex_unlock(&ndr_svlist.svl_mtx);
61310717Samw@Sun.COM 		free(svi);
61410717Samw@Sun.COM 		return (-1);
61510717Samw@Sun.COM 	}
61610717Samw@Sun.COM 
61710717Samw@Sun.COM 	(void) time(&svi->svi_tcached);
61810717Samw@Sun.COM 	(void) strlcpy(svi->svi_server, server, MAXNAMELEN);
61910717Samw@Sun.COM 	(void) strlcpy(svi->svi_domain, domain, MAXNAMELEN);
62010717Samw@Sun.COM 	list_insert_tail(&ndr_svlist.svl_list, svi);
62110717Samw@Sun.COM 	bcopy(&svi->svi_svinfo, svinfo, sizeof (srvsvc_server_info_t));
622*11337SWilliam.Krier@Sun.COM 	svinfo->sv_name = NULL;
623*11337SWilliam.Krier@Sun.COM 	svinfo->sv_comment = NULL;
62410717Samw@Sun.COM 	(void) mutex_unlock(&ndr_svlist.svl_mtx);
62510717Samw@Sun.COM 	return (0);
62610717Samw@Sun.COM }
62710717Samw@Sun.COM 
62810717Samw@Sun.COM static boolean_t
62910717Samw@Sun.COM ndr_svinfo_match(const char *server, const char *domain,
63010717Samw@Sun.COM     const ndr_svinfo_t *svi)
63110717Samw@Sun.COM {
63210966SJordan.Brown@Sun.COM 	if ((smb_strcasecmp(server, svi->svi_server, 0) == 0) &&
63310966SJordan.Brown@Sun.COM 	    (smb_strcasecmp(domain, svi->svi_domain, 0) == 0)) {
63410717Samw@Sun.COM 		return (B_TRUE);
63510717Samw@Sun.COM 	}
63610717Samw@Sun.COM 
63710717Samw@Sun.COM 	return (B_FALSE);
63810717Samw@Sun.COM }
63910717Samw@Sun.COM 
64010717Samw@Sun.COM /*
64110717Samw@Sun.COM  * If the server info in the cache has expired, discard it and return true.
64210717Samw@Sun.COM  * Otherwise return false.
64310717Samw@Sun.COM  *
64410717Samw@Sun.COM  * This is a private function to support ndr_svinfo_lookup() that assumes
64510717Samw@Sun.COM  * the list mutex is held.
64610717Samw@Sun.COM  */
64710717Samw@Sun.COM static boolean_t
64810717Samw@Sun.COM ndr_svinfo_expired(ndr_svinfo_t *svi)
64910717Samw@Sun.COM {
65010717Samw@Sun.COM 	time_t	tnow;
65110717Samw@Sun.COM 
65210717Samw@Sun.COM 	(void) time(&tnow);
65310717Samw@Sun.COM 
65410717Samw@Sun.COM 	if (difftime(tnow, svi->svi_tcached) > NDR_SVINFO_TIMEOUT) {
65510717Samw@Sun.COM 		list_remove(&ndr_svlist.svl_list, svi);
65610717Samw@Sun.COM 		free(svi->svi_svinfo.sv_name);
65710717Samw@Sun.COM 		free(svi->svi_svinfo.sv_comment);
65810717Samw@Sun.COM 		free(svi);
65910717Samw@Sun.COM 		return (B_TRUE);
66010717Samw@Sun.COM 	}
66110717Samw@Sun.COM 
66210717Samw@Sun.COM 	return (B_FALSE);
66310717Samw@Sun.COM }
664