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 */
76DWORD
77ndr_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 */
179void
180ndr_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
191void
192ndr_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