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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 27 */ 28 29 /* 30 * ML-RPC Client handle interface and support functions. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/fcntl.h> 35 #include <sys/poll.h> 36 37 #include <errno.h> 38 #include <strings.h> 39 #include <unistd.h> 40 41 #include <netsmb/smbfs_api.h> 42 #include <smb/ntstatus.h> 43 #include <libmlrpc.h> 44 45 #include <assert.h> 46 47 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *); 48 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *); 49 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *); 50 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *); 51 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *); 52 static void ndr_xa_release(ndr_client_t *); 53 54 /* See notes in mlrpc_clh_bind */ 55 int rpc_pipe_open_retries = 10; 56 57 /* 58 * Create an RPC client binding handle using the given smb_ctx. 59 * That context must already have a session and tree connected. 60 * 61 * Returns zero or an errno value. 62 */ 63 int 64 mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx) 65 { 66 ndr_client_t *clnt = NULL; 67 68 if (ctx == NULL) 69 return (EINVAL); 70 71 /* 72 * Allocate... 73 */ 74 if ((clnt = malloc(sizeof (*clnt))) == NULL) 75 return (ENOMEM); 76 bzero(clnt, sizeof (*clnt)); 77 78 clnt->xa_fd = -1; 79 80 /* 81 * Setup the transport functions. 82 * Always a named pipe (for now). 83 */ 84 clnt->xa_private = ctx; 85 clnt->xa_init = ndr_xa_init; 86 clnt->xa_exchange = ndr_xa_exchange; 87 clnt->xa_read = ndr_xa_read; 88 clnt->xa_preserve = ndr_xa_preserve; 89 clnt->xa_destruct = ndr_xa_destruct; 90 clnt->xa_release = ndr_xa_release; 91 92 /* See _is_bind_handle */ 93 clnt->handle = &handle->handle; 94 95 ndr_svc_binding_pool_init(&clnt->binding_list, 96 clnt->binding_pool, NDR_N_BINDING_POOL); 97 98 if ((clnt->heap = ndr_heap_create()) == NULL) 99 goto nomem; 100 101 /* success! */ 102 bzero(handle, sizeof (*handle)); 103 handle->clnt = clnt; 104 return (0); 105 106 nomem: 107 free(clnt); 108 return (ENOMEM); 109 } 110 111 112 /* 113 * This call must be made to initialize an RPC client structure and bind 114 * to the remote service before any RPCs can be exchanged with that service. 115 * 116 * The mlrpc_handle_t is a wrapper that is used to associate an RPC handle 117 * with the client context for an instance of the interface. The handle 118 * is zeroed to ensure that it doesn't look like a valid handle - 119 * handle content is provided by the remove service. 120 * 121 * The client points to this top-level handle so that we know when to 122 * unbind and teardown the connection. As each handle is initialized it 123 * will inherit a reference to the client context. 124 * 125 * 126 * Similar to MSRPC RpcBindingBind() 127 * 128 * Returns 0 or an NT_STATUS: (failed in...) 129 * 130 * RPC_NT_SERVER_TOO_BUSY (open pipe) 131 * RPC_NT_SERVER_UNAVAILABLE (open pipe) 132 * NT_STATUS_ACCESS_DENIED (open pipe) 133 * NT_STATUS_INVALID_PARAMETER (rpc bind) 134 * NT_STATUS_INTERNAL_ERROR (bad args etc) 135 * NT_STATUS_NO_MEMORY 136 */ 137 uint32_t 138 mlrpc_clh_bind(mlrpc_handle_t *handle, ndr_service_t *svc) 139 { 140 ndr_client_t *clnt = NULL; 141 struct smb_ctx *ctx = NULL; 142 uint32_t status = 0; 143 int fd = -1; 144 int rc, retries; 145 146 if ((clnt = handle->clnt) == NULL) 147 return (NT_STATUS_INTERNAL_ERROR); 148 if ((ctx = clnt->xa_private) == NULL) 149 return (NT_STATUS_INTERNAL_ERROR); 150 if (clnt->xa_fd != -1) 151 return (NT_STATUS_INTERNAL_ERROR); 152 153 /* 154 * Open the named pipe. 155 * 156 * Sometimes a DC may return NT_STATUS_PIPE_NOT_AVAILABLE for 157 * the first few seconds during service auto-start. The client 158 * translates that to EBUSY, so when we see that, wait a bit 159 * and retry the open for up to rpc_pipe_open_retries. If we 160 * fail even after retries, return RPC_NT_SERVER_TOO_BUSY, 161 * which is how callers of this layer expect that reported. 162 * We try up to 10 times, with a 0.5 sec. wait after each 163 * BUSY failure, giving a total wait here of 5 sec. 164 */ 165 retries = rpc_pipe_open_retries; 166 retry_open: 167 fd = smb_fh_open(ctx, svc->endpoint, O_RDWR); 168 if (fd < 0) { 169 rc = errno; 170 switch (rc) { 171 case EBUSY: 172 if (--retries > 0) { 173 (void) poll(NULL, 0, 500); 174 goto retry_open; 175 } 176 status = RPC_NT_SERVER_TOO_BUSY; 177 break; 178 case EACCES: 179 status = NT_STATUS_ACCESS_DENIED; 180 break; 181 default: 182 status = RPC_NT_SERVER_UNAVAILABLE; 183 break; 184 } 185 return (status); 186 } 187 188 clnt->xa_fd = fd; 189 190 /* Paranoia, in case of re-bind. */ 191 bzero(&handle->handle, sizeof (ndr_hdid_t)); 192 193 /* 194 * Do the OtW RPC bind. 195 */ 196 rc = ndr_clnt_bind(clnt, svc, &clnt->binding); 197 switch (rc) { 198 case NDR_DRC_FAULT_OUT_OF_MEMORY: 199 status = NT_STATUS_NO_MEMORY; 200 break; 201 case NDR_DRC_FAULT_API_SERVICE_INVALID: 202 /* svc->..._uuid parse errors */ 203 status = NT_STATUS_INTERNAL_ERROR; 204 break; 205 default: 206 if (NDR_DRC_IS_FAULT(rc)) { 207 status = RPC_NT_PROTOCOL_ERROR; 208 break; 209 } 210 /* FALLTHROUGH */ 211 case NDR_DRC_OK: 212 status = NT_STATUS_SUCCESS; 213 } 214 215 if (status != 0) { 216 if (fd != -1) 217 (void) smb_fh_close(fd); 218 clnt->xa_fd = -1; 219 } 220 221 return (status); 222 } 223 224 /* 225 * Unbind and close the pipe to an RPC service. 226 * 227 * Similar to MSRPC RpcBindingUnbind() 228 * This should be called after a dropped connection. 229 */ 230 void 231 mlrpc_clh_unbind(mlrpc_handle_t *handle) 232 { 233 ndr_client_t *clnt = handle->clnt; 234 235 if (clnt->xa_fd != -1) { 236 (void) smb_fh_close(clnt->xa_fd); 237 clnt->xa_fd = -1; 238 } 239 } 240 241 /* 242 * If the heap has been preserved we need to go through an xa release. 243 * The heap is preserved during an RPC call because that's where data 244 * returned from the server is stored. 245 * 246 * Otherwise we destroy the heap directly. 247 * 248 * Returns the xa_private pointer (if non-NULL) to inform the caller 249 * that it can now be destroyed. 250 */ 251 void * 252 mlrpc_clh_free(mlrpc_handle_t *handle) 253 { 254 ndr_client_t *clnt = handle->clnt; 255 void *private; 256 257 if (clnt == NULL) 258 return (NULL); 259 260 /* 261 * Should never get an unbind on inherited handles. 262 * Callers of ndr_inherit_handle() check handles 263 * with ndr_is_bind_handle() before calling this. 264 * 265 * Maybe make this function more tolerant? 266 */ 267 assert(handle->clnt->handle == &handle->handle); 268 269 mlrpc_clh_unbind(handle); 270 271 if (clnt->heap_preserved) 272 ndr_clnt_free_heap(clnt); /* xa_release */ 273 else 274 ndr_heap_destroy(clnt->heap); 275 276 /* 277 * Note: Caller will free the smb_ctx stored in 278 * clnt->xa_private (or possibly reuse it). 279 */ 280 private = clnt->xa_private; 281 free(clnt); 282 bzero(handle, sizeof (*handle)); 283 return (private); 284 } 285 286 /* 287 * Call the RPC function identified by opnum. The remote service is 288 * identified by the handle, which should have been initialized by 289 * ndr_rpc_bind. 290 * 291 * If the RPC call is successful (returns 0), the caller must call 292 * ndr_rpc_release to release the heap. Otherwise, we release the 293 * heap here. 294 */ 295 int 296 ndr_rpc_call(mlrpc_handle_t *handle, int opnum, void *params) 297 { 298 ndr_client_t *clnt = handle->clnt; 299 int rc; 300 301 if (ndr_rpc_get_heap(handle) == NULL) 302 return (-1); 303 304 rc = ndr_clnt_call(clnt->binding, opnum, params); 305 306 /* 307 * Always clear the nonull flag to ensure 308 * it is not applied to subsequent calls. 309 */ 310 clnt->nonull = B_FALSE; 311 312 if (NDR_DRC_IS_FAULT(rc)) { 313 ndr_rpc_release(handle); 314 return (-1); 315 } 316 317 return (0); 318 } 319 320 /* 321 * Outgoing strings should not be null terminated. 322 */ 323 void 324 ndr_rpc_set_nonull(mlrpc_handle_t *handle) 325 { 326 handle->clnt->nonull = B_TRUE; 327 } 328 329 /* 330 * Get the session key from a bound RPC client handle. 331 * 332 * The key returned is the 16-byte "user session key" 333 * established by the underlying authentication protocol 334 * (either Kerberos or NTLM). This key is needed for 335 * SAM RPC calls such as SamrSetInformationUser, etc. 336 * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25. 337 * 338 * Returns zero (success) or an errno. 339 */ 340 int 341 ndr_rpc_get_ssnkey(mlrpc_handle_t *handle, uchar_t *key, size_t len) 342 { 343 ndr_client_t *clnt = handle->clnt; 344 345 if (clnt == NULL || clnt->xa_fd == -1) 346 return (EINVAL); 347 348 return (smb_fh_getssnkey(clnt->xa_fd, key, len)); 349 } 350 351 void * 352 ndr_rpc_malloc(mlrpc_handle_t *handle, size_t size) 353 { 354 ndr_heap_t *heap; 355 356 if ((heap = ndr_rpc_get_heap(handle)) == NULL) 357 return (NULL); 358 359 return (ndr_heap_malloc(heap, size)); 360 } 361 362 ndr_heap_t * 363 ndr_rpc_get_heap(mlrpc_handle_t *handle) 364 { 365 ndr_client_t *clnt = handle->clnt; 366 367 if (clnt->heap == NULL) 368 clnt->heap = ndr_heap_create(); 369 370 return (clnt->heap); 371 } 372 373 /* 374 * Must be called by RPC clients to free the heap after a successful RPC 375 * call, i.e. ndr_rpc_call returned 0. The caller should take a copy 376 * of any data returned by the RPC prior to calling this function because 377 * returned data is in the heap. 378 */ 379 void 380 ndr_rpc_release(mlrpc_handle_t *handle) 381 { 382 ndr_client_t *clnt = handle->clnt; 383 384 if (clnt->heap_preserved) 385 ndr_clnt_free_heap(clnt); 386 else 387 ndr_heap_destroy(clnt->heap); 388 389 clnt->heap = NULL; 390 } 391 392 /* 393 * Returns true if the handle is null. 394 * Otherwise returns false. 395 */ 396 boolean_t 397 ndr_is_null_handle(mlrpc_handle_t *handle) 398 { 399 static const ndr_hdid_t hdid0 = {0}; 400 401 if (handle == NULL || handle->clnt == NULL) 402 return (B_TRUE); 403 404 if (!memcmp(&handle->handle, &hdid0, sizeof (hdid0))) 405 return (B_TRUE); 406 407 return (B_FALSE); 408 } 409 410 /* 411 * Returns true if the handle is the top level bind handle. 412 * Otherwise returns false. 413 */ 414 boolean_t 415 ndr_is_bind_handle(mlrpc_handle_t *handle) 416 { 417 return (handle->clnt->handle == &handle->handle); 418 } 419 420 /* 421 * Pass the client reference from parent to child. 422 */ 423 void 424 ndr_inherit_handle(mlrpc_handle_t *child, mlrpc_handle_t *parent) 425 { 426 child->clnt = parent->clnt; 427 } 428 429 /* 430 * ndr_rpc_status remains in libmlsvc mlsvc_client.c 431 */ 432 433 /* 434 * The following functions provide the client callback interface. 435 * If the caller hasn't provided a heap, create one here. 436 */ 437 static int 438 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa) 439 { 440 ndr_stream_t *recv_nds = &mxa->recv_nds; 441 ndr_stream_t *send_nds = &mxa->send_nds; 442 ndr_heap_t *heap = clnt->heap; 443 int rc; 444 445 if (heap == NULL) { 446 if ((heap = ndr_heap_create()) == NULL) 447 return (-1); 448 449 clnt->heap = heap; 450 } 451 452 mxa->heap = heap; 453 454 rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap); 455 if (rc == 0) 456 rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT, 457 NDR_MODE_RETURN_RECV, heap); 458 459 if (rc != 0) { 460 nds_destruct(&mxa->recv_nds); 461 nds_destruct(&mxa->send_nds); 462 ndr_heap_destroy(mxa->heap); 463 mxa->heap = NULL; 464 clnt->heap = NULL; 465 return (-1); 466 } 467 468 if (clnt->nonull) 469 NDS_SETF(send_nds, NDS_F_NONULL); 470 471 return (0); 472 } 473 474 /* 475 * This is the entry pointy for an RPC client call exchange with 476 * a server, which will result in an smbrdr SmbTransact request. 477 * 478 * SmbTransact should return the number of bytes received, which 479 * we record as the PDU size, or a negative error code. 480 */ 481 static int 482 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa) 483 { 484 ndr_stream_t *recv_nds = &mxa->recv_nds; 485 ndr_stream_t *send_nds = &mxa->send_nds; 486 int err, more, nbytes; 487 488 nbytes = recv_nds->pdu_max_size; 489 err = smb_fh_xactnp(clnt->xa_fd, 490 send_nds->pdu_size, (char *)send_nds->pdu_base_offset, 491 &nbytes, (char *)recv_nds->pdu_base_offset, &more); 492 if (err) { 493 recv_nds->pdu_size = 0; 494 return (-1); 495 } 496 497 recv_nds->pdu_size = nbytes; 498 return (0); 499 } 500 501 /* 502 * This entry point will be invoked if the xa-exchange response contained 503 * only the first fragment of a multi-fragment response. The RPC client 504 * code will then make repeated xa-read requests to obtain the remaining 505 * fragments, which will result in smbrdr SmbReadX requests. 506 * 507 * SmbReadX should return the number of bytes received, in which case we 508 * expand the PDU size to include the received data, or a negative error 509 * code. 510 */ 511 static int 512 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa) 513 { 514 ndr_stream_t *nds = &mxa->recv_nds; 515 int len; 516 int nbytes; 517 518 if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0) 519 return (-1); 520 521 nbytes = smb_fh_read(clnt->xa_fd, 0, len, 522 (char *)nds->pdu_base_offset + nds->pdu_size); 523 524 if (nbytes < 0) 525 return (-1); 526 527 nds->pdu_size += nbytes; 528 529 if (nds->pdu_size > nds->pdu_max_size) { 530 nds->pdu_size = nds->pdu_max_size; 531 return (-1); 532 } 533 534 return (nbytes); 535 } 536 537 /* 538 * Preserve the heap so that the client application has access to data 539 * returned from the server after an RPC call. 540 */ 541 static void 542 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa) 543 { 544 assert(clnt->heap == mxa->heap); 545 546 clnt->heap_preserved = B_TRUE; 547 mxa->heap = NULL; 548 } 549 550 /* 551 * Dispose of the transaction streams. If the heap has not been 552 * preserved, we can destroy it here. 553 */ 554 static void 555 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa) 556 { 557 nds_destruct(&mxa->recv_nds); 558 nds_destruct(&mxa->send_nds); 559 560 if (!clnt->heap_preserved) { 561 ndr_heap_destroy(mxa->heap); 562 mxa->heap = NULL; 563 clnt->heap = NULL; 564 } 565 } 566 567 /* 568 * Dispose of a preserved heap. 569 */ 570 static void 571 ndr_xa_release(ndr_client_t *clnt) 572 { 573 if (clnt->heap_preserved) { 574 ndr_heap_destroy(clnt->heap); 575 clnt->heap = NULL; 576 clnt->heap_preserved = B_FALSE; 577 } 578 }