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
mlrpc_clh_create(mlrpc_handle_t * handle,void * ctx)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
mlrpc_clh_set_auth(mlrpc_handle_t * handle,ndr_auth_ctx_t * auth_ctx)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
mlrpc_clh_bind(mlrpc_handle_t * handle,ndr_service_t * svc)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
mlrpc_clh_unbind(mlrpc_handle_t * handle)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 *
mlrpc_clh_free(mlrpc_handle_t * handle)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
ndr_rpc_call(mlrpc_handle_t * handle,int opnum,void * params)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
ndr_rpc_set_nonull(mlrpc_handle_t * handle)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
ndr_rpc_get_ssnkey(mlrpc_handle_t * handle,uchar_t * key,size_t len)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 *
ndr_rpc_malloc(mlrpc_handle_t * handle,size_t size)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 *
ndr_rpc_get_heap(mlrpc_handle_t * handle)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
ndr_rpc_release(mlrpc_handle_t * handle)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
ndr_is_null_handle(mlrpc_handle_t * handle)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
ndr_is_bind_handle(mlrpc_handle_t * handle)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
ndr_inherit_handle(mlrpc_handle_t * child,mlrpc_handle_t * parent)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
ndr_xa_init(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_exchange(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_read(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_preserve(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_destruct(ndr_client_t * clnt,ndr_xa_t * mxa)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
ndr_xa_release(ndr_client_t * clnt)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