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
ndr_rpc_bind_common(mlsvc_handle_t * handle,char * server,char * domain,char * username,const char * service,ndr_auth_ctx_t * auth_ctx)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
ndr_rpc_bind(mlsvc_handle_t * handle,char * server,char * domain,char * username,const char * service)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
ndr_rpc_bind_secure(mlsvc_handle_t * handle,char * server,char * domain,char * username,const char * service,ndr_auth_ctx_t * auth_ctx)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
ndr_rpc_unbind(mlsvc_handle_t * handle)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
ndr_rpc_status(mlsvc_handle_t * handle,int opnum,DWORD status)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