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 <libmlrpc/libmlrpc.h>
  43 #include <netsmb/smbfs_api.h>
  44 
  45 #include <smbsrv/libsmb.h>
  46 #include <smbsrv/libmlsvc.h>
  47 #include <libsmbrdr.h>
  48 #include <mlsvc.h>
  49 
  50 
  51 /*
  52  * This call must be made to initialize an RPC client structure and bind
  53  * to the remote service before any RPCs can be exchanged with that service.
  54  *
  55  * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle
  56  * with the client context for an instance of the interface.  The handle
  57  * is zeroed to ensure that it doesn't look like a valid handle -
  58  * handle content is provided by the remove service.
  59  *
  60  * The client points to this top-level handle so that we know when to
  61  * unbind and teardown the connection.  As each handle is initialized it
  62  * will inherit a reference to the client context.
  63  *
  64  * Returns 0 or an NT_STATUS:           (failed in...)
  65  *
  66  *      NT_STATUS_BAD_NETWORK_PATH      (get server addr)
  67  *      NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth)
  68  *      NT_STATUS_BAD_NETWORK_NAME      (tcon)
  69  *      RPC_NT_SERVER_TOO_BUSY          (open pipe)
  70  *      RPC_NT_SERVER_UNAVAILABLE       (open pipe)
  71  *      NT_STATUS_ACCESS_DENIED         (open pipe)
  72  *      NT_STATUS_INVALID_PARAMETER     (rpc bind)
  73  *      NT_STATUS_INTERNAL_ERROR        (bad args etc)
  74  *      NT_STATUS_NO_MEMORY
  75  */
  76 DWORD
  77 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
  78     char *username, const char *service)
  79 {
  80         struct smb_ctx          *ctx = NULL;
  81         ndr_service_t           *svc;
  82         DWORD                   status;
  83         int                     rc;
  84 
  85         if (handle == NULL || server == NULL || server[0] == '\0' ||
  86             domain == NULL || username == NULL)
  87                 return (NT_STATUS_INTERNAL_ERROR);
  88 
  89         /* In case the service was not registered... */
  90         if ((svc = ndr_svc_lookup_name(service)) == NULL)
  91                 return (NT_STATUS_INTERNAL_ERROR);
  92 
  93         /*
  94          * Some callers pass this when they want a NULL session.
  95          * Todo: have callers pass an empty string for that.
  96          */
  97         if (strcmp(username, MLSVC_ANON_USER) == 0)
  98                 username = "";
  99 
 100         /*
 101          * Setup smbfs library handle, authenticate, connect to
 102          * the IPC$ share.  This will reuse an existing connection
 103          * if the driver already has one for this combination of
 104          * server, user, domain.  It may return any of:
 105          *      NT_STATUS_BAD_NETWORK_PATH      (get server addr)
 106          *      NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth)
 107          *      NT_STATUS_BAD_NETWORK_NAME      (tcon)
 108          */
 109         status = smbrdr_ctx_new(&ctx, server, domain, username);
 110         if (status != NT_STATUS_SUCCESS) {
 111                 syslog(LOG_ERR, "ndr_rpc_bind: smbrdr_ctx_new"
 112                     "(Srv=%s Dom=%s User=%s), %s (0x%x)",
 113                     server, domain, username,
 114                     xlate_nt_status(status), status);
 115                 /*
 116                  * If the error is one where changing to a new DC
 117                  * might help, try looking for a different DC.
 118                  */
 119                 switch (status) {
 120                 case NT_STATUS_BAD_NETWORK_PATH:
 121                 case NT_STATUS_BAD_NETWORK_NAME:
 122                         /* Look for a new DC */
 123                         smb_ddiscover_bad_dc(server);
 124                 default:
 125                         break;
 126                 }
 127                 return (status);
 128         }
 129 
 130         /*
 131          * Setup the RPC client handle.
 132          */
 133         rc = mlrpc_clh_create(handle, ctx);
 134         if (rc != 0) {
 135                 syslog(LOG_ERR, "ndr_rpc_bind: mlrpc_clh_create: rc=%d", rc);
 136                 smbrdr_ctx_free(ctx);
 137                 switch (rc) {
 138                 case ENOMEM:
 139                         return (NT_STATUS_NO_MEMORY);
 140                 case EINVAL:
 141                         return (NT_STATUS_INVALID_PARAMETER);
 142                 default:
 143                         return (NT_STATUS_INTERNAL_ERROR);
 144                 }
 145         }
 146 
 147         /*
 148          * This does the pipe open and OtW RPC bind.
 149          * Handles pipe open retries.
 150          */
 151         status = mlrpc_clh_bind(handle, svc);
 152         if (status != 0) {
 153                 syslog(LOG_DEBUG, "ndr_rpc_bind: "
 154                     "mlrpc_clh_bind, %s (0x%x)",
 155                     xlate_nt_status(status), status);
 156                 switch (status) {
 157                 case RPC_NT_SERVER_TOO_BUSY:
 158                         /* Look for a new DC */
 159                         smb_ddiscover_bad_dc(server);
 160                         break;
 161                 default:
 162                         break;
 163                 }
 164                 ctx = mlrpc_clh_free(handle);
 165                 if (ctx != NULL) {
 166                         smbrdr_ctx_free(ctx);
 167                 }
 168         }
 169 
 170         return (status);
 171 }
 172 
 173 /*
 174  * Unbind and close the pipe to an RPC service
 175  * and cleanup the smb_ctx.
 176  *
 177  * The heap may or may not be destroyed (see mlrpc_clh_free)
 178  */
 179 void
 180 ndr_rpc_unbind(mlsvc_handle_t *handle)
 181 {
 182         struct smb_ctx *ctx;
 183 
 184         ctx = mlrpc_clh_free(handle);
 185         if (ctx != NULL)
 186                 smbrdr_ctx_free(ctx);
 187 
 188         bzero(handle, sizeof (mlsvc_handle_t));
 189 }
 190 
 191 void
 192 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
 193 {
 194         ndr_service_t *svc;
 195         char *name = "NDR RPC";
 196         char *s = "unknown";
 197 
 198         switch (NT_SC_SEVERITY(status)) {
 199         case NT_STATUS_SEVERITY_SUCCESS:
 200                 s = "success";
 201                 break;
 202         case NT_STATUS_SEVERITY_INFORMATIONAL:
 203                 s = "info";
 204                 break;
 205         case NT_STATUS_SEVERITY_WARNING:
 206                 s = "warning";
 207                 break;
 208         case NT_STATUS_SEVERITY_ERROR:
 209                 s = "error";
 210                 break;
 211         }
 212 
 213         if (handle) {
 214                 svc = handle->clnt->binding->service;
 215                 name = svc->name;
 216         }
 217 
 218         smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
 219             name, opnum, s, xlate_nt_status(status), status);
 220 }