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 <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 DWORD
77 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
78 char *username, const char *service)
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 * This does the pipe open and OtW RPC bind.
149 * Handles pipe open retries.
150 */
151 status = mlrpc_clh_bind(handle, svc);
152 if (status != 0) {
153 syslog(LOG_DEBUG, "ndr_rpc_bind: "
154 "mlrpc_clh_bind, %s (0x%x)",
155 xlate_nt_status(status), status);
156 switch (status) {
157 case RPC_NT_SERVER_TOO_BUSY:
158 /* Look for a new DC */
159 smb_ddiscover_bad_dc(server);
160 break;
161 default:
162 break;
163 }
164 ctx = mlrpc_clh_free(handle);
165 if (ctx != NULL) {
166 smbrdr_ctx_free(ctx);
167 }
168 }
169
170 return (status);
171 }
172
173 /*
174 * Unbind and close the pipe to an RPC service
175 * and cleanup the smb_ctx.
176 *
177 * The heap may or may not be destroyed (see mlrpc_clh_free)
178 */
179 void
180 ndr_rpc_unbind(mlsvc_handle_t *handle)
181 {
182 struct smb_ctx *ctx;
183
184 ctx = mlrpc_clh_free(handle);
185 if (ctx != NULL)
186 smbrdr_ctx_free(ctx);
187
188 bzero(handle, sizeof (mlsvc_handle_t));
189 }
190
191 void
192 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
193 {
194 ndr_service_t *svc;
195 char *name = "NDR RPC";
196 char *s = "unknown";
197
198 switch (NT_SC_SEVERITY(status)) {
199 case NT_STATUS_SEVERITY_SUCCESS:
200 s = "success";
201 break;
202 case NT_STATUS_SEVERITY_INFORMATIONAL:
203 s = "info";
204 break;
205 case NT_STATUS_SEVERITY_WARNING:
206 s = "warning";
207 break;
208 case NT_STATUS_SEVERITY_ERROR:
209 s = "error";
210 break;
211 }
212
213 if (handle) {
214 svc = handle->clnt->binding->service;
215 name = svc->name;
216 }
217
218 smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
219 name, opnum, s, xlate_nt_status(status), status);
220 }