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