xref: /illumos-gate/usr/src/lib/libmlrpc/common/mlrpc_clh.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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 
47 static int ndr_xa_init(ndr_client_t *, ndr_xa_t *);
48 static int ndr_xa_exchange(ndr_client_t *, ndr_xa_t *);
49 static int ndr_xa_read(ndr_client_t *, ndr_xa_t *);
50 static void ndr_xa_preserve(ndr_client_t *, ndr_xa_t *);
51 static void ndr_xa_destruct(ndr_client_t *, ndr_xa_t *);
52 static void ndr_xa_release(ndr_client_t *);
53 
54 /* See notes in mlrpc_clh_bind */
55 int 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  */
63 int
64 mlrpc_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 
106 nomem:
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  */
137 uint32_t
138 mlrpc_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;
166 retry_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  */
230 void
231 mlrpc_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  */
251 void *
252 mlrpc_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  */
295 int
296 ndr_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  */
323 void
324 ndr_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  */
340 int
341 ndr_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 
351 void *
352 ndr_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 
362 ndr_heap_t *
363 ndr_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  */
379 void
380 ndr_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  */
396 boolean_t
397 ndr_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  */
414 boolean_t
415 ndr_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  */
423 void
424 ndr_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  */
437 static int
438 ndr_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  */
481 static int
482 ndr_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  */
511 static int
512 ndr_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  */
541 static void
542 ndr_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  */
554 static void
555 ndr_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  */
570 static void
571 ndr_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