xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/mlsvc_client.c (revision 88ecc943b4eb72f7c4fbbd8435997b85ef171fc3)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Client NDR RPC interface.
28  */
29 
30 #include <sys/errno.h>
31 #include <strings.h>
32 #include <assert.h>
33 #include <smbsrv/libsmb.h>
34 #include <smbsrv/libsmbrdr.h>
35 #include <smbsrv/libmlrpc.h>
36 #include <smbsrv/libmlsvc.h>
37 
38 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *);
39 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *);
40 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *);
41 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *);
42 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *);
43 static void ndr_xa_release(ndr_client_t *);
44 
45 /*
46  * This call must be made to initialize an RPC client structure and bind
47  * to the remote service before any RPCs can be exchanged with that service.
48  *
49  * The mlsvc_handle_t is a wrapper that is used to associate an RPC handle
50  * with the client context for an instance of the interface.  The handle
51  * is zeroed to ensure that it doesn't look like a valid handle -
52  * handle content is provided by the remove service.
53  *
54  * The client points to this top-level handle so that we know when to
55  * unbind and teardown the connection.  As each handle is initialized it
56  * will inherit a reference to the client context.
57  */
58 int
59 ndr_rpc_bind(mlsvc_handle_t *handle, char *server, char *domain,
60     char *username, const char *service)
61 {
62 	ndr_client_t		*clnt;
63 	ndr_service_t		*svc;
64 	smbrdr_session_info_t	si;
65 	int			fid;
66 	int			rc;
67 
68 	if (handle == NULL || server == NULL ||
69 	    domain == NULL || username == NULL)
70 		return (-1);
71 
72 	if ((svc = ndr_svc_lookup_name(service)) == NULL)
73 		return (-1);
74 
75 	if ((clnt = malloc(sizeof (ndr_client_t))) == NULL)
76 		return (-1);
77 
78 	fid = smbrdr_open_pipe(server, domain, username, svc->endpoint);
79 	if (fid < 0) {
80 		free(clnt);
81 		return (-1);
82 	}
83 
84 	bzero(clnt, sizeof (ndr_client_t));
85 	clnt->handle = &handle->handle;
86 	clnt->fid = fid;
87 
88 	ndr_svc_binding_pool_init(&clnt->binding_list,
89 	    clnt->binding_pool, NDR_N_BINDING_POOL);
90 
91 	clnt->xa_init = ndr_xa_init;
92 	clnt->xa_exchange = ndr_xa_exchange;
93 	clnt->xa_read = ndr_xa_read;
94 	clnt->xa_preserve = ndr_xa_preserve;
95 	clnt->xa_destruct = ndr_xa_destruct;
96 	clnt->xa_release = ndr_xa_release;
97 
98 	(void) smbrdr_session_info(fid, &si);
99 	bzero(&handle->handle, sizeof (ndr_hdid_t));
100 	handle->clnt = clnt;
101 	handle->remote_os = si.si_server_os;
102 
103 	if (ndr_rpc_get_heap(handle) == NULL) {
104 		free(clnt);
105 		return (-1);
106 	}
107 
108 	rc = ndr_clnt_bind(clnt, service, &clnt->binding);
109 	if (NDR_DRC_IS_FAULT(rc)) {
110 		(void) smbrdr_close_pipe(fid);
111 		ndr_heap_destroy(clnt->heap);
112 		free(clnt);
113 		handle->clnt = NULL;
114 		return (-1);
115 	}
116 
117 	return (0);
118 }
119 
120 /*
121  * Unbind and close the pipe to an RPC service.
122  *
123  * If the heap has been preserved we need to go through an xa release.
124  * The heap is preserved during an RPC call because that's where data
125  * returned from the server is stored.
126  *
127  * Otherwise we destroy the heap directly.
128  */
129 void
130 ndr_rpc_unbind(mlsvc_handle_t *handle)
131 {
132 	ndr_client_t *clnt = handle->clnt;
133 
134 	if (clnt->heap_preserved)
135 		ndr_clnt_free_heap(clnt);
136 	else
137 		ndr_heap_destroy(clnt->heap);
138 
139 	(void) smbrdr_close_pipe(clnt->fid);
140 	free(handle->clnt);
141 	bzero(handle, sizeof (mlsvc_handle_t));
142 }
143 
144 /*
145  * Call the RPC function identified by opnum.  The remote service is
146  * identified by the handle, which should have been initialized by
147  * ndr_rpc_bind.
148  *
149  * If the RPC call is successful (returns 0), the caller must call
150  * ndr_rpc_release to release the heap.  Otherwise, we release the
151  * heap here.
152  */
153 int
154 ndr_rpc_call(mlsvc_handle_t *handle, int opnum, void *params)
155 {
156 	ndr_client_t *clnt = handle->clnt;
157 	int rc;
158 
159 	if (ndr_rpc_get_heap(handle) == NULL)
160 		return (-1);
161 
162 	rc = ndr_clnt_call(clnt->binding, opnum, params);
163 
164 	if (NDR_DRC_IS_FAULT(rc)) {
165 		ndr_rpc_release(handle);
166 		return (-1);
167 	}
168 
169 	return (0);
170 }
171 
172 /*
173  * Set information about the remote RPC server in the handle.
174  */
175 void
176 ndr_rpc_server_setinfo(mlsvc_handle_t *handle,
177     const srvsvc_server_info_t *svinfo)
178 {
179 	bcopy(svinfo, &handle->svinfo, sizeof (srvsvc_server_info_t));
180 	handle->svinfo.sv_name = NULL;
181 	handle->svinfo.sv_comment = NULL;
182 
183 	if (svinfo->sv_version_major > 4)
184 		handle->remote_os = NATIVE_OS_WIN2000;
185 	else
186 		handle->remote_os = NATIVE_OS_WINNT;
187 
188 	smb_tracef("NdrRpcServerSetInfo: %s (version %d.%d)",
189 	    svinfo->sv_name ? svinfo->sv_name : "<unknown>",
190 	    svinfo->sv_version_major, svinfo->sv_version_minor);
191 }
192 
193 /*
194  * Get information about the remote RPC server from the handle.
195  */
196 void
197 ndr_rpc_server_getinfo(mlsvc_handle_t *handle, srvsvc_server_info_t *svinfo)
198 {
199 	bcopy(&handle->svinfo, svinfo, sizeof (srvsvc_server_info_t));
200 }
201 
202 /*
203  * Returns the Native-OS of the RPC server.
204  */
205 int
206 ndr_rpc_server_os(mlsvc_handle_t *handle)
207 {
208 	return (handle->remote_os);
209 }
210 
211 /*
212  * Get the session key from a bound RPC client handle.
213  *
214  * The key returned is the 16-byte "user session key"
215  * established by the underlying authentication protocol
216  * (either Kerberos or NTLM).  This key is needed for
217  * SAM RPC calls such as SamrSetInformationUser, etc.
218  * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25.
219  *
220  * Returns zero (success) or an errno.
221  */
222 int
223 ndr_rpc_get_ssnkey(mlsvc_handle_t *handle,
224 	unsigned char *ssn_key, size_t len)
225 {
226 	ndr_client_t *clnt = handle->clnt;
227 	int rc;
228 
229 	if (clnt == NULL)
230 		return (EINVAL);
231 
232 	rc = smbrdr_get_ssnkey(clnt->fid, ssn_key, len);
233 	return (rc);
234 }
235 
236 void *
237 ndr_rpc_malloc(mlsvc_handle_t *handle, size_t size)
238 {
239 	ndr_heap_t *heap;
240 
241 	if ((heap = ndr_rpc_get_heap(handle)) == NULL)
242 		return (NULL);
243 
244 	return (ndr_heap_malloc(heap, size));
245 }
246 
247 ndr_heap_t *
248 ndr_rpc_get_heap(mlsvc_handle_t *handle)
249 {
250 	ndr_client_t *clnt = handle->clnt;
251 
252 	if (clnt->heap == NULL)
253 		clnt->heap = ndr_heap_create();
254 
255 	return (clnt->heap);
256 }
257 
258 /*
259  * Must be called by RPC clients to free the heap after a successful RPC
260  * call, i.e. ndr_rpc_call returned 0.  The caller should take a copy
261  * of any data returned by the RPC prior to calling this function because
262  * returned data is in the heap.
263  */
264 void
265 ndr_rpc_release(mlsvc_handle_t *handle)
266 {
267 	ndr_client_t *clnt = handle->clnt;
268 
269 	if (clnt->heap_preserved)
270 		ndr_clnt_free_heap(clnt);
271 	else
272 		ndr_heap_destroy(clnt->heap);
273 
274 	clnt->heap = NULL;
275 }
276 
277 /*
278  * Returns true if the handle is null.
279  * Otherwise returns false.
280  */
281 boolean_t
282 ndr_is_null_handle(mlsvc_handle_t *handle)
283 {
284 	static ndr_hdid_t zero_handle;
285 
286 	if (handle == NULL || handle->clnt == NULL)
287 		return (B_TRUE);
288 
289 	if (!memcmp(&handle->handle, &zero_handle, sizeof (ndr_hdid_t)))
290 		return (B_TRUE);
291 
292 	return (B_FALSE);
293 }
294 
295 /*
296  * Returns true if the handle is the top level bind handle.
297  * Otherwise returns false.
298  */
299 boolean_t
300 ndr_is_bind_handle(mlsvc_handle_t *handle)
301 {
302 	return (handle->clnt->handle == &handle->handle);
303 }
304 
305 /*
306  * Pass the client reference from parent to child.
307  */
308 void
309 ndr_inherit_handle(mlsvc_handle_t *child, mlsvc_handle_t *parent)
310 {
311 	child->clnt = parent->clnt;
312 	child->remote_os = parent->remote_os;
313 }
314 
315 void
316 ndr_rpc_status(mlsvc_handle_t *handle, int opnum, DWORD status)
317 {
318 	ndr_service_t *svc;
319 	char *name = "NDR RPC";
320 	char *s = "unknown";
321 
322 	if (status == 0)
323 		s = "success";
324 	else if (NT_SC_IS_ERROR(status))
325 		s = "error";
326 	else if (NT_SC_IS_WARNING(status))
327 		s = "warning";
328 	else if (NT_SC_IS_INFO(status))
329 		s = "info";
330 
331 	if (handle) {
332 		svc = handle->clnt->binding->service;
333 		name = svc->name;
334 	}
335 
336 	smb_tracef("%s[0x%02x]: %s: %s (0x%08x)",
337 	    name, opnum, s, xlate_nt_status(status), status);
338 }
339 
340 /*
341  * The following functions provide the client callback interface.
342  * If the caller hasn't provided a heap, create one here.
343  */
344 static int
345 ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa)
346 {
347 	ndr_stream_t *recv_nds = &mxa->recv_nds;
348 	ndr_stream_t *send_nds = &mxa->send_nds;
349 	ndr_heap_t *heap = clnt->heap;
350 
351 	if (heap == NULL) {
352 		if ((heap = ndr_heap_create()) == NULL)
353 			return (-1);
354 
355 		clnt->heap = heap;
356 	}
357 
358 	mxa->heap = heap;
359 
360 	nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap);
361 	nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT,
362 	    NDR_MODE_RETURN_RECV, heap);
363 	return (0);
364 }
365 
366 /*
367  * This is the entry pointy for an RPC client call exchange with
368  * a server, which will result in an smbrdr SmbTransact request.
369  *
370  * SmbTransact should return the number of bytes received, which
371  * we record as the PDU size, or a negative error code.
372  */
373 static int
374 ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa)
375 {
376 	ndr_stream_t *recv_nds = &mxa->recv_nds;
377 	ndr_stream_t *send_nds = &mxa->send_nds;
378 	int nbytes;
379 
380 	nbytes = smbrdr_transact(clnt->fid,
381 	    (char *)send_nds->pdu_base_offset, send_nds->pdu_size,
382 	    (char *)recv_nds->pdu_base_offset, recv_nds->pdu_max_size);
383 
384 	if (nbytes < 0) {
385 		recv_nds->pdu_size = 0;
386 		return (-1);
387 	}
388 
389 	recv_nds->pdu_size = nbytes;
390 	return (nbytes);
391 }
392 
393 /*
394  * This entry point will be invoked if the xa-exchange response contained
395  * only the first fragment of a multi-fragment response.  The RPC client
396  * code will then make repeated xa-read requests to obtain the remaining
397  * fragments, which will result in smbrdr SmbReadX requests.
398  *
399  * SmbReadX should return the number of bytes received, in which case we
400  * expand the PDU size to include the received data, or a negative error
401  * code.
402  */
403 static int
404 ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa)
405 {
406 	ndr_stream_t *nds = &mxa->recv_nds;
407 	int len;
408 	int nbytes;
409 
410 	if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0)
411 		return (-1);
412 
413 	nbytes = smbrdr_readx(clnt->fid,
414 	    (char *)nds->pdu_base_offset + nds->pdu_size, len);
415 
416 	if (nbytes < 0)
417 		return (-1);
418 
419 	nds->pdu_size += nbytes;
420 
421 	if (nds->pdu_size > nds->pdu_max_size) {
422 		nds->pdu_size = nds->pdu_max_size;
423 		return (-1);
424 	}
425 
426 	return (nbytes);
427 }
428 
429 /*
430  * Preserve the heap so that the client application has access to data
431  * returned from the server after an RPC call.
432  */
433 static void
434 ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa)
435 {
436 	assert(clnt->heap == mxa->heap);
437 
438 	clnt->heap_preserved = B_TRUE;
439 	mxa->heap = NULL;
440 }
441 
442 /*
443  * Dispose of the transaction streams.  If the heap has not been
444  * preserved, we can destroy it here.
445  */
446 static void
447 ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa)
448 {
449 	nds_destruct(&mxa->recv_nds);
450 	nds_destruct(&mxa->send_nds);
451 
452 	if (!clnt->heap_preserved) {
453 		ndr_heap_destroy(mxa->heap);
454 		mxa->heap = NULL;
455 		clnt->heap = NULL;
456 	}
457 }
458 
459 /*
460  * Dispose of a preserved heap.
461  */
462 static void
463 ndr_xa_release(ndr_client_t *clnt)
464 {
465 	if (clnt->heap_preserved) {
466 		ndr_heap_destroy(clnt->heap);
467 		clnt->heap = NULL;
468 		clnt->heap_preserved = B_FALSE;
469 	}
470 }
471