1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Client NDR RPC interface. 28 */ 29 30 #include <sys/errno.h> 31 #include <strings.h> 32 #include <assert.h> 33 #include <smbsrv/libsmb.h> 34 #include <smbsrv/libsmbrdr.h> 35 #include <smbsrv/libmlrpc.h> 36 #include <smbsrv/libmlsvc.h> 37 38 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 39 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 40 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 41 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 42 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 43 static void ndr_xa_release(ndr_client_t *); 44 45 /* 46 * This call must be made to initialize an RPC client structure and bind 47 * to the remote service before any RPCs can be exchanged with that service. 48 * 49 * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle 50 * with the client context for an instance of the interface. The handle 51 * is zeroed to ensure that it doesn't look like a valid handle - 52 * handle content is provided by the remove service. 53 * 54 * The client points to this top-level handle so that we know when to 55 * unbind and teardown the connection. As each handle is initialized it 56 * will inherit a reference to the client context. 57 */ 58 int 59 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 60 char *username, const char *service) 61 { 62 ndr_client_t *clnt; 63 ndr_service_t *svc; 64 smbrdr_session_info_t si; 65 int fid; 66 int rc; 67 68 if (handle == NULL || server == NULL || 69 domain == NULL || username == NULL) 70 return (-1); 71 72 if ((svc = ndr_svc_lookup_name(service)) == NULL) 73 return (-1); 74 75 if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) 76 return (-1); 77 78 fid = smbrdr_open_pipe(server, domain, username, svc->endpoint); 79 if (fid < 0) { 80 free(clnt); 81 return (-1); 82 } 83 84 bzero(clnt, sizeof (ndr_client_t)); 85 clnt->handle = &handle->handle; 86 clnt->fid = fid; 87 88 ndr_svc_binding_pool_init(&clnt->binding_list, 89 clnt->binding_pool, NDR_N_BINDING_POOL); 90 91 clnt->xa_init = ndr_xa_init; 92 clnt->xa_exchange = ndr_xa_exchange; 93 clnt->xa_read = ndr_xa_read; 94 clnt->xa_preserve = ndr_xa_preserve; 95 clnt->xa_destruct = ndr_xa_destruct; 96 clnt->xa_release = ndr_xa_release; 97 98 (void) smbrdr_session_info(fid, &si); 99 bzero(&handle->handle, sizeof (ndr_hdid_t)); 100 handle->clnt = clnt; 101 handle->remote_os = si.si_server_os; 102 103 if (ndr_rpc_get_heap(handle) == NULL) { 104 free(clnt); 105 return (-1); 106 } 107 108 rc = ndr_clnt_bind(clnt, service, &clnt->binding); 109 if (NDR_DRC_IS_FAULT(rc)) { 110 (void) smbrdr_close_pipe(fid); 111 ndr_heap_destroy(clnt->heap); 112 free(clnt); 113 handle->clnt = NULL; 114 return (-1); 115 } 116 117 return (0); 118 } 119 120 /* 121 * Unbind and close the pipe to an RPC service. 122 * 123 * If the heap has been preserved we need to go through an xa release. 124 * The heap is preserved during an RPC call because that's where data 125 * returned from the server is stored. 126 * 127 * Otherwise we destroy the heap directly. 128 */ 129 void 130 ndr_rpc_unbind(mlsvc_handle_t *handle) 131 { 132 ndr_client_t *clnt = handle->clnt; 133 134 if (clnt->heap_preserved) 135 ndr_clnt_free_heap(clnt); 136 else 137 ndr_heap_destroy(clnt->heap); 138 139 (void) smbrdr_close_pipe(clnt->fid); 140 free(handle->clnt); 141 bzero(handle, sizeof (mlsvc_handle_t)); 142 } 143 144 /* 145 * Call the RPC function identified by opnum. The remote service is 146 * identified by the handle, which should have been initialized by 147 * ndr_rpc_bind. 148 * 149 * If the RPC call is successful (returns 0), the caller must call 150 * ndr_rpc_release to release the heap. Otherwise, we release the 151 * heap here. 152 */ 153 int 154 ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params) 155 { 156 ndr_client_t *clnt = handle->clnt; 157 int rc; 158 159 if (ndr_rpc_get_heap(handle) == NULL) 160 return (-1); 161 162 rc = ndr_clnt_call(clnt->binding, opnum, params); 163 164 if (NDR_DRC_IS_FAULT(rc)) { 165 ndr_rpc_release(handle); 166 return (-1); 167 } 168 169 return (0); 170 } 171 172 /* 173 * Set information about the remote RPC server in the handle. 174 */ 175 void 176 ndr_rpc_server_setinfo(mlsvc_handle_t *handle, 177 const srvsvc_server_info_t *svinfo) 178 { 179 bcopy(svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t)); 180 handle->svinfo.sv_name = NULL; 181 handle->svinfo.sv_comment = NULL; 182 183 if (svinfo->sv_version_major > 4) 184 handle->remote_os = NATIVE_OS_WIN2000; 185 else 186 handle->remote_os = NATIVE_OS_WINNT; 187 188 smb_tracef("NdrRpcServerSetInfo: %s (version %d.%d)", 189 svinfo->sv_name ? svinfo->sv_name : "<unknown>", 190 svinfo->sv_version_major, svinfo->sv_version_minor); 191 } 192 193 /* 194 * Get information about the remote RPC server from the handle. 195 */ 196 void 197 ndr_rpc_server_getinfo(mlsvc_handle_t *handle, srvsvc_server_info_t *svinfo) 198 { 199 bcopy(&handle->svinfo, svinfo, sizeof (srvsvc_server_info_t)); 200 } 201 202 /* 203 * Returns the Native-OS of the RPC server. 204 */ 205 int 206 ndr_rpc_server_os(mlsvc_handle_t *handle) 207 { 208 return (handle->remote_os); 209 } 210 211 void * 212 ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 213 { 214 ndr_heap_t *heap; 215 216 if ((heap = ndr_rpc_get_heap(handle)) == NULL) 217 return (NULL); 218 219 return (ndr_heap_malloc(heap, size)); 220 } 221 222 ndr_heap_t * 223 ndr_rpc_get_heap(mlsvc_handle_t *handle) 224 { 225 ndr_client_t *clnt = handle->clnt; 226 227 if (clnt->heap == NULL) 228 clnt->heap = ndr_heap_create(); 229 230 return (clnt->heap); 231 } 232 233 /* 234 * Must be called by RPC clients to free the heap after a successful RPC 235 * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 236 * of any data returned by the RPC prior to calling this function because 237 * returned data is in the heap. 238 */ 239 void 240 ndr_rpc_release(mlsvc_handle_t *handle) 241 { 242 ndr_client_t *clnt = handle->clnt; 243 244 if (clnt->heap_preserved) 245 ndr_clnt_free_heap(clnt); 246 else 247 ndr_heap_destroy(clnt->heap); 248 249 clnt->heap = NULL; 250 } 251 252 /* 253 * Returns true if the handle is null. 254 * Otherwise returns false. 255 */ 256 boolean_t 257 ndr_is_null_handle(mlsvc_handle_t *handle) 258 { 259 static ndr_hdid_t zero_handle; 260 261 if (handle == NULL || handle->clnt == NULL) 262 return (B_TRUE); 263 264 if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 265 return (B_TRUE); 266 267 return (B_FALSE); 268 } 269 270 /* 271 * Returns true if the handle is the top level bind handle. 272 * Otherwise returns false. 273 */ 274 boolean_t 275 ndr_is_bind_handle(mlsvc_handle_t *handle) 276 { 277 return (handle->clnt->handle == &handle->handle); 278 } 279 280 /* 281 * Pass the client reference from parent to child. 282 */ 283 void 284 ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 285 { 286 child->clnt = parent->clnt; 287 child->remote_os = parent->remote_os; 288 } 289 290 void 291 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 292 { 293 ndr_service_t *svc; 294 char *name = "NDR RPC"; 295 char *s = "unknown"; 296 297 if (status == 0) 298 s = "success"; 299 else if (NT_SC_IS_ERROR(status)) 300 s = "error"; 301 else if (NT_SC_IS_WARNING(status)) 302 s = "warning"; 303 else if (NT_SC_IS_INFO(status)) 304 s = "info"; 305 306 if (handle) { 307 svc = handle->clnt->binding->service; 308 name = svc->name; 309 } 310 311 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 312 name, opnum, s, xlate_nt_status(status), status); 313 } 314 315 /* 316 * The following functions provide the client callback interface. 317 * If the caller hasn't provided a heap, create one here. 318 */ 319 static int 320 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 321 { 322 ndr_stream_t *recv_nds = &mxa->recv_nds; 323 ndr_stream_t *send_nds = &mxa->send_nds; 324 ndr_heap_t *heap = clnt->heap; 325 326 if (heap == NULL) { 327 if ((heap = ndr_heap_create()) == NULL) 328 return (-1); 329 330 clnt->heap = heap; 331 } 332 333 mxa->heap = heap; 334 335 nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 336 nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 337 NDR_MODE_RETURN_RECV, heap); 338 return (0); 339 } 340 341 /* 342 * This is the entry pointy for an RPC client call exchange with 343 * a server, which will result in an smbrdr SmbTransact request. 344 * 345 * SmbTransact should return the number of bytes received, which 346 * we record as the PDU size, or a negative error code. 347 */ 348 static int 349 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 350 { 351 ndr_stream_t *recv_nds = &mxa->recv_nds; 352 ndr_stream_t *send_nds = &mxa->send_nds; 353 int nbytes; 354 355 nbytes = smbrdr_transact(clnt->fid, 356 (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 357 (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 358 359 if (nbytes < 0) { 360 recv_nds->pdu_size = 0; 361 return (-1); 362 } 363 364 recv_nds->pdu_size = nbytes; 365 return (nbytes); 366 } 367 368 /* 369 * This entry point will be invoked if the xa-exchange response contained 370 * only the first fragment of a multi-fragment response. The RPC client 371 * code will then make repeated xa-read requests to obtain the remaining 372 * fragments, which will result in smbrdr SmbReadX requests. 373 * 374 * SmbReadX should return the number of bytes received, in which case we 375 * expand the PDU size to include the received data, or a negative error 376 * code. 377 */ 378 static int 379 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 380 { 381 ndr_stream_t *nds = &mxa->recv_nds; 382 int len; 383 int nbytes; 384 385 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 386 return (-1); 387 388 nbytes = smbrdr_readx(clnt->fid, 389 (char *)nds->pdu_base_offset + nds->pdu_size, len); 390 391 if (nbytes < 0) 392 return (-1); 393 394 nds->pdu_size += nbytes; 395 396 if (nds->pdu_size > nds->pdu_max_size) { 397 nds->pdu_size = nds->pdu_max_size; 398 return (-1); 399 } 400 401 return (nbytes); 402 } 403 404 /* 405 * Preserve the heap so that the client application has access to data 406 * returned from the server after an RPC call. 407 */ 408 static void 409 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 410 { 411 assert(clnt->heap == mxa->heap); 412 413 clnt->heap_preserved = B_TRUE; 414 mxa->heap = NULL; 415 } 416 417 /* 418 * Dispose of the transaction streams. If the heap has not been 419 * preserved, we can destroy it here. 420 */ 421 static void 422 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 423 { 424 nds_destruct(&mxa->recv_nds); 425 nds_destruct(&mxa->send_nds); 426 427 if (!clnt->heap_preserved) { 428 ndr_heap_destroy(mxa->heap); 429 mxa->heap = NULL; 430 clnt->heap = NULL; 431 } 432 } 433 434 /* 435 * Dispose of a preserved heap. 436 */ 437 static void 438 ndr_xa_release(ndr_client_t *clnt) 439 { 440 if (clnt->heap_preserved) { 441 ndr_heap_destroy(clnt->heap); 442 clnt->heap = NULL; 443 clnt->heap_preserved = B_FALSE; 444 } 445 } 446