xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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 }
221