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 /* 22*11963SAfshin.Ardakani@Sun.COM * Copyright 2010 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> 32*11963SAfshin.Ardakani@Sun.COM #include <sys/tzfile.h> 3310717Samw@Sun.COM #include <time.h> 345331Samw #include <strings.h> 358334SJose.Borrego@Sun.COM #include <assert.h> 3610717Samw@Sun.COM #include <thread.h> 37*11963SAfshin.Ardakani@Sun.COM #include <unistd.h> 38*11963SAfshin.Ardakani@Sun.COM #include <syslog.h> 3910717Samw@Sun.COM #include <synch.h> 405331Samw #include <smbsrv/libsmb.h> 415331Samw #include <smbsrv/libsmbrdr.h> 428334SJose.Borrego@Sun.COM #include <smbsrv/libmlrpc.h> 438334SJose.Borrego@Sun.COM #include <smbsrv/libmlsvc.h> 4411337SWilliam.Krier@Sun.COM #include <smbsrv/ndl/srvsvc.ndl> 45*11963SAfshin.Ardakani@Sun.COM #include <mlsvc.h> 465331Samw 4710717Samw@Sun.COM /* 4810717Samw@Sun.COM * Server info cache entry expiration in seconds. 4910717Samw@Sun.COM */ 5010717Samw@Sun.COM #define NDR_SVINFO_TIMEOUT 1800 5110717Samw@Sun.COM 5210717Samw@Sun.COM typedef struct ndr_svinfo { 5310717Samw@Sun.COM list_node_t svi_lnd; 5410717Samw@Sun.COM time_t svi_tcached; 5510717Samw@Sun.COM char svi_server[MAXNAMELEN]; 5610717Samw@Sun.COM char svi_domain[MAXNAMELEN]; 5710717Samw@Sun.COM srvsvc_server_info_t svi_svinfo; 5810717Samw@Sun.COM } ndr_svinfo_t; 5910717Samw@Sun.COM 6010717Samw@Sun.COM typedef struct ndr_svlist { 6110717Samw@Sun.COM list_t svl_list; 6210717Samw@Sun.COM mutex_t svl_mtx; 6310717Samw@Sun.COM boolean_t svl_init; 6410717Samw@Sun.COM } ndr_svlist_t; 6510717Samw@Sun.COM 6610717Samw@Sun.COM static ndr_svlist_t ndr_svlist; 6710717Samw@Sun.COM 688334SJose.Borrego@Sun.COM static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 698334SJose.Borrego@Sun.COM static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 708334SJose.Borrego@Sun.COM static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 718334SJose.Borrego@Sun.COM static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 728334SJose.Borrego@Sun.COM static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 738334SJose.Borrego@Sun.COM static void ndr_xa_release(ndr_client_t *); 745331Samw 7510717Samw@Sun.COM static int ndr_svinfo_lookup(char *, char *, srvsvc_server_info_t *); 7610717Samw@Sun.COM static boolean_t ndr_svinfo_match(const char *, const char *, const 7710717Samw@Sun.COM ndr_svinfo_t *); 7810717Samw@Sun.COM static boolean_t ndr_svinfo_expired(ndr_svinfo_t *); 7910717Samw@Sun.COM 8010717Samw@Sun.COM /* 8110717Samw@Sun.COM * Initialize the RPC client interface: create the server info cache. 8210717Samw@Sun.COM */ 8310717Samw@Sun.COM void 8410717Samw@Sun.COM ndr_rpc_init(void) 8510717Samw@Sun.COM { 8610717Samw@Sun.COM (void) mutex_lock(&ndr_svlist.svl_mtx); 8710717Samw@Sun.COM 8810717Samw@Sun.COM if (!ndr_svlist.svl_init) { 8910717Samw@Sun.COM list_create(&ndr_svlist.svl_list, sizeof (ndr_svinfo_t), 9010717Samw@Sun.COM offsetof(ndr_svinfo_t, svi_lnd)); 9110717Samw@Sun.COM ndr_svlist.svl_init = B_TRUE; 9210717Samw@Sun.COM } 9310717Samw@Sun.COM 9410717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 9510717Samw@Sun.COM } 9610717Samw@Sun.COM 9710717Samw@Sun.COM /* 9810717Samw@Sun.COM * Terminate the RPC client interface: flush and destroy the server info 9910717Samw@Sun.COM * cache. 10010717Samw@Sun.COM */ 10110717Samw@Sun.COM void 10210717Samw@Sun.COM ndr_rpc_fini(void) 10310717Samw@Sun.COM { 10410717Samw@Sun.COM ndr_svinfo_t *svi; 10510717Samw@Sun.COM 10610717Samw@Sun.COM (void) mutex_lock(&ndr_svlist.svl_mtx); 10710717Samw@Sun.COM 10810717Samw@Sun.COM if (ndr_svlist.svl_init) { 10910717Samw@Sun.COM while ((svi = list_head(&ndr_svlist.svl_list)) != NULL) { 11010717Samw@Sun.COM list_remove(&ndr_svlist.svl_list, svi); 11110717Samw@Sun.COM free(svi->svi_svinfo.sv_name); 11210717Samw@Sun.COM free(svi->svi_svinfo.sv_comment); 11310717Samw@Sun.COM free(svi); 11410717Samw@Sun.COM } 11510717Samw@Sun.COM 11610717Samw@Sun.COM list_destroy(&ndr_svlist.svl_list); 11710717Samw@Sun.COM ndr_svlist.svl_init = B_FALSE; 11810717Samw@Sun.COM } 11910717Samw@Sun.COM 12010717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 12110717Samw@Sun.COM } 12210717Samw@Sun.COM 1235331Samw /* 1248334SJose.Borrego@Sun.COM * This call must be made to initialize an RPC client structure and bind 1258334SJose.Borrego@Sun.COM * to the remote service before any RPCs can be exchanged with that service. 1265331Samw * 1278334SJose.Borrego@Sun.COM * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle 1288334SJose.Borrego@Sun.COM * with the client context for an instance of the interface. The handle 1298334SJose.Borrego@Sun.COM * is zeroed to ensure that it doesn't look like a valid handle - 1308334SJose.Borrego@Sun.COM * handle content is provided by the remove service. 1315331Samw * 1328334SJose.Borrego@Sun.COM * The client points to this top-level handle so that we know when to 1338334SJose.Borrego@Sun.COM * unbind and teardown the connection. As each handle is initialized it 1348334SJose.Borrego@Sun.COM * will inherit a reference to the client context. 1355331Samw */ 1365331Samw int 1378334SJose.Borrego@Sun.COM ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 1388334SJose.Borrego@Sun.COM char *username, const char *service) 1395331Samw { 1408334SJose.Borrego@Sun.COM ndr_client_t *clnt; 1418334SJose.Borrego@Sun.COM ndr_service_t *svc; 14210717Samw@Sun.COM srvsvc_server_info_t svinfo; 1438334SJose.Borrego@Sun.COM int fid; 1448334SJose.Borrego@Sun.COM int rc; 1455331Samw 1468334SJose.Borrego@Sun.COM if (handle == NULL || server == NULL || 1478334SJose.Borrego@Sun.COM domain == NULL || username == NULL) 1485331Samw return (-1); 1495331Samw 1508334SJose.Borrego@Sun.COM if ((svc = ndr_svc_lookup_name(service)) == NULL) 1518334SJose.Borrego@Sun.COM return (-1); 1525331Samw 15310717Samw@Sun.COM /* 15410717Samw@Sun.COM * Set the default based on the assumption that most 15510717Samw@Sun.COM * servers will be Windows 2000 or later. 15610717Samw@Sun.COM * Don't lookup the svinfo if this is a SRVSVC request 15710717Samw@Sun.COM * because the SRVSVC is used to get the server info. 15811337SWilliam.Krier@Sun.COM * None of the SRVSVC calls depend on the server info. 15910717Samw@Sun.COM */ 16011337SWilliam.Krier@Sun.COM bzero(&svinfo, sizeof (srvsvc_server_info_t)); 16111337SWilliam.Krier@Sun.COM svinfo.sv_platform_id = SV_PLATFORM_ID_NT; 16211337SWilliam.Krier@Sun.COM svinfo.sv_version_major = 5; 16311337SWilliam.Krier@Sun.COM svinfo.sv_version_minor = 0; 16411337SWilliam.Krier@Sun.COM svinfo.sv_type = SV_TYPE_DEFAULT; 16511337SWilliam.Krier@Sun.COM svinfo.sv_os = NATIVE_OS_WIN2000; 16610717Samw@Sun.COM 16711337SWilliam.Krier@Sun.COM if (strcasecmp(service, "SRVSVC") != 0) 16811337SWilliam.Krier@Sun.COM (void) ndr_svinfo_lookup(server, domain, &svinfo); 16910717Samw@Sun.COM 1708334SJose.Borrego@Sun.COM if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) 1718334SJose.Borrego@Sun.COM return (-1); 1725331Samw 1738334SJose.Borrego@Sun.COM fid = smbrdr_open_pipe(server, domain, username, svc->endpoint); 1748334SJose.Borrego@Sun.COM if (fid < 0) { 1758334SJose.Borrego@Sun.COM free(clnt); 1765331Samw return (-1); 1775331Samw } 1785331Samw 1798334SJose.Borrego@Sun.COM bzero(clnt, sizeof (ndr_client_t)); 1808334SJose.Borrego@Sun.COM clnt->handle = &handle->handle; 1818334SJose.Borrego@Sun.COM clnt->fid = fid; 1828334SJose.Borrego@Sun.COM 1838334SJose.Borrego@Sun.COM ndr_svc_binding_pool_init(&clnt->binding_list, 1848334SJose.Borrego@Sun.COM clnt->binding_pool, NDR_N_BINDING_POOL); 1858334SJose.Borrego@Sun.COM 1868334SJose.Borrego@Sun.COM clnt->xa_init = ndr_xa_init; 1878334SJose.Borrego@Sun.COM clnt->xa_exchange = ndr_xa_exchange; 1888334SJose.Borrego@Sun.COM clnt->xa_read = ndr_xa_read; 1898334SJose.Borrego@Sun.COM clnt->xa_preserve = ndr_xa_preserve; 1908334SJose.Borrego@Sun.COM clnt->xa_destruct = ndr_xa_destruct; 1918334SJose.Borrego@Sun.COM clnt->xa_release = ndr_xa_release; 1925331Samw 1938334SJose.Borrego@Sun.COM bzero(&handle->handle, sizeof (ndr_hdid_t)); 1948334SJose.Borrego@Sun.COM handle->clnt = clnt; 19511337SWilliam.Krier@Sun.COM bcopy(&svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t)); 1965331Samw 1978334SJose.Borrego@Sun.COM if (ndr_rpc_get_heap(handle) == NULL) { 1988334SJose.Borrego@Sun.COM free(clnt); 1995331Samw return (-1); 2008334SJose.Borrego@Sun.COM } 2018334SJose.Borrego@Sun.COM 2028334SJose.Borrego@Sun.COM rc = ndr_clnt_bind(clnt, service, &clnt->binding); 2038334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) { 2048334SJose.Borrego@Sun.COM (void) smbrdr_close_pipe(fid); 2058334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 2068334SJose.Borrego@Sun.COM free(clnt); 2078334SJose.Borrego@Sun.COM handle->clnt = NULL; 2088334SJose.Borrego@Sun.COM return (-1); 2098334SJose.Borrego@Sun.COM } 2105331Samw 2115331Samw return (0); 2125331Samw } 2135331Samw 2145331Samw /* 2158334SJose.Borrego@Sun.COM * Unbind and close the pipe to an RPC service. 2168334SJose.Borrego@Sun.COM * 2178334SJose.Borrego@Sun.COM * If the heap has been preserved we need to go through an xa release. 2188334SJose.Borrego@Sun.COM * The heap is preserved during an RPC call because that's where data 2198334SJose.Borrego@Sun.COM * returned from the server is stored. 2205331Samw * 2218334SJose.Borrego@Sun.COM * Otherwise we destroy the heap directly. 2228334SJose.Borrego@Sun.COM */ 2238334SJose.Borrego@Sun.COM void 2248334SJose.Borrego@Sun.COM ndr_rpc_unbind(mlsvc_handle_t *handle) 2258334SJose.Borrego@Sun.COM { 2268334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 2278334SJose.Borrego@Sun.COM 2288334SJose.Borrego@Sun.COM if (clnt->heap_preserved) 2298334SJose.Borrego@Sun.COM ndr_clnt_free_heap(clnt); 2308334SJose.Borrego@Sun.COM else 2318334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 2328334SJose.Borrego@Sun.COM 2338334SJose.Borrego@Sun.COM (void) smbrdr_close_pipe(clnt->fid); 2348334SJose.Borrego@Sun.COM free(handle->clnt); 2358334SJose.Borrego@Sun.COM bzero(handle, sizeof (mlsvc_handle_t)); 2368334SJose.Borrego@Sun.COM } 2378334SJose.Borrego@Sun.COM 2388334SJose.Borrego@Sun.COM /* 2398334SJose.Borrego@Sun.COM * Call the RPC function identified by opnum. The remote service is 2408334SJose.Borrego@Sun.COM * identified by the handle, which should have been initialized by 2418334SJose.Borrego@Sun.COM * ndr_rpc_bind. 2428334SJose.Borrego@Sun.COM * 2438334SJose.Borrego@Sun.COM * If the RPC call is successful (returns 0), the caller must call 2448334SJose.Borrego@Sun.COM * ndr_rpc_release to release the heap. Otherwise, we release the 2458334SJose.Borrego@Sun.COM * heap here. 2465331Samw */ 2475331Samw int 2488334SJose.Borrego@Sun.COM ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) 2498334SJose.Borrego@Sun.COM { 2508334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 2518334SJose.Borrego@Sun.COM int rc; 2528334SJose.Borrego@Sun.COM 2538334SJose.Borrego@Sun.COM if (ndr_rpc_get_heap(handle) == NULL) 2548334SJose.Borrego@Sun.COM return (-1); 2558334SJose.Borrego@Sun.COM 2568334SJose.Borrego@Sun.COM rc = ndr_clnt_call(clnt->binding, opnum, params); 2578334SJose.Borrego@Sun.COM 25811337SWilliam.Krier@Sun.COM /* 25911337SWilliam.Krier@Sun.COM * Always clear the nonull flag to ensure 26011337SWilliam.Krier@Sun.COM * it is not applied to subsequent calls. 26111337SWilliam.Krier@Sun.COM */ 26211337SWilliam.Krier@Sun.COM clnt->nonull = B_FALSE; 26311337SWilliam.Krier@Sun.COM 2648334SJose.Borrego@Sun.COM if (NDR_DRC_IS_FAULT(rc)) { 2658334SJose.Borrego@Sun.COM ndr_rpc_release(handle); 2668334SJose.Borrego@Sun.COM return (-1); 2678334SJose.Borrego@Sun.COM } 2688334SJose.Borrego@Sun.COM 2698334SJose.Borrego@Sun.COM return (0); 2708334SJose.Borrego@Sun.COM } 2718334SJose.Borrego@Sun.COM 2728334SJose.Borrego@Sun.COM /* 27311337SWilliam.Krier@Sun.COM * Outgoing strings should not be null terminated. 27411337SWilliam.Krier@Sun.COM */ 27511337SWilliam.Krier@Sun.COM void 27611337SWilliam.Krier@Sun.COM ndr_rpc_set_nonull(mlsvc_handle_t *handle) 27711337SWilliam.Krier@Sun.COM { 27811337SWilliam.Krier@Sun.COM handle->clnt->nonull = B_TRUE; 27911337SWilliam.Krier@Sun.COM } 28011337SWilliam.Krier@Sun.COM 28111337SWilliam.Krier@Sun.COM /* 28211337SWilliam.Krier@Sun.COM * Return a reference to the server info. 28311337SWilliam.Krier@Sun.COM */ 28411337SWilliam.Krier@Sun.COM const srvsvc_server_info_t * 28511337SWilliam.Krier@Sun.COM ndr_rpc_server_info(mlsvc_handle_t *handle) 28611337SWilliam.Krier@Sun.COM { 28711337SWilliam.Krier@Sun.COM return (&handle->svinfo); 28811337SWilliam.Krier@Sun.COM } 28911337SWilliam.Krier@Sun.COM 29011337SWilliam.Krier@Sun.COM /* 29111337SWilliam.Krier@Sun.COM * Return the RPC server OS level. 2928334SJose.Borrego@Sun.COM */ 29310717Samw@Sun.COM uint32_t 2948334SJose.Borrego@Sun.COM ndr_rpc_server_os(mlsvc_handle_t *handle) 2955331Samw { 29611337SWilliam.Krier@Sun.COM return (handle->svinfo.sv_os); 2978334SJose.Borrego@Sun.COM } 2988334SJose.Borrego@Sun.COM 29910504SKeyur.Desai@Sun.COM /* 30010504SKeyur.Desai@Sun.COM * Get the session key from a bound RPC client handle. 30110504SKeyur.Desai@Sun.COM * 30210504SKeyur.Desai@Sun.COM * The key returned is the 16-byte "user session key" 30310504SKeyur.Desai@Sun.COM * established by the underlying authentication protocol 30410504SKeyur.Desai@Sun.COM * (either Kerberos or NTLM). This key is needed for 30510504SKeyur.Desai@Sun.COM * SAM RPC calls such as SamrSetInformationUser, etc. 30610504SKeyur.Desai@Sun.COM * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. 30710504SKeyur.Desai@Sun.COM * 30810504SKeyur.Desai@Sun.COM * Returns zero (success) or an errno. 30910504SKeyur.Desai@Sun.COM */ 31010504SKeyur.Desai@Sun.COM int 31110504SKeyur.Desai@Sun.COM ndr_rpc_get_ssnkey(mlsvc_handle_t *handle, 31210504SKeyur.Desai@Sun.COM unsigned char *ssn_key, size_t len) 31310504SKeyur.Desai@Sun.COM { 31410504SKeyur.Desai@Sun.COM ndr_client_t *clnt = handle->clnt; 31510504SKeyur.Desai@Sun.COM int rc; 31610504SKeyur.Desai@Sun.COM 31710504SKeyur.Desai@Sun.COM if (clnt == NULL) 31810504SKeyur.Desai@Sun.COM return (EINVAL); 31910504SKeyur.Desai@Sun.COM 32010504SKeyur.Desai@Sun.COM rc = smbrdr_get_ssnkey(clnt->fid, ssn_key, len); 32110504SKeyur.Desai@Sun.COM return (rc); 32210504SKeyur.Desai@Sun.COM } 32310504SKeyur.Desai@Sun.COM 3248334SJose.Borrego@Sun.COM void * 3258334SJose.Borrego@Sun.COM ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 3268334SJose.Borrego@Sun.COM { 3278334SJose.Borrego@Sun.COM ndr_heap_t *heap; 3288334SJose.Borrego@Sun.COM 3298334SJose.Borrego@Sun.COM if ((heap = ndr_rpc_get_heap(handle)) == NULL) 3308334SJose.Borrego@Sun.COM return (NULL); 3318334SJose.Borrego@Sun.COM 3328334SJose.Borrego@Sun.COM return (ndr_heap_malloc(heap, size)); 3338334SJose.Borrego@Sun.COM } 3348334SJose.Borrego@Sun.COM 3358334SJose.Borrego@Sun.COM ndr_heap_t * 3368334SJose.Borrego@Sun.COM ndr_rpc_get_heap(mlsvc_handle_t *handle) 3378334SJose.Borrego@Sun.COM { 3388334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 3398334SJose.Borrego@Sun.COM 3408334SJose.Borrego@Sun.COM if (clnt->heap == NULL) 3418334SJose.Borrego@Sun.COM clnt->heap = ndr_heap_create(); 3428334SJose.Borrego@Sun.COM 3438334SJose.Borrego@Sun.COM return (clnt->heap); 3445331Samw } 3455331Samw 3465331Samw /* 3478334SJose.Borrego@Sun.COM * Must be called by RPC clients to free the heap after a successful RPC 3488334SJose.Borrego@Sun.COM * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 3498334SJose.Borrego@Sun.COM * of any data returned by the RPC prior to calling this function because 3508334SJose.Borrego@Sun.COM * returned data is in the heap. 3515331Samw */ 3525331Samw void 3538334SJose.Borrego@Sun.COM ndr_rpc_release(mlsvc_handle_t *handle) 3548334SJose.Borrego@Sun.COM { 3558334SJose.Borrego@Sun.COM ndr_client_t *clnt = handle->clnt; 3568334SJose.Borrego@Sun.COM 3578334SJose.Borrego@Sun.COM if (clnt->heap_preserved) 3588334SJose.Borrego@Sun.COM ndr_clnt_free_heap(clnt); 3598334SJose.Borrego@Sun.COM else 3608334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 3618334SJose.Borrego@Sun.COM 3628334SJose.Borrego@Sun.COM clnt->heap = NULL; 3638334SJose.Borrego@Sun.COM } 3648334SJose.Borrego@Sun.COM 3658334SJose.Borrego@Sun.COM /* 3668334SJose.Borrego@Sun.COM * Returns true if the handle is null. 3678334SJose.Borrego@Sun.COM * Otherwise returns false. 3688334SJose.Borrego@Sun.COM */ 3698334SJose.Borrego@Sun.COM boolean_t 3708334SJose.Borrego@Sun.COM ndr_is_null_handle(mlsvc_handle_t *handle) 3715331Samw { 3728334SJose.Borrego@Sun.COM static ndr_hdid_t zero_handle; 3738334SJose.Borrego@Sun.COM 3748334SJose.Borrego@Sun.COM if (handle == NULL || handle->clnt == NULL) 3758334SJose.Borrego@Sun.COM return (B_TRUE); 3768334SJose.Borrego@Sun.COM 3778334SJose.Borrego@Sun.COM if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 3788334SJose.Borrego@Sun.COM return (B_TRUE); 3798334SJose.Borrego@Sun.COM 3808334SJose.Borrego@Sun.COM return (B_FALSE); 3818334SJose.Borrego@Sun.COM } 3828334SJose.Borrego@Sun.COM 3838334SJose.Borrego@Sun.COM /* 3848334SJose.Borrego@Sun.COM * Returns true if the handle is the top level bind handle. 3858334SJose.Borrego@Sun.COM * Otherwise returns false. 3868334SJose.Borrego@Sun.COM */ 3878334SJose.Borrego@Sun.COM boolean_t 3888334SJose.Borrego@Sun.COM ndr_is_bind_handle(mlsvc_handle_t *handle) 3898334SJose.Borrego@Sun.COM { 3908334SJose.Borrego@Sun.COM return (handle->clnt->handle == &handle->handle); 3915331Samw } 3925331Samw 3935331Samw /* 3948334SJose.Borrego@Sun.COM * Pass the client reference from parent to child. 3955331Samw */ 3968334SJose.Borrego@Sun.COM void 3978334SJose.Borrego@Sun.COM ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 3988334SJose.Borrego@Sun.COM { 3998334SJose.Borrego@Sun.COM child->clnt = parent->clnt; 40011337SWilliam.Krier@Sun.COM bcopy(&parent->svinfo, &child->svinfo, sizeof (srvsvc_server_info_t)); 4018334SJose.Borrego@Sun.COM } 4028334SJose.Borrego@Sun.COM 4038334SJose.Borrego@Sun.COM void 4048334SJose.Borrego@Sun.COM ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 4055331Samw { 4068334SJose.Borrego@Sun.COM ndr_service_t *svc; 4078334SJose.Borrego@Sun.COM char *name = "NDR RPC"; 4088334SJose.Borrego@Sun.COM char *s = "unknown"; 4098334SJose.Borrego@Sun.COM 4108334SJose.Borrego@Sun.COM if (status == 0) 4118334SJose.Borrego@Sun.COM s = "success"; 4128334SJose.Borrego@Sun.COM else if (NT_SC_IS_ERROR(status)) 4138334SJose.Borrego@Sun.COM s = "error"; 4148334SJose.Borrego@Sun.COM else if (NT_SC_IS_WARNING(status)) 4158334SJose.Borrego@Sun.COM s = "warning"; 4168334SJose.Borrego@Sun.COM else if (NT_SC_IS_INFO(status)) 4178334SJose.Borrego@Sun.COM s = "info"; 4188334SJose.Borrego@Sun.COM 4198334SJose.Borrego@Sun.COM if (handle) { 4208334SJose.Borrego@Sun.COM svc = handle->clnt->binding->service; 4218334SJose.Borrego@Sun.COM name = svc->name; 4228334SJose.Borrego@Sun.COM } 4235331Samw 4248334SJose.Borrego@Sun.COM smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 4258334SJose.Borrego@Sun.COM name, opnum, s, xlate_nt_status(status), status); 4268334SJose.Borrego@Sun.COM } 4278334SJose.Borrego@Sun.COM 4288334SJose.Borrego@Sun.COM /* 4298334SJose.Borrego@Sun.COM * The following functions provide the client callback interface. 4308334SJose.Borrego@Sun.COM * If the caller hasn't provided a heap, create one here. 4318334SJose.Borrego@Sun.COM */ 4328334SJose.Borrego@Sun.COM static int 4338334SJose.Borrego@Sun.COM ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 4348334SJose.Borrego@Sun.COM { 43511337SWilliam.Krier@Sun.COM ndr_stream_t *recv_nds = &mxa->recv_nds; 43611337SWilliam.Krier@Sun.COM ndr_stream_t *send_nds = &mxa->send_nds; 43711337SWilliam.Krier@Sun.COM ndr_heap_t *heap = clnt->heap; 43811337SWilliam.Krier@Sun.COM int rc; 4398334SJose.Borrego@Sun.COM 4408334SJose.Borrego@Sun.COM if (heap == NULL) { 4418334SJose.Borrego@Sun.COM if ((heap = ndr_heap_create()) == NULL) 4425331Samw return (-1); 4438334SJose.Borrego@Sun.COM 4448334SJose.Borrego@Sun.COM clnt->heap = heap; 4455331Samw } 4465331Samw 4475331Samw mxa->heap = heap; 4485331Samw 44911337SWilliam.Krier@Sun.COM rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 45011337SWilliam.Krier@Sun.COM if (rc == 0) 45111337SWilliam.Krier@Sun.COM rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 45211337SWilliam.Krier@Sun.COM NDR_MODE_RETURN_RECV, heap); 45311337SWilliam.Krier@Sun.COM 45411337SWilliam.Krier@Sun.COM if (rc != 0) { 45511337SWilliam.Krier@Sun.COM nds_destruct(&mxa->recv_nds); 45611337SWilliam.Krier@Sun.COM nds_destruct(&mxa->send_nds); 45711337SWilliam.Krier@Sun.COM ndr_heap_destroy(mxa->heap); 45811337SWilliam.Krier@Sun.COM mxa->heap = NULL; 45911337SWilliam.Krier@Sun.COM clnt->heap = NULL; 46011337SWilliam.Krier@Sun.COM return (-1); 46111337SWilliam.Krier@Sun.COM } 46211337SWilliam.Krier@Sun.COM 46311337SWilliam.Krier@Sun.COM if (clnt->nonull) 46411337SWilliam.Krier@Sun.COM NDS_SETF(send_nds, NDS_F_NONULL); 46511337SWilliam.Krier@Sun.COM 4665331Samw return (0); 4675331Samw } 4685331Samw 4695331Samw /* 4705331Samw * This is the entry pointy for an RPC client call exchange with 4715331Samw * a server, which will result in an smbrdr SmbTransact request. 4725331Samw * 4735331Samw * SmbTransact should return the number of bytes received, which 4745331Samw * we record as the PDU size, or a negative error code. 4755331Samw */ 4765331Samw static int 4778334SJose.Borrego@Sun.COM ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 4785331Samw { 4798334SJose.Borrego@Sun.COM ndr_stream_t *recv_nds = &mxa->recv_nds; 4808334SJose.Borrego@Sun.COM ndr_stream_t *send_nds = &mxa->send_nds; 4818334SJose.Borrego@Sun.COM int nbytes; 4825331Samw 4838334SJose.Borrego@Sun.COM nbytes = smbrdr_transact(clnt->fid, 4848334SJose.Borrego@Sun.COM (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 4858334SJose.Borrego@Sun.COM (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 4865331Samw 4878334SJose.Borrego@Sun.COM if (nbytes < 0) { 4888334SJose.Borrego@Sun.COM recv_nds->pdu_size = 0; 4898334SJose.Borrego@Sun.COM return (-1); 4908334SJose.Borrego@Sun.COM } 4915331Samw 4928334SJose.Borrego@Sun.COM recv_nds->pdu_size = nbytes; 4938334SJose.Borrego@Sun.COM return (nbytes); 4945331Samw } 4955331Samw 4965331Samw /* 4975331Samw * This entry point will be invoked if the xa-exchange response contained 4985331Samw * only the first fragment of a multi-fragment response. The RPC client 4995331Samw * code will then make repeated xa-read requests to obtain the remaining 5005331Samw * fragments, which will result in smbrdr SmbReadX requests. 5015331Samw * 5025331Samw * SmbReadX should return the number of bytes received, in which case we 5035331Samw * expand the PDU size to include the received data, or a negative error 5045331Samw * code. 5055331Samw */ 5065331Samw static int 5078334SJose.Borrego@Sun.COM ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 5085331Samw { 5098334SJose.Borrego@Sun.COM ndr_stream_t *nds = &mxa->recv_nds; 5105331Samw int len; 5118334SJose.Borrego@Sun.COM int nbytes; 5125331Samw 5138334SJose.Borrego@Sun.COM if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 5145331Samw return (-1); 5155331Samw 5168334SJose.Borrego@Sun.COM nbytes = smbrdr_readx(clnt->fid, 5178334SJose.Borrego@Sun.COM (char *)nds->pdu_base_offset + nds->pdu_size, len); 5185331Samw 5198334SJose.Borrego@Sun.COM if (nbytes < 0) 5205331Samw return (-1); 5215331Samw 5228334SJose.Borrego@Sun.COM nds->pdu_size += nbytes; 5235331Samw 5248334SJose.Borrego@Sun.COM if (nds->pdu_size > nds->pdu_max_size) { 5258334SJose.Borrego@Sun.COM nds->pdu_size = nds->pdu_max_size; 5265331Samw return (-1); 5275331Samw } 5285331Samw 5298334SJose.Borrego@Sun.COM return (nbytes); 5305331Samw } 5315331Samw 5325331Samw /* 5338334SJose.Borrego@Sun.COM * Preserve the heap so that the client application has access to data 5348334SJose.Borrego@Sun.COM * returned from the server after an RPC call. 5355331Samw */ 5368334SJose.Borrego@Sun.COM static void 5378334SJose.Borrego@Sun.COM ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 5385331Samw { 5398334SJose.Borrego@Sun.COM assert(clnt->heap == mxa->heap); 5405331Samw 5418334SJose.Borrego@Sun.COM clnt->heap_preserved = B_TRUE; 5425331Samw mxa->heap = NULL; 5435331Samw } 5445331Samw 5455331Samw /* 5468334SJose.Borrego@Sun.COM * Dispose of the transaction streams. If the heap has not been 5478334SJose.Borrego@Sun.COM * preserved, we can destroy it here. 5485331Samw */ 5498334SJose.Borrego@Sun.COM static void 5508334SJose.Borrego@Sun.COM ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 5515331Samw { 5528334SJose.Borrego@Sun.COM nds_destruct(&mxa->recv_nds); 5538334SJose.Borrego@Sun.COM nds_destruct(&mxa->send_nds); 5548334SJose.Borrego@Sun.COM 5558334SJose.Borrego@Sun.COM if (!clnt->heap_preserved) { 5568334SJose.Borrego@Sun.COM ndr_heap_destroy(mxa->heap); 5578334SJose.Borrego@Sun.COM mxa->heap = NULL; 5588334SJose.Borrego@Sun.COM clnt->heap = NULL; 5595331Samw } 5605331Samw } 5615331Samw 5625331Samw /* 5638334SJose.Borrego@Sun.COM * Dispose of a preserved heap. 5645331Samw */ 5655331Samw static void 5668334SJose.Borrego@Sun.COM ndr_xa_release(ndr_client_t *clnt) 5675331Samw { 5688334SJose.Borrego@Sun.COM if (clnt->heap_preserved) { 5698334SJose.Borrego@Sun.COM ndr_heap_destroy(clnt->heap); 5708334SJose.Borrego@Sun.COM clnt->heap = NULL; 5718334SJose.Borrego@Sun.COM clnt->heap_preserved = B_FALSE; 5725331Samw } 5735331Samw } 57410717Samw@Sun.COM 57510717Samw@Sun.COM /* 57610717Samw@Sun.COM * Lookup platform, type and version information about a server. 57710717Samw@Sun.COM * If the cache doesn't already contain the data, contact the server and 57810717Samw@Sun.COM * cache the response before returning the server info to the caller. 57911337SWilliam.Krier@Sun.COM * 58011337SWilliam.Krier@Sun.COM * We don't provide the name or comment for now, which avoids the need 58111337SWilliam.Krier@Sun.COM * to deal with unnecessary memory management. 58210717Samw@Sun.COM */ 58310717Samw@Sun.COM static int 58410717Samw@Sun.COM ndr_svinfo_lookup(char *server, char *domain, srvsvc_server_info_t *svinfo) 58510717Samw@Sun.COM { 586*11963SAfshin.Ardakani@Sun.COM static boolean_t timechecked = B_FALSE; 58710717Samw@Sun.COM ndr_svinfo_t *svi; 58810717Samw@Sun.COM 58910717Samw@Sun.COM (void) mutex_lock(&ndr_svlist.svl_mtx); 590*11963SAfshin.Ardakani@Sun.COM if (!ndr_svlist.svl_init) 591*11963SAfshin.Ardakani@Sun.COM return (-1); 59210717Samw@Sun.COM 59310717Samw@Sun.COM svi = list_head(&ndr_svlist.svl_list); 59410717Samw@Sun.COM while (svi != NULL) { 59510717Samw@Sun.COM if (ndr_svinfo_expired(svi)) { 59610717Samw@Sun.COM svi = list_head(&ndr_svlist.svl_list); 59710717Samw@Sun.COM continue; 59810717Samw@Sun.COM } 59910717Samw@Sun.COM 60010717Samw@Sun.COM if (ndr_svinfo_match(server, domain, svi)) { 60110717Samw@Sun.COM bcopy(&svi->svi_svinfo, svinfo, 60210717Samw@Sun.COM sizeof (srvsvc_server_info_t)); 60311337SWilliam.Krier@Sun.COM svinfo->sv_name = NULL; 60411337SWilliam.Krier@Sun.COM svinfo->sv_comment = NULL; 60510717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 60610717Samw@Sun.COM return (0); 60710717Samw@Sun.COM } 60810717Samw@Sun.COM 60910717Samw@Sun.COM svi = list_next(&ndr_svlist.svl_list, svi); 61010717Samw@Sun.COM } 61110717Samw@Sun.COM 61210717Samw@Sun.COM if ((svi = malloc(sizeof (ndr_svinfo_t))) == NULL) { 61310717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 61410717Samw@Sun.COM return (-1); 61510717Samw@Sun.COM } 61610717Samw@Sun.COM 61710717Samw@Sun.COM if (srvsvc_net_server_getinfo(server, domain, &svi->svi_svinfo) < 0) { 61810717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 61910717Samw@Sun.COM free(svi); 62010717Samw@Sun.COM return (-1); 62110717Samw@Sun.COM } 62210717Samw@Sun.COM 62310717Samw@Sun.COM (void) time(&svi->svi_tcached); 62410717Samw@Sun.COM (void) strlcpy(svi->svi_server, server, MAXNAMELEN); 62510717Samw@Sun.COM (void) strlcpy(svi->svi_domain, domain, MAXNAMELEN); 62610717Samw@Sun.COM list_insert_tail(&ndr_svlist.svl_list, svi); 62710717Samw@Sun.COM bcopy(&svi->svi_svinfo, svinfo, sizeof (srvsvc_server_info_t)); 62811337SWilliam.Krier@Sun.COM svinfo->sv_name = NULL; 62911337SWilliam.Krier@Sun.COM svinfo->sv_comment = NULL; 630*11963SAfshin.Ardakani@Sun.COM 631*11963SAfshin.Ardakani@Sun.COM if (!timechecked) { 632*11963SAfshin.Ardakani@Sun.COM timechecked = B_TRUE; 633*11963SAfshin.Ardakani@Sun.COM ndr_srvsvc_timecheck(server, domain); 634*11963SAfshin.Ardakani@Sun.COM } 635*11963SAfshin.Ardakani@Sun.COM 63610717Samw@Sun.COM (void) mutex_unlock(&ndr_svlist.svl_mtx); 63710717Samw@Sun.COM return (0); 63810717Samw@Sun.COM } 63910717Samw@Sun.COM 64010717Samw@Sun.COM static boolean_t 64110717Samw@Sun.COM ndr_svinfo_match(const char *server, const char *domain, 64210717Samw@Sun.COM const ndr_svinfo_t *svi) 64310717Samw@Sun.COM { 64410966SJordan.Brown@Sun.COM if ((smb_strcasecmp(server, svi->svi_server, 0) == 0) && 64510966SJordan.Brown@Sun.COM (smb_strcasecmp(domain, svi->svi_domain, 0) == 0)) { 64610717Samw@Sun.COM return (B_TRUE); 64710717Samw@Sun.COM } 64810717Samw@Sun.COM 64910717Samw@Sun.COM return (B_FALSE); 65010717Samw@Sun.COM } 65110717Samw@Sun.COM 65210717Samw@Sun.COM /* 65310717Samw@Sun.COM * If the server info in the cache has expired, discard it and return true. 65410717Samw@Sun.COM * Otherwise return false. 65510717Samw@Sun.COM * 65610717Samw@Sun.COM * This is a private function to support ndr_svinfo_lookup() that assumes 65710717Samw@Sun.COM * the list mutex is held. 65810717Samw@Sun.COM */ 65910717Samw@Sun.COM static boolean_t 66010717Samw@Sun.COM ndr_svinfo_expired(ndr_svinfo_t *svi) 66110717Samw@Sun.COM { 66210717Samw@Sun.COM time_t tnow; 66310717Samw@Sun.COM 66410717Samw@Sun.COM (void) time(&tnow); 66510717Samw@Sun.COM 66610717Samw@Sun.COM if (difftime(tnow, svi->svi_tcached) > NDR_SVINFO_TIMEOUT) { 66710717Samw@Sun.COM list_remove(&ndr_svlist.svl_list, svi); 66810717Samw@Sun.COM free(svi->svi_svinfo.sv_name); 66910717Samw@Sun.COM free(svi->svi_svinfo.sv_comment); 67010717Samw@Sun.COM free(svi); 67110717Samw@Sun.COM return (B_TRUE); 67210717Samw@Sun.COM } 67310717Samw@Sun.COM 67410717Samw@Sun.COM return (B_FALSE); 67510717Samw@Sun.COM } 676*11963SAfshin.Ardakani@Sun.COM 677*11963SAfshin.Ardakani@Sun.COM /* 678*11963SAfshin.Ardakani@Sun.COM * Compare the time here with the remote time on the server 679*11963SAfshin.Ardakani@Sun.COM * and report clock skew. 680*11963SAfshin.Ardakani@Sun.COM */ 681*11963SAfshin.Ardakani@Sun.COM void 682*11963SAfshin.Ardakani@Sun.COM ndr_srvsvc_timecheck(char *server, char *domain) 683*11963SAfshin.Ardakani@Sun.COM { 684*11963SAfshin.Ardakani@Sun.COM char hostname[MAXHOSTNAMELEN]; 685*11963SAfshin.Ardakani@Sun.COM struct timeval dc_tv; 686*11963SAfshin.Ardakani@Sun.COM struct tm dc_tm; 687*11963SAfshin.Ardakani@Sun.COM struct tm *tm; 688*11963SAfshin.Ardakani@Sun.COM time_t tnow; 689*11963SAfshin.Ardakani@Sun.COM time_t tdiff; 690*11963SAfshin.Ardakani@Sun.COM int priority; 691*11963SAfshin.Ardakani@Sun.COM 692*11963SAfshin.Ardakani@Sun.COM if (srvsvc_net_remote_tod(server, domain, &dc_tv, &dc_tm) < 0) { 693*11963SAfshin.Ardakani@Sun.COM syslog(LOG_DEBUG, "srvsvc_net_remote_tod failed"); 694*11963SAfshin.Ardakani@Sun.COM return; 695*11963SAfshin.Ardakani@Sun.COM } 696*11963SAfshin.Ardakani@Sun.COM 697*11963SAfshin.Ardakani@Sun.COM tnow = time(NULL); 698*11963SAfshin.Ardakani@Sun.COM 699*11963SAfshin.Ardakani@Sun.COM if (tnow > dc_tv.tv_sec) 700*11963SAfshin.Ardakani@Sun.COM tdiff = (tnow - dc_tv.tv_sec) / SECSPERMIN; 701*11963SAfshin.Ardakani@Sun.COM else 702*11963SAfshin.Ardakani@Sun.COM tdiff = (dc_tv.tv_sec - tnow) / SECSPERMIN; 703*11963SAfshin.Ardakani@Sun.COM 704*11963SAfshin.Ardakani@Sun.COM if (tdiff != 0) { 705*11963SAfshin.Ardakani@Sun.COM (void) strlcpy(hostname, "localhost", MAXHOSTNAMELEN); 706*11963SAfshin.Ardakani@Sun.COM (void) gethostname(hostname, MAXHOSTNAMELEN); 707*11963SAfshin.Ardakani@Sun.COM 708*11963SAfshin.Ardakani@Sun.COM priority = (tdiff > 2) ? LOG_NOTICE : LOG_DEBUG; 709*11963SAfshin.Ardakani@Sun.COM syslog(priority, "DC [%s] clock skew detected: %u minutes", 710*11963SAfshin.Ardakani@Sun.COM server, tdiff); 711*11963SAfshin.Ardakani@Sun.COM 712*11963SAfshin.Ardakani@Sun.COM tm = gmtime(&dc_tv.tv_sec); 713*11963SAfshin.Ardakani@Sun.COM syslog(priority, "%-8s UTC: %s", server, asctime(tm)); 714*11963SAfshin.Ardakani@Sun.COM tm = gmtime(&tnow); 715*11963SAfshin.Ardakani@Sun.COM syslog(priority, "%-8s UTC: %s", hostname, asctime(tm)); 716*11963SAfshin.Ardakani@Sun.COM } 717*11963SAfshin.Ardakani@Sun.COM } 718