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 }