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