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 }