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