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