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 }