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 
  52 /*
  53  * This call must be made to initialize an RPC client structure and bind
  54  * to the remote service before any RPCs can be exchanged with that service.
  55  *
  56  * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle
  57  * with the client context for an instance of the interface.  The handle
  58  * is zeroed to ensure that it doesn't look like a valid handle -
  59  * handle content is provided by the remove service.
  60  *
  61  * The client points to this top-level handle so that we know when to
  62  * unbind and teardown the connection.  As each handle is initialized it
  63  * will inherit a reference to the client context.
  64  *
  65  * Returns 0 or an NT_STATUS:
  66  *      NT_STATUS_BAD_NETWORK_PATH      (get server addr)
  67  *      NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth)
  68  *      NT_STATUS_BAD_NETWORK_NAME      (tcon, open)
  69  *      NT_STATUS_ACCESS_DENIED         (open pipe)
  70  *      NT_STATUS_INVALID_PARAMETER     (rpc bind)
  71  *
  72  *      NT_STATUS_INTERNAL_ERROR        (bad args etc)
  73  *      NT_STATUS_NO_MEMORY
  74  */
  75 DWORD
  76 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
  77     char *username, const char *service)
  78 {
  79         struct smb_ctx          *ctx = NULL;
  80         ndr_client_t            *clnt = NULL;
  81         ndr_service_t           *svc;
  82         DWORD                   status;
  83         int                     fd = -1;
  84         int                     rc;
  85 
  86         if (handle == NULL || server == NULL || server[0] == '\0' ||
  87             domain == NULL || username == NULL)
  88                 return (NT_STATUS_INTERNAL_ERROR);
  89 
  90         /* In case the service was not registered... */
  91         if ((svc = ndr_svc_lookup_name(service)) == NULL)
  92                 return (NT_STATUS_INTERNAL_ERROR);
  93 
  94         /*
  95          * Some callers pass this when they want a NULL session.
  96          * Todo: have callers pass an empty string for that.
  97          */
  98         if (strcmp(username, MLSVC_ANON_USER) == 0)
  99                 username = "";
 100 
 101         /*
 102          * Setup smbfs library handle, authenticate, connect to
 103          * the IPC$ share.  This will reuse an existing connection
 104          * if the driver already has one for this combination of
 105          * server, user, domain.  It may return any of:
 106          *      NT_STATUS_BAD_NETWORK_PATH      (get server addr)
 107          *      NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth)
 108          *      NT_STATUS_BAD_NETWORK_NAME      (tcon)
 109          */
 110         status = smbrdr_ctx_new(&ctx, server, domain, username);
 111         if (status != NT_STATUS_SUCCESS) {
 112                 syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new"
 113                     "(Srv=%s Dom=%s User=%s), %s (0x%x)",
 114                     server, domain, username,
 115                     xlate_nt_status(status), status);
 116                 /* Tell the DC Locator this DC failed. */
 117                 smb_ddiscover_bad_dc(server);
 118                 goto errout;
 119         }
 120 
 121         /*
 122          * Open the named pipe.
 123          */
 124         fd = smb_fh_open(ctx, svc->endpoint, O_RDWR);
 125         if (fd < 0) {
 126                 rc = errno;
 127                 syslog(LOG_DEBUG, "ndr_rpc_bind: "
 128                     "smb_fh_open (%s) err=%d",
 129                     svc->endpoint, rc);
 130                 switch (rc) {
 131                 case EACCES:
 132                         status = NT_STATUS_ACCESS_DENIED;
 133                         break;
 134                 default:
 135                         status = NT_STATUS_BAD_NETWORK_NAME;
 136                         break;
 137                 }
 138                 goto errout;
 139         }
 140 
 141         /*
 142          * Setup the RPC client handle.
 143          */
 144         if ((clnt = malloc(sizeof (ndr_client_t))) == NULL) {
 145                 status = NT_STATUS_NO_MEMORY;
 146                 goto errout;
 147         }
 148         bzero(clnt, sizeof (ndr_client_t));
 149 
 150         clnt->handle = &handle->handle;
 151         clnt->xa_init = ndr_xa_init;
 152         clnt->xa_exchange = ndr_xa_exchange;
 153         clnt->xa_read = ndr_xa_read;
 154         clnt->xa_preserve = ndr_xa_preserve;
 155         clnt->xa_destruct = ndr_xa_destruct;
 156         clnt->xa_release = ndr_xa_release;
 157         clnt->xa_private = ctx;
 158         clnt->xa_fd = fd;
 159 
 160         ndr_svc_binding_pool_init(&clnt->binding_list,
 161             clnt->binding_pool, NDR_N_BINDING_POOL);
 162 
 163         if ((clnt->heap = ndr_heap_create()) == NULL) {
 164                 status = NT_STATUS_NO_MEMORY;
 165                 goto errout;
 166         }
 167 
 168         /*
 169          * Fill in the caller's handle.
 170          */
 171         bzero(&handle->handle, sizeof (ndr_hdid_t));
 172         handle->clnt = clnt;
 173 
 174         /*
 175          * Do the OtW RPC bind.
 176          */
 177         rc = ndr_clnt_bind(clnt, service, &clnt->binding);
 178         switch (rc) {
 179         case NDR_DRC_FAULT_OUT_OF_MEMORY:
 180                 status = NT_STATUS_NO_MEMORY;
 181                 break;
 182         case NDR_DRC_FAULT_API_SERVICE_INVALID: /* not registered */
 183                 status = NT_STATUS_INTERNAL_ERROR;
 184                 break;
 185         default:
 186                 if (NDR_DRC_IS_FAULT(rc)) {
 187                         status = NT_STATUS_INVALID_PARAMETER;
 188                         break;
 189                 }
 190                 /* FALLTHROUGH */
 191         case NDR_DRC_OK:
 192                 return (NT_STATUS_SUCCESS);
 193         }
 194 
 195         syslog(LOG_DEBUG, "ndr_rpc_bind: "
 196             "ndr_clnt_bind, %s (0x%x)",
 197             xlate_nt_status(status), status);
 198 
 199 errout:
 200         handle->clnt = NULL;
 201         if (clnt != NULL) {
 202                 ndr_heap_destroy(clnt->heap);
 203                 free(clnt);
 204         }
 205         if (ctx != NULL) {
 206                 if (fd != -1)
 207                         (void) smb_fh_close(fd);
 208                 smbrdr_ctx_free(ctx);
 209         }
 210 
 211         return (status);
 212 }
 213 
 214 /*
 215  * Unbind and close the pipe to an RPC service.
 216  *
 217  * If the heap has been preserved we need to go through an xa release.
 218  * The heap is preserved during an RPC call because that's where data
 219  * returned from the server is stored.
 220  *
 221  * Otherwise we destroy the heap directly.
 222  */
 223 void
 224 ndr_rpc_unbind(mlsvc_handle_t *handle)
 225 {
 226         ndr_client_t *clnt = handle->clnt;
 227         struct smb_ctx *ctx = clnt->xa_private;
 228 
 229         if (clnt->heap_preserved)
 230                 ndr_clnt_free_heap(clnt);
 231         else
 232                 ndr_heap_destroy(clnt->heap);
 233 
 234         (void) smb_fh_close(clnt->xa_fd);
 235         smbrdr_ctx_free(ctx);
 236         free(clnt);
 237         bzero(handle, sizeof (mlsvc_handle_t));
 238 }
 239 
 240 void
 241 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
 242 {
 243         ndr_service_t *svc;
 244         char *name = "NDR RPC";
 245         char *s = "unknown";
 246 
 247         switch (NT_SC_SEVERITY(status)) {
 248         case NT_STATUS_SEVERITY_SUCCESS:
 249                 s = "success";
 250                 break;
 251         case NT_STATUS_SEVERITY_INFORMATIONAL:
 252                 s = "info";
 253                 break;
 254         case NT_STATUS_SEVERITY_WARNING:
 255                 s = "warning";
 256                 break;
 257         case NT_STATUS_SEVERITY_ERROR:
 258                 s = "error";
 259                 break;
 260         }
 261 
 262         if (handle) {
 263                 svc = handle->clnt->binding->service;
 264                 name = svc->name;
 265         }
 266 
 267         smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
 268             name, opnum, s, xlate_nt_status(status), status);
 269 }