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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
30 * ML-RPC Client handle interface and support functions.
33 #include <sys/types.h>
34 #include <sys/fcntl.h>
41 #include <netsmb/smbfs_api.h>
42 #include <smb/ntstatus.h>
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
*);
54 /* See notes in mlrpc_clh_bind */
55 int rpc_pipe_open_retries
= 10;
58 * Create an RPC client binding handle using the given smb_ctx.
59 * That context must already have a session and tree connected.
61 * Returns zero or an errno value.
64 mlrpc_clh_create(mlrpc_handle_t
*handle
, void *ctx
)
66 ndr_client_t
*clnt
= NULL
;
74 if ((clnt
= malloc(sizeof (*clnt
))) == NULL
)
76 bzero(clnt
, sizeof (*clnt
));
81 * Setup the transport functions.
82 * Always a named pipe (for now).
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
;
92 /* See _is_bind_handle */
93 clnt
->handle
= &handle
->handle
;
95 ndr_svc_binding_pool_init(&clnt
->binding_list
,
96 clnt
->binding_pool
, NDR_N_BINDING_POOL
);
98 if ((clnt
->heap
= ndr_heap_create()) == NULL
)
102 bzero(handle
, sizeof (*handle
));
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.
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.
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.
126 * Similar to MSRPC RpcBindingBind()
128 * Returns 0 or an NT_STATUS: (failed in...)
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
138 mlrpc_clh_bind(mlrpc_handle_t
*handle
, ndr_service_t
*svc
)
140 ndr_client_t
*clnt
= NULL
;
141 struct smb_ctx
*ctx
= NULL
;
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
);
154 * Open the named pipe.
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.
165 retries
= rpc_pipe_open_retries
;
167 fd
= smb_fh_open(ctx
, svc
->endpoint
, O_RDWR
);
173 (void) poll(NULL
, 0, 500);
176 status
= RPC_NT_SERVER_TOO_BUSY
;
179 status
= NT_STATUS_ACCESS_DENIED
;
182 status
= RPC_NT_SERVER_UNAVAILABLE
;
190 /* Paranoia, in case of re-bind. */
191 bzero(&handle
->handle
, sizeof (ndr_hdid_t
));
194 * Do the OtW RPC bind.
196 rc
= ndr_clnt_bind(clnt
, svc
, &clnt
->binding
);
198 case NDR_DRC_FAULT_OUT_OF_MEMORY
:
199 status
= NT_STATUS_NO_MEMORY
;
201 case NDR_DRC_FAULT_API_SERVICE_INVALID
:
202 /* svc->..._uuid parse errors */
203 status
= NT_STATUS_INTERNAL_ERROR
;
206 if (NDR_DRC_IS_FAULT(rc
)) {
207 status
= RPC_NT_PROTOCOL_ERROR
;
212 status
= NT_STATUS_SUCCESS
;
217 (void) smb_fh_close(fd
);
225 * Unbind and close the pipe to an RPC service.
227 * Similar to MSRPC RpcBindingUnbind()
228 * This should be called after a dropped connection.
231 mlrpc_clh_unbind(mlrpc_handle_t
*handle
)
233 ndr_client_t
*clnt
= handle
->clnt
;
235 if (clnt
->xa_fd
!= -1) {
236 (void) smb_fh_close(clnt
->xa_fd
);
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.
246 * Otherwise we destroy the heap directly.
248 * Returns the xa_private pointer (if non-NULL) to inform the caller
249 * that it can now be destroyed.
252 mlrpc_clh_free(mlrpc_handle_t
*handle
)
254 ndr_client_t
*clnt
= handle
->clnt
;
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.
265 * Maybe make this function more tolerant?
267 assert(handle
->clnt
->handle
== &handle
->handle
);
269 mlrpc_clh_unbind(handle
);
271 if (clnt
->heap_preserved
)
272 ndr_clnt_free_heap(clnt
); /* xa_release */
274 ndr_heap_destroy(clnt
->heap
);
277 * Note: Caller will free the smb_ctx stored in
278 * clnt->xa_private (or possibly reuse it).
280 private = clnt
->xa_private
;
282 bzero(handle
, sizeof (*handle
));
287 * Call the RPC function identified by opnum. The remote service is
288 * identified by the handle, which should have been initialized by
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
296 ndr_rpc_call(mlrpc_handle_t
*handle
, int opnum
, void *params
)
298 ndr_client_t
*clnt
= handle
->clnt
;
301 if (ndr_rpc_get_heap(handle
) == NULL
)
304 rc
= ndr_clnt_call(clnt
->binding
, opnum
, params
);
307 * Always clear the nonull flag to ensure
308 * it is not applied to subsequent calls.
310 clnt
->nonull
= B_FALSE
;
312 if (NDR_DRC_IS_FAULT(rc
)) {
313 ndr_rpc_release(handle
);
321 * Outgoing strings should not be null terminated.
324 ndr_rpc_set_nonull(mlrpc_handle_t
*handle
)
326 handle
->clnt
->nonull
= B_TRUE
;
330 * Get the session key from a bound RPC client handle.
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.
338 * Returns zero (success) or an errno.
341 ndr_rpc_get_ssnkey(mlrpc_handle_t
*handle
, uchar_t
*key
, size_t len
)
343 ndr_client_t
*clnt
= handle
->clnt
;
345 if (clnt
== NULL
|| clnt
->xa_fd
== -1)
348 return (smb_fh_getssnkey(clnt
->xa_fd
, key
, len
));
352 ndr_rpc_malloc(mlrpc_handle_t
*handle
, size_t size
)
356 if ((heap
= ndr_rpc_get_heap(handle
)) == NULL
)
359 return (ndr_heap_malloc(heap
, size
));
363 ndr_rpc_get_heap(mlrpc_handle_t
*handle
)
365 ndr_client_t
*clnt
= handle
->clnt
;
367 if (clnt
->heap
== NULL
)
368 clnt
->heap
= ndr_heap_create();
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.
380 ndr_rpc_release(mlrpc_handle_t
*handle
)
382 ndr_client_t
*clnt
= handle
->clnt
;
384 if (clnt
->heap_preserved
)
385 ndr_clnt_free_heap(clnt
);
387 ndr_heap_destroy(clnt
->heap
);
393 * Returns true if the handle is null.
394 * Otherwise returns false.
397 ndr_is_null_handle(mlrpc_handle_t
*handle
)
399 static const ndr_hdid_t hdid0
= {0};
401 if (handle
== NULL
|| handle
->clnt
== NULL
)
404 if (!memcmp(&handle
->handle
, &hdid0
, sizeof (hdid0
)))
411 * Returns true if the handle is the top level bind handle.
412 * Otherwise returns false.
415 ndr_is_bind_handle(mlrpc_handle_t
*handle
)
417 return (handle
->clnt
->handle
== &handle
->handle
);
421 * Pass the client reference from parent to child.
424 ndr_inherit_handle(mlrpc_handle_t
*child
, mlrpc_handle_t
*parent
)
426 child
->clnt
= parent
->clnt
;
430 * ndr_rpc_status remains in libmlsvc mlsvc_client.c
434 * The following functions provide the client callback interface.
435 * If the caller hasn't provided a heap, create one here.
438 ndr_xa_init(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
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
;
446 if ((heap
= ndr_heap_create()) == NULL
)
454 rc
= nds_initialize(send_nds
, 0, NDR_MODE_CALL_SEND
, heap
);
456 rc
= nds_initialize(recv_nds
, NDR_PDU_SIZE_HINT_DEFAULT
,
457 NDR_MODE_RETURN_RECV
, heap
);
460 nds_destruct(&mxa
->recv_nds
);
461 nds_destruct(&mxa
->send_nds
);
462 ndr_heap_destroy(mxa
->heap
);
469 NDS_SETF(send_nds
, NDS_F_NONULL
);
475 * This is the entry pointy for an RPC client call exchange with
476 * a server, which will result in an smbrdr SmbTransact request.
478 * SmbTransact should return the number of bytes received, which
479 * we record as the PDU size, or a negative error code.
482 ndr_xa_exchange(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
484 ndr_stream_t
*recv_nds
= &mxa
->recv_nds
;
485 ndr_stream_t
*send_nds
= &mxa
->send_nds
;
486 int err
, more
, nbytes
;
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
);
493 recv_nds
->pdu_size
= 0;
497 recv_nds
->pdu_size
= nbytes
;
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.
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
512 ndr_xa_read(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
514 ndr_stream_t
*nds
= &mxa
->recv_nds
;
518 if ((len
= (nds
->pdu_max_size
- nds
->pdu_size
)) < 0)
521 nbytes
= smb_fh_read(clnt
->xa_fd
, 0, len
,
522 (char *)nds
->pdu_base_offset
+ nds
->pdu_size
);
527 nds
->pdu_size
+= nbytes
;
529 if (nds
->pdu_size
> nds
->pdu_max_size
) {
530 nds
->pdu_size
= nds
->pdu_max_size
;
538 * Preserve the heap so that the client application has access to data
539 * returned from the server after an RPC call.
542 ndr_xa_preserve(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
544 assert(clnt
->heap
== mxa
->heap
);
546 clnt
->heap_preserved
= B_TRUE
;
551 * Dispose of the transaction streams. If the heap has not been
552 * preserved, we can destroy it here.
555 ndr_xa_destruct(ndr_client_t
*clnt
, ndr_xa_t
*mxa
)
557 nds_destruct(&mxa
->recv_nds
);
558 nds_destruct(&mxa
->send_nds
);
560 if (!clnt
->heap_preserved
) {
561 ndr_heap_destroy(mxa
->heap
);
568 * Dispose of a preserved heap.
571 ndr_xa_release(ndr_client_t
*clnt
)
573 if (clnt
->heap_preserved
) {
574 ndr_heap_destroy(clnt
->heap
);
576 clnt
->heap_preserved
= B_FALSE
;