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 30*10717Samw@Sun.COM #include <sys/types.h> 315331Samw #include <sys/errno.h> 32*10717Samw@Sun.COM #include <time.h> 335331Samw #include <strings.h> 348334SJose.Borrego@Sun.COM #include <assert.h> 35*10717Samw@Sun.COM #include <thread.h> 36*10717Samw@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> 415331Samw 42*10717Samw@Sun.COM /* 43*10717Samw@Sun.COM * Server info cache entry expiration in seconds. 44*10717Samw@Sun.COM */ 45*10717Samw@Sun.COM #define NDR_SVINFO_TIMEOUT 1800 46*10717Samw@Sun.COM 47*10717Samw@Sun.COM typedef struct ndr_svinfo { 48*10717Samw@Sun.COM list_node_t svi_lnd; 49*10717Samw@Sun.COM time_t svi_tcached; 50*10717Samw@Sun.COM char svi_server[MAXNAMELEN]; 51*10717Samw@Sun.COM char svi_domain[MAXNAMELEN]; 52*10717Samw@Sun.COM srvsvc_server_info_t svi_svinfo; 53*10717Samw@Sun.COM } ndr_svinfo_t; 54*10717Samw@Sun.COM 55*10717Samw@Sun.COM typedef struct ndr_svlist { 56*10717Samw@Sun.COM list_t svl_list; 57*10717Samw@Sun.COM mutex_t svl_mtx; 58*10717Samw@Sun.COM boolean_t svl_init; 59*10717Samw@Sun.COM } ndr_svlist_t; 60*10717Samw@Sun.COM 61*10717Samw@Sun.COM static ndr_svlist_t ndr_svlist; 62*10717Samw@Sun.COM 638334SJose.Borrego@Sun.COM static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 648334SJose.Borrego@Sun.COM static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 658334SJose.Borrego@Sun.COM static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 668334SJose.Borrego@Sun.COM static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 678334SJose.Borrego@Sun.COM static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 688334SJose.Borrego@Sun.COM static void ndr_xa_release(ndr_client_t *); 695331Samw 70*10717Samw@Sun.COM static int ndr_svinfo_lookup(char *, char *, srvsvc_server_info_t *); 71*10717Samw@Sun.COM static boolean_t ndr_svinfo_match(const char *, const char *, const 72*10717Samw@Sun.COM ndr_svinfo_t *); 73*10717Samw@Sun.COM static boolean_t ndr_svinfo_expired(ndr_svinfo_t *); 74*10717Samw@Sun.COM 75*10717Samw@Sun.COM /* 76*10717Samw@Sun.COM * Initialize the RPC client interface: create the server info cache. 77*10717Samw@Sun.COM */ 78*10717Samw@Sun.COM void 79*10717Samw@Sun.COM ndr_rpc_init(void) 80*10717Samw@Sun.COM { 81*10717Samw@Sun.COM (void) mutex_lock(&ndr_svlist.svl_mtx); 82*10717Samw@Sun.COM 83*10717Samw@Sun.COM if (!ndr_svlist.svl_init) { 84*10717Samw@Sun.COM list_create(&ndr_svlist.svl_list, sizeof (ndr_svinfo_t), 85*10717Samw@Sun.COM offsetof(ndr_svinfo_t, svi_lnd)); 86*10717Samw@Sun.COM ndr_svlist.svl_init = B_TRUE; 87*10717Samw@Sun.COM } 88*10717Samw@Sun.COM 89*10717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 90*10717Samw@Sun.COM } 91*10717Samw@Sun.COM 92*10717Samw@Sun.COM /* 93*10717Samw@Sun.COM * Terminate the RPC client interface: flush and destroy the server info 94*10717Samw@Sun.COM * cache. 95*10717Samw@Sun.COM */ 96*10717Samw@Sun.COM void 97*10717Samw@Sun.COM ndr_rpc_fini(void) 98*10717Samw@Sun.COM { 99*10717Samw@Sun.COM ndr_svinfo_t *svi; 100*10717Samw@Sun.COM 101*10717Samw@Sun.COM (void) mutex_lock(&ndr_svlist.svl_mtx); 102*10717Samw@Sun.COM 103*10717Samw@Sun.COM if (ndr_svlist.svl_init) { 104*10717Samw@Sun.COM while ((svi = list_head(&ndr_svlist.svl_list)) != NULL) { 105*10717Samw@Sun.COM list_remove(&ndr_svlist.svl_list, svi); 106*10717Samw@Sun.COM free(svi->svi_svinfo.sv_name); 107*10717Samw@Sun.COM free(svi->svi_svinfo.sv_comment); 108*10717Samw@Sun.COM free(svi); 109*10717Samw@Sun.COM } 110*10717Samw@Sun.COM 111*10717Samw@Sun.COM list_destroy(&ndr_svlist.svl_list); 112*10717Samw@Sun.COM ndr_svlist.svl_init = B_FALSE; 113*10717Samw@Sun.COM } 114*10717Samw@Sun.COM 115*10717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 116*10717Samw@Sun.COM } 117*10717Samw@Sun.COM 1185331Samw /* 1198334SJose.Borrego@Sun.COM * This call must be made to initialize an RPC client structure and bind 1208334SJose.Borrego@Sun.COM * to the remote service before any RPCs can be exchanged with that service. 1215331Samw * 1228334SJose.Borrego@Sun.COM * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle 1238334SJose.Borrego@Sun.COM * with the client context for an instance of the interface. The handle 1248334SJose.Borrego@Sun.COM * is zeroed to ensure that it doesn't look like a valid handle - 1258334SJose.Borrego@Sun.COM * handle content is provided by the remove service. 1265331Samw * 1278334SJose.Borrego@Sun.COM * The client points to this top-level handle so that we know when to 1288334SJose.Borrego@Sun.COM * unbind and teardown the connection. As each handle is initialized it 1298334SJose.Borrego@Sun.COM * will inherit a reference to the client context. 1305331Samw */ 1315331Samw int 1328334SJose.Borrego@Sun.COM ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 1338334SJose.Borrego@Sun.COM char *username, const char *service) 1345331Samw { 1358334SJose.Borrego@Sun.COM ndr_client_t *clnt; 1368334SJose.Borrego@Sun.COM ndr_service_t *svc; 137*10717Samw@Sun.COM srvsvc_server_info_t svinfo; 138*10717Samw@Sun.COM int remote_os; 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 149*10717Samw@Sun.COM /* 150*10717Samw@Sun.COM * Set the default based on the assumption that most 151*10717Samw@Sun.COM * servers will be Windows 2000 or later. 152*10717Samw@Sun.COM * Don't lookup the svinfo if this is a SRVSVC request 153*10717Samw@Sun.COM * because the SRVSVC is used to get the server info. 154*10717Samw@Sun.COM * None of the SRVSVC calls depend on the remote OS. 155*10717Samw@Sun.COM */ 156*10717Samw@Sun.COM remote_os = NATIVE_OS_WIN2000; 157*10717Samw@Sun.COM 158*10717Samw@Sun.COM if (strcasecmp(service, "SRVSVC") != 0) { 159*10717Samw@Sun.COM if (ndr_svinfo_lookup(server, domain, &svinfo) == 0) 160*10717Samw@Sun.COM remote_os = svinfo.sv_os; 161*10717Samw@Sun.COM } 162*10717Samw@Sun.COM 1638334SJose.Borrego@Sun.COM if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) 1648334SJose.Borrego@Sun.COM return (-1); 1655331Samw 1668334SJose.Borrego@Sun.COM fid = smbrdr_open_pipe(server, domain, username, svc->endpoint); 1678334SJose.Borrego@Sun.COM if (fid < 0) { 1688334SJose.Borrego@Sun.COM free(clnt); 1695331Samw return (-1); 1705331Samw } 1715331Samw 1728334SJose.Borrego@Sun.COM bzero(clnt, sizeof (ndr_client_t)); 1738334SJose.Borrego@Sun.COM clnt->handle = &handle->handle; 1748334SJose.Borrego@Sun.COM clnt->fid = fid; 1758334SJose.Borrego@Sun.COM 1768334SJose.Borrego@Sun.COM ndr_svc_binding_pool_init(&clnt->binding_list, 1778334SJose.Borrego@Sun.COM clnt->binding_pool, NDR_N_BINDING_POOL); 1788334SJose.Borrego@Sun.COM 1798334SJose.Borrego@Sun.COM clnt->xa_init = ndr_xa_init; 1808334SJose.Borrego@Sun.COM clnt->xa_exchange = ndr_xa_exchange; 1818334SJose.Borrego@Sun.COM clnt->xa_read = ndr_xa_read; 1828334SJose.Borrego@Sun.COM clnt->xa_preserve = ndr_xa_preserve; 1838334SJose.Borrego@Sun.COM clnt->xa_destruct = ndr_xa_destruct; 1848334SJose.Borrego@Sun.COM clnt->xa_release = ndr_xa_release; 1855331Samw 1868334SJose.Borrego@Sun.COM bzero(&handle->handle, sizeof (ndr_hdid_t)); 1878334SJose.Borrego@Sun.COM handle->clnt = clnt; 188*10717Samw@Sun.COM handle->remote_os = remote_os; 1895331Samw 1908334SJose.Borrego@Sun.COM if (ndr_rpc_get_heap(handle) == NULL) { 1918334SJose.Borrego@Sun.COM free(clnt); 1925331Samw return (-1); 1938334SJose.Borrego@Sun.COM } 1948334SJose.Borrego@Sun.COM 1958334SJose.Borrego@Sun.COM rc = ndr_clnt_bind(clnt, service, &clnt->binding); 1968334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) { 1978334SJose.Borrego@Sun.COM (void) smbrdr_close_pipe(fid); 1988334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 1998334SJose.Borrego@Sun.COM free(clnt); 2008334SJose.Borrego@Sun.COM handle->clnt = NULL; 2018334SJose.Borrego@Sun.COM return (-1); 2028334SJose.Borrego@Sun.COM } 2035331Samw 2045331Samw return (0); 2055331Samw } 2065331Samw 2075331Samw /* 2088334SJose.Borrego@Sun.COM * Unbind and close the pipe to an RPC service. 2098334SJose.Borrego@Sun.COM * 2108334SJose.Borrego@Sun.COM * If the heap has been preserved we need to go through an xa release. 2118334SJose.Borrego@Sun.COM * The heap is preserved during an RPC call because that's where data 2128334SJose.Borrego@Sun.COM * returned from the server is stored. 2135331Samw * 2148334SJose.Borrego@Sun.COM * Otherwise we destroy the heap directly. 2158334SJose.Borrego@Sun.COM */ 2168334SJose.Borrego@Sun.COM void 2178334SJose.Borrego@Sun.COM ndr_rpc_unbind(mlsvc_handle_t *handle) 2188334SJose.Borrego@Sun.COM { 2198334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 2208334SJose.Borrego@Sun.COM 2218334SJose.Borrego@Sun.COM if (clnt->heap_preserved) 2228334SJose.Borrego@Sun.COM ndr_clnt_free_heap(clnt); 2238334SJose.Borrego@Sun.COM else 2248334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 2258334SJose.Borrego@Sun.COM 2268334SJose.Borrego@Sun.COM (void) smbrdr_close_pipe(clnt->fid); 2278334SJose.Borrego@Sun.COM free(handle->clnt); 2288334SJose.Borrego@Sun.COM bzero(handle, sizeof (mlsvc_handle_t)); 2298334SJose.Borrego@Sun.COM } 2308334SJose.Borrego@Sun.COM 2318334SJose.Borrego@Sun.COM /* 2328334SJose.Borrego@Sun.COM * Call the RPC function identified by opnum. The remote service is 2338334SJose.Borrego@Sun.COM * identified by the handle, which should have been initialized by 2348334SJose.Borrego@Sun.COM * ndr_rpc_bind. 2358334SJose.Borrego@Sun.COM * 2368334SJose.Borrego@Sun.COM * If the RPC call is successful (returns 0), the caller must call 2378334SJose.Borrego@Sun.COM * ndr_rpc_release to release the heap. Otherwise, we release the 2388334SJose.Borrego@Sun.COM * heap here. 2395331Samw */ 2405331Samw int 2418334SJose.Borrego@Sun.COM ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) 2428334SJose.Borrego@Sun.COM { 2438334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 2448334SJose.Borrego@Sun.COM int rc; 2458334SJose.Borrego@Sun.COM 2468334SJose.Borrego@Sun.COM if (ndr_rpc_get_heap(handle) == NULL) 2478334SJose.Borrego@Sun.COM return (-1); 2488334SJose.Borrego@Sun.COM 2498334SJose.Borrego@Sun.COM rc = ndr_clnt_call(clnt->binding, opnum, params); 2508334SJose.Borrego@Sun.COM 2518334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) { 2528334SJose.Borrego@Sun.COM ndr_rpc_release(handle); 2538334SJose.Borrego@Sun.COM return (-1); 2548334SJose.Borrego@Sun.COM } 2558334SJose.Borrego@Sun.COM 2568334SJose.Borrego@Sun.COM return (0); 2578334SJose.Borrego@Sun.COM } 2588334SJose.Borrego@Sun.COM 2598334SJose.Borrego@Sun.COM /* 2608334SJose.Borrego@Sun.COM * Returns the Native-OS of the RPC server. 2618334SJose.Borrego@Sun.COM */ 262*10717Samw@Sun.COM uint32_t 2638334SJose.Borrego@Sun.COM ndr_rpc_server_os(mlsvc_handle_t *handle) 2645331Samw { 2658334SJose.Borrego@Sun.COM return (handle->remote_os); 2668334SJose.Borrego@Sun.COM } 2678334SJose.Borrego@Sun.COM 26810504SKeyur.Desai@Sun.COM /* 26910504SKeyur.Desai@Sun.COM * Get the session key from a bound RPC client handle. 27010504SKeyur.Desai@Sun.COM * 27110504SKeyur.Desai@Sun.COM * The key returned is the 16-byte "user session key" 27210504SKeyur.Desai@Sun.COM * established by the underlying authentication protocol 27310504SKeyur.Desai@Sun.COM * (either Kerberos or NTLM). This key is needed for 27410504SKeyur.Desai@Sun.COM * SAM RPC calls such as SamrSetInformationUser, etc. 27510504SKeyur.Desai@Sun.COM * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. 27610504SKeyur.Desai@Sun.COM * 27710504SKeyur.Desai@Sun.COM * Returns zero (success) or an errno. 27810504SKeyur.Desai@Sun.COM */ 27910504SKeyur.Desai@Sun.COM int 28010504SKeyur.Desai@Sun.COM ndr_rpc_get_ssnkey(mlsvc_handle_t *handle, 28110504SKeyur.Desai@Sun.COM unsigned char *ssn_key, size_t len) 28210504SKeyur.Desai@Sun.COM { 28310504SKeyur.Desai@Sun.COM ndr_client_t *clnt = handle->clnt; 28410504SKeyur.Desai@Sun.COM int rc; 28510504SKeyur.Desai@Sun.COM 28610504SKeyur.Desai@Sun.COM if (clnt == NULL) 28710504SKeyur.Desai@Sun.COM return (EINVAL); 28810504SKeyur.Desai@Sun.COM 28910504SKeyur.Desai@Sun.COM rc = smbrdr_get_ssnkey(clnt->fid, ssn_key, len); 29010504SKeyur.Desai@Sun.COM return (rc); 29110504SKeyur.Desai@Sun.COM } 29210504SKeyur.Desai@Sun.COM 2938334SJose.Borrego@Sun.COM void * 2948334SJose.Borrego@Sun.COM ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 2958334SJose.Borrego@Sun.COM { 2968334SJose.Borrego@Sun.COM ndr_heap_t *heap; 2978334SJose.Borrego@Sun.COM 2988334SJose.Borrego@Sun.COM if ((heap = ndr_rpc_get_heap(handle)) == NULL) 2998334SJose.Borrego@Sun.COM return (NULL); 3008334SJose.Borrego@Sun.COM 3018334SJose.Borrego@Sun.COM return (ndr_heap_malloc(heap, size)); 3028334SJose.Borrego@Sun.COM } 3038334SJose.Borrego@Sun.COM 3048334SJose.Borrego@Sun.COM ndr_heap_t * 3058334SJose.Borrego@Sun.COM ndr_rpc_get_heap(mlsvc_handle_t *handle) 3068334SJose.Borrego@Sun.COM { 3078334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 3088334SJose.Borrego@Sun.COM 3098334SJose.Borrego@Sun.COM if (clnt->heap == NULL) 3108334SJose.Borrego@Sun.COM clnt->heap = ndr_heap_create(); 3118334SJose.Borrego@Sun.COM 3128334SJose.Borrego@Sun.COM return (clnt->heap); 3135331Samw } 3145331Samw 3155331Samw /* 3168334SJose.Borrego@Sun.COM * Must be called by RPC clients to free the heap after a successful RPC 3178334SJose.Borrego@Sun.COM * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 3188334SJose.Borrego@Sun.COM * of any data returned by the RPC prior to calling this function because 3198334SJose.Borrego@Sun.COM * returned data is in the heap. 3205331Samw */ 3215331Samw void 3228334SJose.Borrego@Sun.COM ndr_rpc_release(mlsvc_handle_t *handle) 3238334SJose.Borrego@Sun.COM { 3248334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 3258334SJose.Borrego@Sun.COM 3268334SJose.Borrego@Sun.COM if (clnt->heap_preserved) 3278334SJose.Borrego@Sun.COM ndr_clnt_free_heap(clnt); 3288334SJose.Borrego@Sun.COM else 3298334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 3308334SJose.Borrego@Sun.COM 3318334SJose.Borrego@Sun.COM clnt->heap = NULL; 3328334SJose.Borrego@Sun.COM } 3338334SJose.Borrego@Sun.COM 3348334SJose.Borrego@Sun.COM /* 3358334SJose.Borrego@Sun.COM * Returns true if the handle is null. 3368334SJose.Borrego@Sun.COM * Otherwise returns false. 3378334SJose.Borrego@Sun.COM */ 3388334SJose.Borrego@Sun.COM boolean_t 3398334SJose.Borrego@Sun.COM ndr_is_null_handle(mlsvc_handle_t *handle) 3405331Samw { 3418334SJose.Borrego@Sun.COM static ndr_hdid_t zero_handle; 3428334SJose.Borrego@Sun.COM 3438334SJose.Borrego@Sun.COM if (handle == NULL || handle->clnt == NULL) 3448334SJose.Borrego@Sun.COM return (B_TRUE); 3458334SJose.Borrego@Sun.COM 3468334SJose.Borrego@Sun.COM if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 3478334SJose.Borrego@Sun.COM return (B_TRUE); 3488334SJose.Borrego@Sun.COM 3498334SJose.Borrego@Sun.COM return (B_FALSE); 3508334SJose.Borrego@Sun.COM } 3518334SJose.Borrego@Sun.COM 3528334SJose.Borrego@Sun.COM /* 3538334SJose.Borrego@Sun.COM * Returns true if the handle is the top level bind handle. 3548334SJose.Borrego@Sun.COM * Otherwise returns false. 3558334SJose.Borrego@Sun.COM */ 3568334SJose.Borrego@Sun.COM boolean_t 3578334SJose.Borrego@Sun.COM ndr_is_bind_handle(mlsvc_handle_t *handle) 3588334SJose.Borrego@Sun.COM { 3598334SJose.Borrego@Sun.COM return (handle->clnt->handle == &handle->handle); 3605331Samw } 3615331Samw 3625331Samw /* 3638334SJose.Borrego@Sun.COM * Pass the client reference from parent to child. 3645331Samw */ 3658334SJose.Borrego@Sun.COM void 3668334SJose.Borrego@Sun.COM ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 3678334SJose.Borrego@Sun.COM { 3688334SJose.Borrego@Sun.COM child->clnt = parent->clnt; 3698334SJose.Borrego@Sun.COM child->remote_os = parent->remote_os; 3708334SJose.Borrego@Sun.COM } 3718334SJose.Borrego@Sun.COM 3728334SJose.Borrego@Sun.COM void 3738334SJose.Borrego@Sun.COM ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 3745331Samw { 3758334SJose.Borrego@Sun.COM ndr_service_t *svc; 3768334SJose.Borrego@Sun.COM char *name = "NDR RPC"; 3778334SJose.Borrego@Sun.COM char *s = "unknown"; 3788334SJose.Borrego@Sun.COM 3798334SJose.Borrego@Sun.COM if (status == 0) 3808334SJose.Borrego@Sun.COM s = "success"; 3818334SJose.Borrego@Sun.COM else if (NT_SC_IS_ERROR(status)) 3828334SJose.Borrego@Sun.COM s = "error"; 3838334SJose.Borrego@Sun.COM else if (NT_SC_IS_WARNING(status)) 3848334SJose.Borrego@Sun.COM s = "warning"; 3858334SJose.Borrego@Sun.COM else if (NT_SC_IS_INFO(status)) 3868334SJose.Borrego@Sun.COM s = "info"; 3878334SJose.Borrego@Sun.COM 3888334SJose.Borrego@Sun.COM if (handle) { 3898334SJose.Borrego@Sun.COM svc = handle->clnt->binding->service; 3908334SJose.Borrego@Sun.COM name = svc->name; 3918334SJose.Borrego@Sun.COM } 3925331Samw 3938334SJose.Borrego@Sun.COM smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 3948334SJose.Borrego@Sun.COM name, opnum, s, xlate_nt_status(status), status); 3958334SJose.Borrego@Sun.COM } 3968334SJose.Borrego@Sun.COM 3978334SJose.Borrego@Sun.COM /* 3988334SJose.Borrego@Sun.COM * The following functions provide the client callback interface. 3998334SJose.Borrego@Sun.COM * If the caller hasn't provided a heap, create one here. 4008334SJose.Borrego@Sun.COM */ 4018334SJose.Borrego@Sun.COM static int 4028334SJose.Borrego@Sun.COM ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 4038334SJose.Borrego@Sun.COM { 4048334SJose.Borrego@Sun.COM ndr_stream_t *recv_nds = &mxa->recv_nds; 4058334SJose.Borrego@Sun.COM ndr_stream_t *send_nds = &mxa->send_nds; 4068334SJose.Borrego@Sun.COM ndr_heap_t *heap = clnt->heap; 4078334SJose.Borrego@Sun.COM 4088334SJose.Borrego@Sun.COM if (heap == NULL) { 4098334SJose.Borrego@Sun.COM if ((heap = ndr_heap_create()) == NULL) 4105331Samw return (-1); 4118334SJose.Borrego@Sun.COM 4128334SJose.Borrego@Sun.COM clnt->heap = heap; 4135331Samw } 4145331Samw 4155331Samw mxa->heap = heap; 4165331Samw 4178334SJose.Borrego@Sun.COM nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 4189914Samw@Sun.COM nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 4199914Samw@Sun.COM NDR_MODE_RETURN_RECV, heap); 4205331Samw return (0); 4215331Samw } 4225331Samw 4235331Samw /* 4245331Samw * This is the entry pointy for an RPC client call exchange with 4255331Samw * a server, which will result in an smbrdr SmbTransact request. 4265331Samw * 4275331Samw * SmbTransact should return the number of bytes received, which 4285331Samw * we record as the PDU size, or a negative error code. 4295331Samw */ 4305331Samw static int 4318334SJose.Borrego@Sun.COM ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 4325331Samw { 4338334SJose.Borrego@Sun.COM ndr_stream_t *recv_nds = &mxa->recv_nds; 4348334SJose.Borrego@Sun.COM ndr_stream_t *send_nds = &mxa->send_nds; 4358334SJose.Borrego@Sun.COM int nbytes; 4365331Samw 4378334SJose.Borrego@Sun.COM nbytes = smbrdr_transact(clnt->fid, 4388334SJose.Borrego@Sun.COM (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 4398334SJose.Borrego@Sun.COM (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 4405331Samw 4418334SJose.Borrego@Sun.COM if (nbytes < 0) { 4428334SJose.Borrego@Sun.COM recv_nds->pdu_size = 0; 4438334SJose.Borrego@Sun.COM return (-1); 4448334SJose.Borrego@Sun.COM } 4455331Samw 4468334SJose.Borrego@Sun.COM recv_nds->pdu_size = nbytes; 4478334SJose.Borrego@Sun.COM return (nbytes); 4485331Samw } 4495331Samw 4505331Samw /* 4515331Samw * This entry point will be invoked if the xa-exchange response contained 4525331Samw * only the first fragment of a multi-fragment response. The RPC client 4535331Samw * code will then make repeated xa-read requests to obtain the remaining 4545331Samw * fragments, which will result in smbrdr SmbReadX requests. 4555331Samw * 4565331Samw * SmbReadX should return the number of bytes received, in which case we 4575331Samw * expand the PDU size to include the received data, or a negative error 4585331Samw * code. 4595331Samw */ 4605331Samw static int 4618334SJose.Borrego@Sun.COM ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 4625331Samw { 4638334SJose.Borrego@Sun.COM ndr_stream_t *nds = &mxa->recv_nds; 4645331Samw int len; 4658334SJose.Borrego@Sun.COM int nbytes; 4665331Samw 4678334SJose.Borrego@Sun.COM if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 4685331Samw return (-1); 4695331Samw 4708334SJose.Borrego@Sun.COM nbytes = smbrdr_readx(clnt->fid, 4718334SJose.Borrego@Sun.COM (char *)nds->pdu_base_offset + nds->pdu_size, len); 4725331Samw 4738334SJose.Borrego@Sun.COM if (nbytes < 0) 4745331Samw return (-1); 4755331Samw 4768334SJose.Borrego@Sun.COM nds->pdu_size += nbytes; 4775331Samw 4788334SJose.Borrego@Sun.COM if (nds->pdu_size > nds->pdu_max_size) { 4798334SJose.Borrego@Sun.COM nds->pdu_size = nds->pdu_max_size; 4805331Samw return (-1); 4815331Samw } 4825331Samw 4838334SJose.Borrego@Sun.COM return (nbytes); 4845331Samw } 4855331Samw 4865331Samw /* 4878334SJose.Borrego@Sun.COM * Preserve the heap so that the client application has access to data 4888334SJose.Borrego@Sun.COM * returned from the server after an RPC call. 4895331Samw */ 4908334SJose.Borrego@Sun.COM static void 4918334SJose.Borrego@Sun.COM ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 4925331Samw { 4938334SJose.Borrego@Sun.COM assert(clnt->heap == mxa->heap); 4945331Samw 4958334SJose.Borrego@Sun.COM clnt->heap_preserved = B_TRUE; 4965331Samw mxa->heap = NULL; 4975331Samw } 4985331Samw 4995331Samw /* 5008334SJose.Borrego@Sun.COM * Dispose of the transaction streams. If the heap has not been 5018334SJose.Borrego@Sun.COM * preserved, we can destroy it here. 5025331Samw */ 5038334SJose.Borrego@Sun.COM static void 5048334SJose.Borrego@Sun.COM ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 5055331Samw { 5068334SJose.Borrego@Sun.COM nds_destruct(&mxa->recv_nds); 5078334SJose.Borrego@Sun.COM nds_destruct(&mxa->send_nds); 5088334SJose.Borrego@Sun.COM 5098334SJose.Borrego@Sun.COM if (!clnt->heap_preserved) { 5108334SJose.Borrego@Sun.COM ndr_heap_destroy(mxa->heap); 5118334SJose.Borrego@Sun.COM mxa->heap = NULL; 5128334SJose.Borrego@Sun.COM clnt->heap = NULL; 5135331Samw } 5145331Samw } 5155331Samw 5165331Samw /* 5178334SJose.Borrego@Sun.COM * Dispose of a preserved heap. 5185331Samw */ 5195331Samw static void 5208334SJose.Borrego@Sun.COM ndr_xa_release(ndr_client_t *clnt) 5215331Samw { 5228334SJose.Borrego@Sun.COM if (clnt->heap_preserved) { 5238334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 5248334SJose.Borrego@Sun.COM clnt->heap = NULL; 5258334SJose.Borrego@Sun.COM clnt->heap_preserved = B_FALSE; 5265331Samw } 5275331Samw } 528*10717Samw@Sun.COM 529*10717Samw@Sun.COM /* 530*10717Samw@Sun.COM * Lookup platform, type and version information about a server. 531*10717Samw@Sun.COM * If the cache doesn't already contain the data, contact the server and 532*10717Samw@Sun.COM * cache the response before returning the server info to the caller. 533*10717Samw@Sun.COM */ 534*10717Samw@Sun.COM static int 535*10717Samw@Sun.COM ndr_svinfo_lookup(char *server, char *domain, srvsvc_server_info_t *svinfo) 536*10717Samw@Sun.COM { 537*10717Samw@Sun.COM ndr_svinfo_t *svi; 538*10717Samw@Sun.COM 539*10717Samw@Sun.COM (void) mutex_lock(&ndr_svlist.svl_mtx); 540*10717Samw@Sun.COM assert(ndr_svlist.svl_init == B_TRUE); 541*10717Samw@Sun.COM 542*10717Samw@Sun.COM svi = list_head(&ndr_svlist.svl_list); 543*10717Samw@Sun.COM while (svi != NULL) { 544*10717Samw@Sun.COM if (ndr_svinfo_expired(svi)) { 545*10717Samw@Sun.COM svi = list_head(&ndr_svlist.svl_list); 546*10717Samw@Sun.COM continue; 547*10717Samw@Sun.COM } 548*10717Samw@Sun.COM 549*10717Samw@Sun.COM if (ndr_svinfo_match(server, domain, svi)) { 550*10717Samw@Sun.COM bcopy(&svi->svi_svinfo, svinfo, 551*10717Samw@Sun.COM sizeof (srvsvc_server_info_t)); 552*10717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 553*10717Samw@Sun.COM return (0); 554*10717Samw@Sun.COM } 555*10717Samw@Sun.COM 556*10717Samw@Sun.COM svi = list_next(&ndr_svlist.svl_list, svi); 557*10717Samw@Sun.COM } 558*10717Samw@Sun.COM 559*10717Samw@Sun.COM if ((svi = malloc(sizeof (ndr_svinfo_t))) == NULL) { 560*10717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 561*10717Samw@Sun.COM return (-1); 562*10717Samw@Sun.COM } 563*10717Samw@Sun.COM 564*10717Samw@Sun.COM if (srvsvc_net_server_getinfo(server, domain, &svi->svi_svinfo) < 0) { 565*10717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 566*10717Samw@Sun.COM free(svi); 567*10717Samw@Sun.COM return (-1); 568*10717Samw@Sun.COM } 569*10717Samw@Sun.COM 570*10717Samw@Sun.COM (void) time(&svi->svi_tcached); 571*10717Samw@Sun.COM (void) strlcpy(svi->svi_server, server, MAXNAMELEN); 572*10717Samw@Sun.COM (void) strlcpy(svi->svi_domain, domain, MAXNAMELEN); 573*10717Samw@Sun.COM list_insert_tail(&ndr_svlist.svl_list, svi); 574*10717Samw@Sun.COM bcopy(&svi->svi_svinfo, svinfo, sizeof (srvsvc_server_info_t)); 575*10717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 576*10717Samw@Sun.COM return (0); 577*10717Samw@Sun.COM } 578*10717Samw@Sun.COM 579*10717Samw@Sun.COM static boolean_t 580*10717Samw@Sun.COM ndr_svinfo_match(const char *server, const char *domain, 581*10717Samw@Sun.COM const ndr_svinfo_t *svi) 582*10717Samw@Sun.COM { 583*10717Samw@Sun.COM if ((utf8_strcasecmp(server, svi->svi_server) == 0) && 584*10717Samw@Sun.COM (utf8_strcasecmp(domain, svi->svi_domain) == 0)) { 585*10717Samw@Sun.COM return (B_TRUE); 586*10717Samw@Sun.COM } 587*10717Samw@Sun.COM 588*10717Samw@Sun.COM return (B_FALSE); 589*10717Samw@Sun.COM } 590*10717Samw@Sun.COM 591*10717Samw@Sun.COM /* 592*10717Samw@Sun.COM * If the server info in the cache has expired, discard it and return true. 593*10717Samw@Sun.COM * Otherwise return false. 594*10717Samw@Sun.COM * 595*10717Samw@Sun.COM * This is a private function to support ndr_svinfo_lookup() that assumes 596*10717Samw@Sun.COM * the list mutex is held. 597*10717Samw@Sun.COM */ 598*10717Samw@Sun.COM static boolean_t 599*10717Samw@Sun.COM ndr_svinfo_expired(ndr_svinfo_t *svi) 600*10717Samw@Sun.COM { 601*10717Samw@Sun.COM time_t tnow; 602*10717Samw@Sun.COM 603*10717Samw@Sun.COM (void) time(&tnow); 604*10717Samw@Sun.COM 605*10717Samw@Sun.COM if (difftime(tnow, svi->svi_tcached) > NDR_SVINFO_TIMEOUT) { 606*10717Samw@Sun.COM list_remove(&ndr_svlist.svl_list, svi); 607*10717Samw@Sun.COM free(svi->svi_svinfo.sv_name); 608*10717Samw@Sun.COM free(svi->svi_svinfo.sv_comment); 609*10717Samw@Sun.COM free(svi); 610*10717Samw@Sun.COM return (B_TRUE); 611*10717Samw@Sun.COM } 612*10717Samw@Sun.COM 613*10717Samw@Sun.COM return (B_FALSE); 614*10717Samw@Sun.COM } 615