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