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 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
27 */
28
29/*
30 * ML-RPC Client handle interface and support functions.
31 */
32
33#include <sys/types.h>
34#include <sys/fcntl.h>
35#include <sys/poll.h>
36
37#include <errno.h>
38#include <strings.h>
39#include <unistd.h>
40
41#include <netsmb/smbfs_api.h>
42#include <smb/ntstatus.h>
43#include <libmlrpc.h>
44
45#include <assert.h>
46
47static int ndr_xa_init(ndr_client_t *, ndr_xa_t *);
48static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *);
49static int ndr_xa_read(ndr_client_t *, ndr_xa_t *);
50static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *);
51static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *);
52static void ndr_xa_release(ndr_client_t *);
53
54/* See notes in mlrpc_clh_bind */
55int rpc_pipe_open_retries = 10;
56
57/*
58 * Create an RPC client binding handle using the given smb_ctx.
59 * That context must already have a session and tree connected.
60 *
61 * Returns zero or an errno value.
62 */
63int
64mlrpc_clh_create(mlrpc_handle_t *handle, void *ctx)
65{
66	ndr_client_t	*clnt = NULL;
67
68	if (ctx == NULL)
69		return (EINVAL);
70
71	/*
72	 * Allocate...
73	 */
74	if ((clnt = malloc(sizeof (*clnt))) == NULL)
75		return (ENOMEM);
76	bzero(clnt, sizeof (*clnt));
77
78	clnt->xa_fd = -1;
79
80	/*
81	 * Setup the transport functions.
82	 * Always a named pipe (for now).
83	 */
84	clnt->xa_private = ctx;
85	clnt->xa_init = ndr_xa_init;
86	clnt->xa_exchange = ndr_xa_exchange;
87	clnt->xa_read = ndr_xa_read;
88	clnt->xa_preserve = ndr_xa_preserve;
89	clnt->xa_destruct = ndr_xa_destruct;
90	clnt->xa_release = ndr_xa_release;
91
92	/* See _is_bind_handle */
93	clnt->handle = &handle->handle;
94
95	ndr_svc_binding_pool_init(&clnt->binding_list,
96	    clnt->binding_pool, NDR_N_BINDING_POOL);
97
98	if ((clnt->heap = ndr_heap_create()) == NULL)
99		goto nomem;
100
101	/* success! */
102	bzero(handle, sizeof (*handle));
103	handle->clnt = clnt;
104	return (0);
105
106nomem:
107	free(clnt);
108	return (ENOMEM);
109}
110
111
112/*
113 * This call must be made to initialize an RPC client structure and bind
114 * to the remote service before any RPCs can be exchanged with that service.
115 *
116 * The mlrpc_handle_t is a wrapper that is used to associate an RPC handle
117 * with the client context for an instance of the interface.  The handle
118 * is zeroed to ensure that it doesn't look like a valid handle -
119 * handle content is provided by the remove service.
120 *
121 * The client points to this top-level handle so that we know when to
122 * unbind and teardown the connection.  As each handle is initialized it
123 * will inherit a reference to the client context.
124 *
125 *
126 * Similar to MSRPC RpcBindingBind()
127 *
128 * Returns 0 or an NT_STATUS:		(failed in...)
129 *
130 *	RPC_NT_SERVER_TOO_BUSY		(open pipe)
131 *	RPC_NT_SERVER_UNAVAILABLE	(open pipe)
132 *	NT_STATUS_ACCESS_DENIED		(open pipe)
133 *	NT_STATUS_INVALID_PARAMETER	(rpc bind)
134 *	NT_STATUS_INTERNAL_ERROR	(bad args etc)
135 *	NT_STATUS_NO_MEMORY
136 */
137uint32_t
138mlrpc_clh_bind(mlrpc_handle_t *handle, ndr_service_t *svc)
139{
140	ndr_client_t		*clnt = NULL;
141	struct smb_ctx		*ctx = NULL;
142	uint32_t		status = 0;
143	int			fd = -1;
144	int			rc, retries;
145
146	if ((clnt = handle->clnt) == NULL)
147		return (NT_STATUS_INTERNAL_ERROR);
148	if ((ctx = clnt->xa_private) == NULL)
149		return (NT_STATUS_INTERNAL_ERROR);
150	if (clnt->xa_fd != -1)
151		return (NT_STATUS_INTERNAL_ERROR);
152
153	/*
154	 * Open the named pipe.
155	 *
156	 * Sometimes a DC may return NT_STATUS_PIPE_NOT_AVAILABLE for
157	 * the first few seconds during service auto-start.  The client
158	 * translates that to EBUSY, so when we see that, wait a bit
159	 * and retry the open for up to rpc_pipe_open_retries.  If we
160	 * fail even after retries, return RPC_NT_SERVER_TOO_BUSY,
161	 * which is how callers of this layer expect that reported.
162	 * We try up to 10 times, with a 0.5 sec. wait after each
163	 * BUSY failure, giving a total wait here of 5 sec.
164	 */
165	retries = rpc_pipe_open_retries;
166retry_open:
167	fd = smb_fh_open(ctx, svc->endpoint, O_RDWR);
168	if (fd < 0) {
169		rc = errno;
170		switch (rc) {
171		case EBUSY:
172			if (--retries > 0) {
173				(void) poll(NULL, 0, 500);
174				goto retry_open;
175			}
176			status = RPC_NT_SERVER_TOO_BUSY;
177			break;
178		case EACCES:
179			status = NT_STATUS_ACCESS_DENIED;
180			break;
181		default:
182			status = RPC_NT_SERVER_UNAVAILABLE;
183			break;
184		}
185		return (status);
186	}
187
188	clnt->xa_fd = fd;
189
190	/* Paranoia, in case of re-bind. */
191	bzero(&handle->handle, sizeof (ndr_hdid_t));
192
193	/*
194	 * Do the OtW RPC bind.
195	 */
196	rc = ndr_clnt_bind(clnt, svc, &clnt->binding);
197	switch (rc) {
198	case NDR_DRC_FAULT_OUT_OF_MEMORY:
199		status = NT_STATUS_NO_MEMORY;
200		break;
201	case NDR_DRC_FAULT_API_SERVICE_INVALID:
202		/* svc->..._uuid parse errors */
203		status = NT_STATUS_INTERNAL_ERROR;
204		break;
205	default:
206		if (NDR_DRC_IS_FAULT(rc)) {
207			status = RPC_NT_PROTOCOL_ERROR;
208			break;
209		}
210		/* FALLTHROUGH */
211	case NDR_DRC_OK:
212		status = NT_STATUS_SUCCESS;
213	}
214
215	if (status != 0) {
216		if (fd != -1)
217			(void) smb_fh_close(fd);
218		clnt->xa_fd = -1;
219	}
220
221	return (status);
222}
223
224/*
225 * Unbind and close the pipe to an RPC service.
226 *
227 * Similar to MSRPC RpcBindingUnbind()
228 * This should be called after a dropped connection.
229 */
230void
231mlrpc_clh_unbind(mlrpc_handle_t *handle)
232{
233	ndr_client_t *clnt = handle->clnt;
234
235	if (clnt->xa_fd != -1) {
236		(void) smb_fh_close(clnt->xa_fd);
237		clnt->xa_fd = -1;
238	}
239}
240
241/*
242 * If the heap has been preserved we need to go through an xa release.
243 * The heap is preserved during an RPC call because that's where data
244 * returned from the server is stored.
245 *
246 * Otherwise we destroy the heap directly.
247 *
248 * Returns the xa_private pointer (if non-NULL) to inform the caller
249 * that it can now be destroyed.
250 */
251void *
252mlrpc_clh_free(mlrpc_handle_t *handle)
253{
254	ndr_client_t *clnt = handle->clnt;
255	void *private;
256
257	if (clnt == NULL)
258		return (NULL);
259
260	/*
261	 * Should never get an unbind on inherited handles.
262	 * Callers of ndr_inherit_handle() check handles
263	 * with ndr_is_bind_handle() before calling this.
264	 *
265	 * Maybe make this function more tolerant?
266	 */
267	assert(handle->clnt->handle == &handle->handle);
268
269	mlrpc_clh_unbind(handle);
270
271	if (clnt->heap_preserved)
272		ndr_clnt_free_heap(clnt); /* xa_release */
273	else
274		ndr_heap_destroy(clnt->heap);
275
276	/*
277	 * Note: Caller will free the smb_ctx stored in
278	 * clnt->xa_private (or possibly reuse it).
279	 */
280	private = clnt->xa_private;
281	free(clnt);
282	bzero(handle, sizeof (*handle));
283	return (private);
284}
285
286/*
287 * Call the RPC function identified by opnum.  The remote service is
288 * identified by the handle, which should have been initialized by
289 * ndr_rpc_bind.
290 *
291 * If the RPC call is successful (returns 0), the caller must call
292 * ndr_rpc_release to release the heap.  Otherwise, we release the
293 * heap here.
294 */
295int
296ndr_rpc_call(mlrpc_handle_t *handle, int opnum, void *params)
297{
298	ndr_client_t *clnt = handle->clnt;
299	int rc;
300
301	if (ndr_rpc_get_heap(handle) == NULL)
302		return (-1);
303
304	rc = ndr_clnt_call(clnt->binding, opnum, params);
305
306	/*
307	 * Always clear the nonull flag to ensure
308	 * it is not applied to subsequent calls.
309	 */
310	clnt->nonull = B_FALSE;
311
312	if (NDR_DRC_IS_FAULT(rc)) {
313		ndr_rpc_release(handle);
314		return (-1);
315	}
316
317	return (0);
318}
319
320/*
321 * Outgoing strings should not be null terminated.
322 */
323void
324ndr_rpc_set_nonull(mlrpc_handle_t *handle)
325{
326	handle->clnt->nonull = B_TRUE;
327}
328
329/*
330 * Get the session key from a bound RPC client handle.
331 *
332 * The key returned is the 16-byte "user session key"
333 * established by the underlying authentication protocol
334 * (either Kerberos or NTLM).  This key is needed for
335 * SAM RPC calls such as SamrSetInformationUser, etc.
336 * See [MS-SAMR] sections: 2.2.3.3, 2.2.7.21, 2.2.7.25.
337 *
338 * Returns zero (success) or an errno.
339 */
340int
341ndr_rpc_get_ssnkey(mlrpc_handle_t *handle, uchar_t *key, size_t len)
342{
343	ndr_client_t *clnt = handle->clnt;
344
345	if (clnt == NULL || clnt->xa_fd == -1)
346		return (EINVAL);
347
348	return (smb_fh_getssnkey(clnt->xa_fd, key, len));
349}
350
351void *
352ndr_rpc_malloc(mlrpc_handle_t *handle, size_t size)
353{
354	ndr_heap_t *heap;
355
356	if ((heap = ndr_rpc_get_heap(handle)) == NULL)
357		return (NULL);
358
359	return (ndr_heap_malloc(heap, size));
360}
361
362ndr_heap_t *
363ndr_rpc_get_heap(mlrpc_handle_t *handle)
364{
365	ndr_client_t *clnt = handle->clnt;
366
367	if (clnt->heap == NULL)
368		clnt->heap = ndr_heap_create();
369
370	return (clnt->heap);
371}
372
373/*
374 * Must be called by RPC clients to free the heap after a successful RPC
375 * call, i.e. ndr_rpc_call returned 0.  The caller should take a copy
376 * of any data returned by the RPC prior to calling this function because
377 * returned data is in the heap.
378 */
379void
380ndr_rpc_release(mlrpc_handle_t *handle)
381{
382	ndr_client_t *clnt = handle->clnt;
383
384	if (clnt->heap_preserved)
385		ndr_clnt_free_heap(clnt);
386	else
387		ndr_heap_destroy(clnt->heap);
388
389	clnt->heap = NULL;
390}
391
392/*
393 * Returns true if the handle is null.
394 * Otherwise returns false.
395 */
396boolean_t
397ndr_is_null_handle(mlrpc_handle_t *handle)
398{
399	static const ndr_hdid_t hdid0 = {0};
400
401	if (handle == NULL || handle->clnt == NULL)
402		return (B_TRUE);
403
404	if (!memcmp(&handle->handle, &hdid0, sizeof (hdid0)))
405		return (B_TRUE);
406
407	return (B_FALSE);
408}
409
410/*
411 * Returns true if the handle is the top level bind handle.
412 * Otherwise returns false.
413 */
414boolean_t
415ndr_is_bind_handle(mlrpc_handle_t *handle)
416{
417	return (handle->clnt->handle == &handle->handle);
418}
419
420/*
421 * Pass the client reference from parent to child.
422 */
423void
424ndr_inherit_handle(mlrpc_handle_t *child, mlrpc_handle_t *parent)
425{
426	child->clnt = parent->clnt;
427}
428
429/*
430 * ndr_rpc_status remains in libmlsvc mlsvc_client.c
431 */
432
433/*
434 * The following functions provide the client callback interface.
435 * If the caller hasn't provided a heap, create one here.
436 */
437static int
438ndr_xa_init(ndr_client_t *clnt, ndr_xa_t *mxa)
439{
440	ndr_stream_t *recv_nds = &mxa->recv_nds;
441	ndr_stream_t *send_nds = &mxa->send_nds;
442	ndr_heap_t *heap = clnt->heap;
443	int		rc;
444
445	if (heap == NULL) {
446		if ((heap = ndr_heap_create()) == NULL)
447			return (-1);
448
449		clnt->heap = heap;
450	}
451
452	mxa->heap = heap;
453
454	rc = nds_initialize(send_nds, 0, NDR_MODE_CALL_SEND, heap);
455	if (rc == 0)
456		rc = nds_initialize(recv_nds, NDR_PDU_SIZE_HINT_DEFAULT,
457		    NDR_MODE_RETURN_RECV, heap);
458
459	if (rc != 0) {
460		nds_destruct(&mxa->recv_nds);
461		nds_destruct(&mxa->send_nds);
462		ndr_heap_destroy(mxa->heap);
463		mxa->heap = NULL;
464		clnt->heap = NULL;
465		return (-1);
466	}
467
468	if (clnt->nonull)
469		NDS_SETF(send_nds, NDS_F_NONULL);
470
471	return (0);
472}
473
474/*
475 * This is the entry pointy for an RPC client call exchange with
476 * a server, which will result in an smbrdr SmbTransact request.
477 *
478 * SmbTransact should return the number of bytes received, which
479 * we record as the PDU size, or a negative error code.
480 */
481static int
482ndr_xa_exchange(ndr_client_t *clnt, ndr_xa_t *mxa)
483{
484	ndr_stream_t *recv_nds = &mxa->recv_nds;
485	ndr_stream_t *send_nds = &mxa->send_nds;
486	int err, more, nbytes;
487
488	nbytes = recv_nds->pdu_max_size;
489	err = smb_fh_xactnp(clnt->xa_fd,
490	    send_nds->pdu_size, (char *)send_nds->pdu_base_offset,
491	    &nbytes, (char *)recv_nds->pdu_base_offset, &more);
492	if (err) {
493		recv_nds->pdu_size = 0;
494		return (-1);
495	}
496
497	recv_nds->pdu_size = nbytes;
498	return (0);
499}
500
501/*
502 * This entry point will be invoked if the xa-exchange response contained
503 * only the first fragment of a multi-fragment response.  The RPC client
504 * code will then make repeated xa-read requests to obtain the remaining
505 * fragments, which will result in smbrdr SmbReadX requests.
506 *
507 * SmbReadX should return the number of bytes received, in which case we
508 * expand the PDU size to include the received data, or a negative error
509 * code.
510 */
511static int
512ndr_xa_read(ndr_client_t *clnt, ndr_xa_t *mxa)
513{
514	ndr_stream_t *nds = &mxa->recv_nds;
515	int len;
516	int nbytes;
517
518	if ((len = (nds->pdu_max_size - nds->pdu_size)) < 0)
519		return (-1);
520
521	nbytes = smb_fh_read(clnt->xa_fd, 0, len,
522	    (char *)nds->pdu_base_offset + nds->pdu_size);
523
524	if (nbytes < 0)
525		return (-1);
526
527	nds->pdu_size += nbytes;
528
529	if (nds->pdu_size > nds->pdu_max_size) {
530		nds->pdu_size = nds->pdu_max_size;
531		return (-1);
532	}
533
534	return (nbytes);
535}
536
537/*
538 * Preserve the heap so that the client application has access to data
539 * returned from the server after an RPC call.
540 */
541static void
542ndr_xa_preserve(ndr_client_t *clnt, ndr_xa_t *mxa)
543{
544	assert(clnt->heap == mxa->heap);
545
546	clnt->heap_preserved = B_TRUE;
547	mxa->heap = NULL;
548}
549
550/*
551 * Dispose of the transaction streams.  If the heap has not been
552 * preserved, we can destroy it here.
553 */
554static void
555ndr_xa_destruct(ndr_client_t *clnt, ndr_xa_t *mxa)
556{
557	nds_destruct(&mxa->recv_nds);
558	nds_destruct(&mxa->send_nds);
559
560	if (!clnt->heap_preserved) {
561		ndr_heap_destroy(mxa->heap);
562		mxa->heap = NULL;
563		clnt->heap = NULL;
564	}
565}
566
567/*
568 * Dispose of a preserved heap.
569 */
570static void
571ndr_xa_release(ndr_client_t *clnt)
572{
573	if (clnt->heap_preserved) {
574		ndr_heap_destroy(clnt->heap);
575		clnt->heap = NULL;
576		clnt->heap_preserved = B_FALSE;
577	}
578}
579