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