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 * Returns the Native-OS of the RPC server. 174 */ 175 int 176 ndr_rpc_server_os(mlsvc_handle_t *handle) 177 { 178 return (handle->remote_os); 179 } 180 181 void * 182 ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size) 183 { 184 ndr_heap_t *heap; 185 186 if ((heap = ndr_rpc_get_heap(handle)) == NULL) 187 return (NULL); 188 189 return (ndr_heap_malloc(heap, size)); 190 } 191 192 ndr_heap_t * 193 ndr_rpc_get_heap(mlsvc_handle_t *handle) 194 { 195 ndr_client_t *clnt = handle->clnt; 196 197 if (clnt->heap == NULL) 198 clnt->heap = ndr_heap_create(); 199 200 return (clnt->heap); 201 } 202 203 /* 204 * Must be called by RPC clients to free the heap after a successful RPC 205 * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 206 * of any data returned by the RPC prior to calling this function because 207 * returned data is in the heap. 208 */ 209 void 210 ndr_rpc_release(mlsvc_handle_t *handle) 211 { 212 ndr_client_t *clnt = handle->clnt; 213 214 if (clnt->heap_preserved) 215 ndr_clnt_free_heap(clnt); 216 else 217 ndr_heap_destroy(clnt->heap); 218 219 clnt->heap = NULL; 220 } 221 222 /* 223 * Returns true if the handle is null. 224 * Otherwise returns false. 225 */ 226 boolean_t 227 ndr_is_null_handle(mlsvc_handle_t *handle) 228 { 229 static ndr_hdid_t zero_handle; 230 231 if (handle == NULL || handle->clnt == NULL) 232 return (B_TRUE); 233 234 if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t))) 235 return (B_TRUE); 236 237 return (B_FALSE); 238 } 239 240 /* 241 * Returns true if the handle is the top level bind handle. 242 * Otherwise returns false. 243 */ 244 boolean_t 245 ndr_is_bind_handle(mlsvc_handle_t *handle) 246 { 247 return (handle->clnt->handle == &handle->handle); 248 } 249 250 /* 251 * Pass the client reference from parent to child. 252 */ 253 void 254 ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent) 255 { 256 child->clnt = parent->clnt; 257 child->remote_os = parent->remote_os; 258 } 259 260 void 261 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 262 { 263 ndr_service_t *svc; 264 char *name = "NDR RPC"; 265 char *s = "unknown"; 266 267 if (status == 0) 268 s = "success"; 269 else if (NT_SC_IS_ERROR(status)) 270 s = "error"; 271 else if (NT_SC_IS_WARNING(status)) 272 s = "warning"; 273 else if (NT_SC_IS_INFO(status)) 274 s = "info"; 275 276 if (handle) { 277 svc = handle->clnt->binding->service; 278 name = svc->name; 279 } 280 281 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 282 name, opnum, s, xlate_nt_status(status), status); 283 } 284 285 /* 286 * The following functions provide the client callback interface. 287 * If the caller hasn't provided a heap, create one here. 288 */ 289 static int 290 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 291 { 292 ndr_stream_t *recv_nds = &mxa->recv_nds; 293 ndr_stream_t *send_nds = &mxa->send_nds; 294 ndr_heap_t *heap = clnt->heap; 295 296 if (heap == NULL) { 297 if ((heap = ndr_heap_create()) == NULL) 298 return (-1); 299 300 clnt->heap = heap; 301 } 302 303 mxa->heap = heap; 304 305 nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 306 nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 307 NDR_MODE_RETURN_RECV, heap); 308 return (0); 309 } 310 311 /* 312 * This is the entry pointy for an RPC client call exchange with 313 * a server, which will result in an smbrdr SmbTransact request. 314 * 315 * SmbTransact should return the number of bytes received, which 316 * we record as the PDU size, or a negative error code. 317 */ 318 static int 319 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 320 { 321 ndr_stream_t *recv_nds = &mxa->recv_nds; 322 ndr_stream_t *send_nds = &mxa->send_nds; 323 int nbytes; 324 325 nbytes = smbrdr_transact(clnt->fid, 326 (char *)send_nds->pdu_base_offset, send_nds->pdu_size, 327 (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size); 328 329 if (nbytes < 0) { 330 recv_nds->pdu_size = 0; 331 return (-1); 332 } 333 334 recv_nds->pdu_size = nbytes; 335 return (nbytes); 336 } 337 338 /* 339 * This entry point will be invoked if the xa-exchange response contained 340 * only the first fragment of a multi-fragment response. The RPC client 341 * code will then make repeated xa-read requests to obtain the remaining 342 * fragments, which will result in smbrdr SmbReadX requests. 343 * 344 * SmbReadX should return the number of bytes received, in which case we 345 * expand the PDU size to include the received data, or a negative error 346 * code. 347 */ 348 static int 349 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 350 { 351 ndr_stream_t *nds = &mxa->recv_nds; 352 int len; 353 int nbytes; 354 355 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 356 return (-1); 357 358 nbytes = smbrdr_readx(clnt->fid, 359 (char *)nds->pdu_base_offset + nds->pdu_size, len); 360 361 if (nbytes < 0) 362 return (-1); 363 364 nds->pdu_size += nbytes; 365 366 if (nds->pdu_size > nds->pdu_max_size) { 367 nds->pdu_size = nds->pdu_max_size; 368 return (-1); 369 } 370 371 return (nbytes); 372 } 373 374 /* 375 * Preserve the heap so that the client application has access to data 376 * returned from the server after an RPC call. 377 */ 378 static void 379 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 380 { 381 assert(clnt->heap == mxa->heap); 382 383 clnt->heap_preserved = B_TRUE; 384 mxa->heap = NULL; 385 } 386 387 /* 388 * Dispose of the transaction streams. If the heap has not been 389 * preserved, we can destroy it here. 390 */ 391 static void 392 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 393 { 394 nds_destruct(&mxa->recv_nds); 395 nds_destruct(&mxa->send_nds); 396 397 if (!clnt->heap_preserved) { 398 ndr_heap_destroy(mxa->heap); 399 mxa->heap = NULL; 400 clnt->heap = NULL; 401 } 402 } 403 404 /* 405 * Dispose of a preserved heap. 406 */ 407 static void 408 ndr_xa_release(ndr_client_t *clnt) 409 { 410 if (clnt->heap_preserved) { 411 ndr_heap_destroy(clnt->heap); 412 clnt->heap = NULL; 413 clnt->heap_preserved = B_FALSE; 414 } 415 } 416