xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c (revision 9b664393d4fdda96221e6ea9ea95790d3c15be70)
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  */
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 static DWORD
77 ndr_rpc_bind_common(mlsvc_handle_t *handle, char *server, char *domain,
78     char *username, const char *service, ndr_auth_ctx_t *auth_ctx)
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 	 * Setup authentication, if requested.
149 	 */
150 	status = mlrpc_clh_set_auth(handle, auth_ctx);
151 	if (status != 0) {
152 		syslog(LOG_DEBUG, "ndr_rpc_bind: "
153 		    "mlrpc_clh_set_auth, %s (0x%x)",
154 		    xlate_nt_status(status), status);
155 
156 		goto errout;
157 	}
158 
159 	/*
160 	 * This does the pipe open and OtW RPC bind.
161 	 * Handles pipe open retries.
162 	 */
163 	status = mlrpc_clh_bind(handle, svc);
164 	if (status != 0) {
165 		syslog(LOG_DEBUG, "ndr_rpc_bind: "
166 		    "mlrpc_clh_bind, %s (0x%x)",
167 		    xlate_nt_status(status), status);
168 		switch (status) {
169 		case RPC_NT_SERVER_TOO_BUSY:
170 			/* Look for a new DC */
171 			smb_ddiscover_bad_dc(server);
172 			break;
173 		default:
174 			break;
175 		}
176 
177 		goto errout;
178 	}
179 
180 	return (NT_STATUS_SUCCESS);
181 
182 errout:
183 	ctx = mlrpc_clh_free(handle);
184 	if (ctx != NULL) {
185 		smbrdr_ctx_free(ctx);
186 	}
187 	return (status);
188 }
189 
190 DWORD
191 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
192     char *username, const char *service)
193 {
194 	return (ndr_rpc_bind_common(handle, server, domain, username, service,
195 	    NULL));
196 }
197 
198 DWORD
199 ndr_rpc_bind_secure(mlsvc_handle_t *handle, char *server, char *domain,
200     char *username, const char *service, ndr_auth_ctx_t *auth_ctx)
201 {
202 	return (ndr_rpc_bind_common(handle, server, domain, username, service,
203 	    auth_ctx));
204 }
205 
206 /*
207  * Unbind and close the pipe to an RPC service
208  * and cleanup the smb_ctx.
209  *
210  * The heap may or may not be destroyed (see mlrpc_clh_free)
211  */
212 void
213 ndr_rpc_unbind(mlsvc_handle_t *handle)
214 {
215 	struct smb_ctx *ctx;
216 
217 	ctx = mlrpc_clh_free(handle);
218 	if (ctx != NULL)
219 		smbrdr_ctx_free(ctx);
220 
221 	bzero(handle, sizeof (mlsvc_handle_t));
222 }
223 
224 void
225 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
226 {
227 	ndr_service_t *svc;
228 	char *name = "NDR RPC";
229 	char *s = "unknown";
230 
231 	switch (NT_SC_SEVERITY(status)) {
232 	case NT_STATUS_SEVERITY_SUCCESS:
233 		s = "success";
234 		break;
235 	case NT_STATUS_SEVERITY_INFORMATIONAL:
236 		s = "info";
237 		break;
238 	case NT_STATUS_SEVERITY_WARNING:
239 		s = "warning";
240 		break;
241 	case NT_STATUS_SEVERITY_ERROR:
242 		s = "error";
243 		break;
244 	}
245 
246 	if (handle) {
247 		svc = handle->clnt->binding->service;
248 		name = svc->name;
249 	}
250 
251 	smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
252 	    name, opnum, s, xlate_nt_status(status), status);
253 }
254