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 * Copyright 2023 RackTop Systems, Inc. 26 */ 27 28 /* 29 * Client NDR RPC interface. 30 */ 31 32 #include <sys/types.h> 33 #include <sys/errno.h> 34 #include <sys/fcntl.h> 35 #include <time.h> 36 #include <strings.h> 37 #include <assert.h> 38 #include <errno.h> 39 #include <thread.h> 40 #include <syslog.h> 41 #include <synch.h> 42 43 #include <libmlrpc/libmlrpc.h> 44 #include <netsmb/smbfs_api.h> 45 46 #include <smbsrv/libsmb.h> 47 #include <smbsrv/libmlsvc.h> 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: (failed in...) 66 * 67 * NT_STATUS_BAD_NETWORK_PATH (get server addr) 68 * NT_STATUS_NETWORK_ACCESS_DENIED (connect, auth) 69 * NT_STATUS_BAD_NETWORK_NAME (tcon) 70 * RPC_NT_SERVER_TOO_BUSY (open pipe) 71 * RPC_NT_SERVER_UNAVAILABLE (open pipe) 72 * NT_STATUS_ACCESS_DENIED (open pipe) 73 * NT_STATUS_INVALID_PARAMETER (rpc bind) 74 * NT_STATUS_INTERNAL_ERROR (bad args etc) 75 * NT_STATUS_NO_MEMORY 76 */ 77 static DWORD 78 ndr_rpc_bind_common(mlsvc_handle_t *handle, char *server, char *domain, 79 char *username, const char *service, ndr_auth_ctx_t *auth_ctx) 80 { 81 struct smb_ctx *ctx = NULL; 82 ndr_service_t *svc; 83 DWORD status; 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 Svc=%s), %s (0x%x)", 114 server, domain, username, service, 115 xlate_nt_status(status), status); 116 /* 117 * If the error is one where changing to a new DC 118 * might help, try looking for a different DC. 119 */ 120 switch (status) { 121 case NT_STATUS_BAD_NETWORK_PATH: 122 case NT_STATUS_BAD_NETWORK_NAME: 123 /* Look for a new DC */ 124 smb_ddiscover_bad_dc(server); 125 default: 126 break; 127 } 128 return (status); 129 } 130 131 /* 132 * Setup the RPC client handle. 133 */ 134 rc = mlrpc_clh_create(handle, ctx); 135 if (rc != 0) { 136 syslog(LOG_ERR, "ndr_rpc_bind: mlrpc_clh_create: rc=%d", rc); 137 smbrdr_ctx_free(ctx); 138 switch (rc) { 139 case ENOMEM: 140 return (NT_STATUS_NO_MEMORY); 141 case EINVAL: 142 return (NT_STATUS_INVALID_PARAMETER); 143 default: 144 return (NT_STATUS_INTERNAL_ERROR); 145 } 146 } 147 148 /* 149 * Setup authentication, if requested. 150 */ 151 status = mlrpc_clh_set_auth(handle, auth_ctx); 152 if (status != 0) { 153 syslog(LOG_DEBUG, "ndr_rpc_bind: " 154 "mlrpc_clh_set_auth, %s (0x%x)", 155 xlate_nt_status(status), status); 156 157 goto errout; 158 } 159 160 /* 161 * This does the pipe open and OtW RPC bind. 162 * Handles pipe open retries. 163 */ 164 status = mlrpc_clh_bind(handle, svc); 165 if (status != 0) { 166 syslog(LOG_DEBUG, "ndr_rpc_bind: mlrpc_clh_bind" 167 "(Srv=%s Dom=%s User=%s Svc=%s), %s (0x%x)", 168 server, domain, username, service, 169 xlate_nt_status(status), status); 170 switch (status) { 171 case RPC_NT_SERVER_TOO_BUSY: 172 /* Look for a new DC */ 173 smb_ddiscover_bad_dc(server); 174 break; 175 default: 176 break; 177 } 178 179 goto errout; 180 } 181 182 return (NT_STATUS_SUCCESS); 183 184 errout: 185 ctx = mlrpc_clh_free(handle); 186 if (ctx != NULL) { 187 smbrdr_ctx_free(ctx); 188 } 189 return (status); 190 } 191 192 DWORD 193 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain, 194 char *username, const char *service) 195 { 196 return (ndr_rpc_bind_common(handle, server, domain, username, service, 197 NULL)); 198 } 199 200 DWORD 201 ndr_rpc_bind_secure(mlsvc_handle_t *handle, char *server, char *domain, 202 char *username, const char *service, ndr_auth_ctx_t *auth_ctx) 203 { 204 return (ndr_rpc_bind_common(handle, server, domain, username, service, 205 auth_ctx)); 206 } 207 208 /* 209 * Unbind and close the pipe to an RPC service 210 * and cleanup the smb_ctx. 211 * 212 * The heap may or may not be destroyed (see mlrpc_clh_free) 213 */ 214 void 215 ndr_rpc_unbind(mlsvc_handle_t *handle) 216 { 217 struct smb_ctx *ctx; 218 219 ctx = mlrpc_clh_free(handle); 220 if (ctx != NULL) 221 smbrdr_ctx_free(ctx); 222 223 bzero(handle, sizeof (mlsvc_handle_t)); 224 } 225 226 void 227 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status) 228 { 229 ndr_service_t *svc; 230 char *name = "NDR RPC"; 231 char *s = "unknown"; 232 233 switch (NT_SC_SEVERITY(status)) { 234 case NT_STATUS_SEVERITY_SUCCESS: 235 s = "success"; 236 break; 237 case NT_STATUS_SEVERITY_INFORMATIONAL: 238 s = "info"; 239 break; 240 case NT_STATUS_SEVERITY_WARNING: 241 s = "warning"; 242 break; 243 case NT_STATUS_SEVERITY_ERROR: 244 s = "error"; 245 break; 246 } 247 248 if (handle) { 249 svc = handle->clnt->binding->service; 250 name = svc->name; 251 } 252 253 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)", 254 name, opnum, s, xlate_nt_status(status), status); 255 } 256