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 2020 Tintri by DDN, 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 static DWORD 77 ndr_rpc_bind_common(mlsvc_handle_t *handle, char *server, char *domain, 78 char *username, const char *service, ndr_auth_ctx_t *auth_ctx) 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 * Setup authentication, if requested. 149 */ 150 status = mlrpc_clh_set_auth(handle, auth_ctx); 151 if (status != 0) { 152 syslog(LOG_DEBUG, "ndr_rpc_bind: " 153 "mlrpc_clh_set_auth, %s (0x%x)", 154 xlate_nt_status(status), status); 155 156 goto errout; 157 } 158 159 /* 160 * This does the pipe open and OtW RPC bind. 161 * Handles pipe open retries. 162 */ 163 status = mlrpc_clh_bind(handle, svc); 164 if (status != 0) { 165 syslog(LOG_DEBUG, "ndr_rpc_bind: " 166 "mlrpc_clh_bind, %s (0x%x)", 167 xlate_nt_status(status), status); 168 switch (status) { 169 case RPC_NT_SERVER_TOO_BUSY: 170 /* Look for a new DC */ 171 smb_ddiscover_bad_dc(server); 172 break; 173 default: 174 break; 175 } 176 177 goto errout; 178 } 179 180 return (NT_STATUS_SUCCESS); 181 182 errout: 183 ctx = mlrpc_clh_free(handle); 184 if (ctx != NULL) { 185 smbrdr_ctx_free(ctx); 186 } 187 return (status); 188 } 189 190 DWORD 191 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 192 char *username, const char *service) 193 { 194 return (ndr_rpc_bind_common(handle, server, domain, username, service, 195 NULL)); 196 } 197 198 DWORD 199 ndr_rpc_bind_secure(mlsvc_handle_t *handle, char *server, char *domain, 200 char *username, const char *service, ndr_auth_ctx_t *auth_ctx) 201 { 202 return (ndr_rpc_bind_common(handle, server, domain, username, service, 203 auth_ctx)); 204 } 205 206 /* 207 * Unbind and close the pipe to an RPC service 208 * and cleanup the smb_ctx. 209 * 210 * The heap may or may not be destroyed (see mlrpc_clh_free) 211 */ 212 void 213 ndr_rpc_unbind(mlsvc_handle_t *handle) 214 { 215 struct smb_ctx *ctx; 216 217 ctx = mlrpc_clh_free(handle); 218 if (ctx != NULL) 219 smbrdr_ctx_free(ctx); 220 221 bzero(handle, sizeof (mlsvc_handle_t)); 222 } 223 224 void 225 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 226 { 227 ndr_service_t *svc; 228 char *name = "NDR RPC"; 229 char *s = "unknown"; 230 231 switch (NT_SC_SEVERITY(status)) { 232 case NT_STATUS_SEVERITY_SUCCESS: 233 s = "success"; 234 break; 235 case NT_STATUS_SEVERITY_INFORMATIONAL: 236 s = "info"; 237 break; 238 case NT_STATUS_SEVERITY_WARNING: 239 s = "warning"; 240 break; 241 case NT_STATUS_SEVERITY_ERROR: 242 s = "error"; 243 break; 244 } 245 246 if (handle) { 247 svc = handle->clnt->binding->service; 248 name = svc->name; 249 } 250 251 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 252 name, opnum, s, xlate_nt_status(status), status); 253 } 254