xref: /illumos-gate/usr/src/cmd/smbsrv/smbd/smbd_doorsvc.c (revision 6d02032db7b674f185405d42cc8bf10a46a9ab3a)
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  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <sys/list.h>
26 #include <assert.h>
27 #include <alloca.h>
28 #include <door.h>
29 #include <errno.h>
30 #include <syslog.h>
31 #include <unistd.h>
32 #include <stdio.h>
33 #include <synch.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <pthread.h>
39 #include <strings.h>
40 #include <smbsrv/smb_door.h>
41 #include <smbsrv/smb_xdr.h>
42 #include <smbsrv/smb_token.h>
43 #include <smbsrv/libmlsvc.h>
44 #include <smbsrv/libsmbns.h>
45 #include "smbd.h"
46 
47 #define	SMBD_ARG_MAGIC		0x53415247	/* 'SARG' */
48 
49 /*
50  * Parameter for door operations.
51  */
52 typedef struct smbd_arg {
53 	uint32_t	magic;
54 	list_node_t	lnd;
55 	smb_doorhdr_t	hdr;
56 	const char	*opname;
57 	char		*data;
58 	size_t		datalen;
59 	char		*rbuf;
60 	size_t		rsize;
61 	boolean_t	response_ready;
62 	boolean_t	response_abort;
63 	uint32_t	status;
64 } smbd_arg_t;
65 
66 /*
67  * The list contains asynchronous requests that have been initiated
68  * but have not yet been collected (via smbd_dop_async_response).
69  */
70 typedef struct smbd_doorsvc {
71 	mutex_t		sd_mutex;
72 	cond_t		sd_cv;
73 	list_t		sd_async_list;
74 	uint32_t	sd_async_count;
75 } smbd_doorsvc_t;
76 
77 static int smbd_dop_null(smbd_arg_t *);
78 static int smbd_dop_async_response(smbd_arg_t *);
79 static int smbd_dop_user_auth_logon(smbd_arg_t *);
80 static int smbd_dop_user_nonauth_logon(smbd_arg_t *);
81 static int smbd_dop_user_auth_logoff(smbd_arg_t *);
82 static int smbd_dop_lookup_sid(smbd_arg_t *);
83 static int smbd_dop_lookup_name(smbd_arg_t *);
84 static int smbd_dop_join(smbd_arg_t *);
85 static int smbd_dop_get_dcinfo(smbd_arg_t *);
86 static int smbd_dop_vss_get_count(smbd_arg_t *);
87 static int smbd_dop_vss_get_snapshots(smbd_arg_t *);
88 static int smbd_dop_vss_map_gmttoken(smbd_arg_t *);
89 static int smbd_dop_ads_find_host(smbd_arg_t *);
90 static int smbd_dop_quota_query(smbd_arg_t *);
91 static int smbd_dop_quota_set(smbd_arg_t *);
92 static int smbd_dop_dfs_get_referrals(smbd_arg_t *);
93 
94 typedef int (*smbd_dop_t)(smbd_arg_t *);
95 
96 typedef struct smbd_doorop {
97 	smb_dopcode_t	opcode;
98 	smbd_dop_t	op;
99 } smbd_doorop_t;
100 
101 smbd_doorop_t smbd_doorops[] = {
102 	{ SMB_DR_NULL,			smbd_dop_null },
103 	{ SMB_DR_ASYNC_RESPONSE,	smbd_dop_async_response },
104 	{ SMB_DR_USER_AUTH_LOGON,	smbd_dop_user_auth_logon },
105 	{ SMB_DR_USER_NONAUTH_LOGON,	smbd_dop_user_nonauth_logon },
106 	{ SMB_DR_USER_AUTH_LOGOFF,	smbd_dop_user_auth_logoff },
107 	{ SMB_DR_LOOKUP_SID,		smbd_dop_lookup_sid },
108 	{ SMB_DR_LOOKUP_NAME,		smbd_dop_lookup_name },
109 	{ SMB_DR_JOIN,			smbd_dop_join },
110 	{ SMB_DR_GET_DCINFO,		smbd_dop_get_dcinfo },
111 	{ SMB_DR_VSS_GET_COUNT,		smbd_dop_vss_get_count },
112 	{ SMB_DR_VSS_GET_SNAPSHOTS,	smbd_dop_vss_get_snapshots },
113 	{ SMB_DR_VSS_MAP_GMTTOKEN,	smbd_dop_vss_map_gmttoken },
114 	{ SMB_DR_ADS_FIND_HOST,		smbd_dop_ads_find_host },
115 	{ SMB_DR_QUOTA_QUERY,		smbd_dop_quota_query },
116 	{ SMB_DR_QUOTA_SET,		smbd_dop_quota_set },
117 	{ SMB_DR_DFS_GET_REFERRALS,	smbd_dop_dfs_get_referrals }
118 };
119 
120 static int smbd_ndoorop = (sizeof (smbd_doorops) / sizeof (smbd_doorops[0]));
121 
122 static smbd_doorsvc_t smbd_doorsvc;
123 static int smbd_door_fd = -1;
124 static int smbd_door_cookie = 0x534D4244;	/* SMBD */
125 static smbd_door_t smbd_door_sdh;
126 
127 static void smbd_door_dispatch(void *, char *, size_t, door_desc_t *, uint_t);
128 static int smbd_door_dispatch_async(smbd_arg_t *);
129 static void smbd_door_release_async(smbd_arg_t *);
130 static void *smbd_door_dispatch_op(void *);
131 
132 /*
133  * Start the smbd door service.  Create and bind to a door.
134  * Returns 0 on success. Otherwise, -1.
135  */
136 int
137 smbd_door_start(void)
138 {
139 	int	newfd;
140 
141 	(void) mutex_lock(&smbd_doorsvc.sd_mutex);
142 
143 	if (smbd_door_fd != -1) {
144 		(void) fprintf(stderr, "smb_doorsrv_start: already started");
145 		(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
146 		return (-1);
147 	}
148 
149 	smbd_door_init(&smbd_door_sdh, "doorsrv");
150 
151 	list_create(&smbd_doorsvc.sd_async_list, sizeof (smbd_arg_t),
152 	    offsetof(smbd_arg_t, lnd));
153 	smbd_doorsvc.sd_async_count = 0;
154 
155 	if ((smbd_door_fd = door_create(smbd_door_dispatch,
156 	    &smbd_door_cookie, DOOR_UNREF)) < 0) {
157 		(void) fprintf(stderr, "smb_doorsrv_start: door_create: %s",
158 		    strerror(errno));
159 		smbd_door_fd = -1;
160 		(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
161 		return (-1);
162 	}
163 
164 	(void) unlink(SMBD_DOOR_NAME);
165 
166 	if ((newfd = creat(SMBD_DOOR_NAME, 0644)) < 0) {
167 		(void) fprintf(stderr, "smb_doorsrv_start: open: %s",
168 		    strerror(errno));
169 		(void) door_revoke(smbd_door_fd);
170 		smbd_door_fd = -1;
171 		(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
172 		return (-1);
173 	}
174 
175 	(void) close(newfd);
176 	(void) fdetach(SMBD_DOOR_NAME);
177 
178 	if (fattach(smbd_door_fd, SMBD_DOOR_NAME) < 0) {
179 		(void) fprintf(stderr, "smb_doorsrv_start: fattach: %s",
180 		    strerror(errno));
181 		(void) door_revoke(smbd_door_fd);
182 		smbd_door_fd = -1;
183 		(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
184 		return (-1);
185 	}
186 
187 	(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
188 	return (smbd_door_fd);
189 }
190 
191 /*
192  * Stop the smbd door service.
193  */
194 void
195 smbd_door_stop(void)
196 {
197 	(void) mutex_lock(&smbd_doorsvc.sd_mutex);
198 
199 	smbd_door_fini(&smbd_door_sdh);
200 
201 	if (smbd_door_fd != -1) {
202 		(void) fdetach(SMBD_DOOR_NAME);
203 		(void) door_revoke(smbd_door_fd);
204 		smbd_door_fd = -1;
205 	}
206 
207 	(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
208 }
209 
210 /*ARGSUSED*/
211 static void
212 smbd_door_dispatch(void *cookie, char *argp, size_t arg_size, door_desc_t *dp,
213     uint_t n_desc)
214 {
215 	smbd_arg_t	dop_arg;
216 	smb_doorhdr_t	*hdr;
217 	size_t		hdr_size;
218 	char		*rbuf = NULL;
219 
220 	smbd_door_enter(&smbd_door_sdh);
221 
222 	if (!smbd_online())
223 		smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0);
224 
225 	bzero(&dop_arg, sizeof (smbd_arg_t));
226 	hdr = &dop_arg.hdr;
227 	hdr_size = xdr_sizeof(smb_doorhdr_xdr, hdr);
228 
229 	if ((cookie != &smbd_door_cookie) || (argp == NULL) ||
230 	    (arg_size < hdr_size)) {
231 		smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0);
232 	}
233 
234 	if (smb_doorhdr_decode(hdr, (uint8_t *)argp, hdr_size) == -1) {
235 		syslog(LOG_DEBUG, "smbd_door_dispatch: header decode failed");
236 		smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0);
237 	}
238 
239 	if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || (hdr->dh_txid == 0)) {
240 		syslog(LOG_DEBUG, "smbd_door_dispatch: invalid header");
241 		smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0);
242 	}
243 
244 	dop_arg.opname = smb_doorhdr_opname(hdr->dh_op);
245 	dop_arg.data = argp + hdr_size;
246 	dop_arg.datalen = hdr->dh_datalen;
247 
248 	if (hdr->dh_op == SMB_DR_ASYNC_RESPONSE) {
249 		/*
250 		 * ASYNC_RESPONSE is used to collect the response
251 		 * to an async call; it cannot be an async call.
252 		 */
253 		hdr->dh_flags &= ~SMB_DF_ASYNC;
254 	}
255 
256 	if (hdr->dh_flags & SMB_DF_ASYNC) {
257 		if (smbd_door_dispatch_async(&dop_arg) == 0)
258 			hdr->dh_door_rc = SMB_DOP_SUCCESS;
259 		else
260 			hdr->dh_door_rc = SMB_DOP_NOT_CALLED;
261 	} else {
262 		(void) smbd_door_dispatch_op(&dop_arg);
263 	}
264 
265 	if ((rbuf = (char *)alloca(dop_arg.rsize + hdr_size)) == NULL) {
266 		errno = ENOMEM;
267 		syslog(LOG_DEBUG, "smbd_door_dispatch[%s]: alloca %m",
268 		    dop_arg.opname);
269 		smbd_door_return(&smbd_door_sdh, NULL, 0, NULL, 0);
270 	}
271 
272 	if (dop_arg.rbuf != NULL) {
273 		(void) memcpy(rbuf + hdr_size, dop_arg.rbuf, dop_arg.rsize);
274 		free(dop_arg.rbuf);
275 	}
276 
277 	hdr->dh_datalen = dop_arg.rsize;
278 	(void) smb_doorhdr_encode(hdr, (uint8_t *)rbuf, hdr_size);
279 	dop_arg.rsize += hdr_size;
280 
281 	smbd_door_return(&smbd_door_sdh, rbuf, dop_arg.rsize, NULL, 0);
282 	/*NOTREACHED*/
283 }
284 
285 /*
286  * Launch a thread to process an asynchronous door call.
287  */
288 static int
289 smbd_door_dispatch_async(smbd_arg_t *req_arg)
290 {
291 	smbd_arg_t	*arg = NULL;
292 	char		*data = NULL;
293 	pthread_attr_t	attr;
294 	pthread_t	tid;
295 	int		rc;
296 
297 	if ((req_arg->hdr.dh_flags & SMB_DF_ASYNC) == 0) {
298 		errno = EINVAL;
299 		return (-1);
300 	}
301 
302 	if ((arg = malloc(sizeof (smbd_arg_t))) == NULL) {
303 		syslog(LOG_DEBUG, "smbd_door_dispatch_async[%s]: %m",
304 		    req_arg->opname);
305 		return (-1);
306 	}
307 
308 	(void) memcpy(arg, req_arg, sizeof (smbd_arg_t));
309 	arg->data = NULL;
310 
311 	if (req_arg->datalen != 0) {
312 		if ((data = malloc(req_arg->datalen)) == NULL) {
313 			free(arg);
314 			syslog(LOG_DEBUG, "smbd_door_dispatch_async[%s]: %m",
315 			    req_arg->opname);
316 			return (-1);
317 		}
318 
319 		(void) memcpy(data, req_arg->data, req_arg->datalen);
320 		arg->data = data;
321 	}
322 
323 	(void) mutex_lock(&smbd_doorsvc.sd_mutex);
324 	arg->magic = SMBD_ARG_MAGIC;
325 	list_insert_tail(&smbd_doorsvc.sd_async_list, arg);
326 	++smbd_doorsvc.sd_async_count;
327 	(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
328 
329 	(void) pthread_attr_init(&attr);
330 	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
331 	rc = pthread_create(&tid, &attr, smbd_door_dispatch_op, arg);
332 	(void) pthread_attr_destroy(&attr);
333 
334 	if (rc != 0) {
335 		(void) mutex_lock(&smbd_doorsvc.sd_mutex);
336 		smbd_door_release_async(arg);
337 		(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
338 	}
339 
340 	return (rc);
341 }
342 
343 /*
344  * Remove an entry from the async response pending list and free
345  * the arg and associated data.
346  *
347  * Must only be called while holding the smbd_doorsvc mutex.
348  */
349 static void
350 smbd_door_release_async(smbd_arg_t *arg)
351 {
352 	if (arg != NULL) {
353 		assert(arg->magic == SMBD_ARG_MAGIC);
354 		arg->magic = (uint32_t)~SMBD_ARG_MAGIC;
355 
356 		list_remove(&smbd_doorsvc.sd_async_list, arg);
357 		--smbd_doorsvc.sd_async_count;
358 		free(arg->data);
359 		arg->data = NULL;
360 		free(arg);
361 	}
362 }
363 
364 /*
365  * All door calls are processed here: synchronous or asynchronous:
366  * - synchronous calls are invoked by direct function call
367  * - asynchronous calls are invoked from a launched thread
368  *
369  * If the kernel has attempted to collect a response before the op
370  * has completed, the arg will have been marked as response_abort
371  * and we can discard the response data and release the arg.
372  *
373  * We send a notification when asynchronous (ASYNC) door calls
374  * from the kernel (SYSSPACE) have completed.
375  */
376 static void *
377 smbd_door_dispatch_op(void *thread_arg)
378 {
379 	smbd_arg_t	*arg = (smbd_arg_t *)thread_arg;
380 	smbd_doorop_t	*doorop;
381 	smb_doorhdr_t	*hdr;
382 	int		i;
383 
384 	if ((!smbd_online()) || arg == NULL)
385 		return (NULL);
386 
387 	hdr = &arg->hdr;
388 	arg->opname = smb_doorhdr_opname(hdr->dh_op);
389 
390 	for (i = 0; i < smbd_ndoorop; ++i) {
391 		doorop = &smbd_doorops[i];
392 
393 		if (hdr->dh_op == doorop->opcode) {
394 			hdr->dh_door_rc = doorop->op(arg);
395 			hdr->dh_status = arg->status;
396 
397 			if ((hdr->dh_flags & SMB_DF_SYSSPACE) &&
398 			    (hdr->dh_flags & SMB_DF_ASYNC)) {
399 				assert(hdr->dh_op != SMB_DR_ASYNC_RESPONSE);
400 
401 				(void) mutex_lock(&smbd_doorsvc.sd_mutex);
402 				if (arg->response_abort) {
403 					free(arg->rbuf);
404 					arg->rbuf = NULL;
405 					smbd_door_release_async(arg);
406 				} else {
407 					arg->response_ready = B_TRUE;
408 				}
409 				(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
410 
411 				(void) smb_kmod_event_notify(hdr->dh_txid);
412 			}
413 
414 			return (NULL);
415 		}
416 	}
417 
418 	syslog(LOG_ERR, "smbd_door_dispatch_op[%s]: invalid op %u",
419 	    arg->opname, hdr->dh_op);
420 	return (NULL);
421 }
422 
423 /*
424  * Wrapper for door_return.  smbd_door_enter() increments a reference count
425  * when a door call is dispatched and smbd_door_return() decrements the
426  * reference count when it completes.
427  *
428  * The reference counting is used in smbd_door_fini() to wait for active
429  * calls to complete before closing the door.
430  */
431 void
432 smbd_door_init(smbd_door_t *sdh, const char *name)
433 {
434 	(void) strlcpy(sdh->sd_name, name, SMBD_DOOR_NAMESZ);
435 }
436 
437 void
438 smbd_door_enter(smbd_door_t *sdh)
439 {
440 	(void) mutex_lock(&sdh->sd_mutex);
441 	++sdh->sd_ncalls;
442 	(void) mutex_unlock(&sdh->sd_mutex);
443 }
444 
445 /*
446  * We have two calls to door_return because the first call (with data)
447  * can fail, which can leave the door call blocked here.  The second
448  * call (with NULL) is guaranteed to unblock and return to the caller.
449  */
450 void
451 smbd_door_return(smbd_door_t *sdh, char *data_ptr, size_t data_size,
452     door_desc_t *desc_ptr, uint_t num_desc)
453 {
454 	(void) mutex_lock(&sdh->sd_mutex);
455 
456 	if (sdh->sd_ncalls == 0)
457 		syslog(LOG_ERR, "smbd_door_return[%s]: unexpected count=0",
458 		    sdh->sd_name);
459 	else
460 		--sdh->sd_ncalls;
461 
462 	(void) cond_broadcast(&sdh->sd_cv);
463 	(void) mutex_unlock(&sdh->sd_mutex);
464 
465 	(void) door_return(data_ptr, data_size, desc_ptr, num_desc);
466 	(void) door_return(NULL, 0, NULL, 0);
467 	/* NOTREACHED */
468 }
469 
470 /*
471  * A door service is about to terminate.
472  * Give active requests a small grace period to complete.
473  */
474 void
475 smbd_door_fini(smbd_door_t *sdh)
476 {
477 	timestruc_t	delay;
478 	int		rc = 0;
479 
480 	(void) mutex_lock(&sdh->sd_mutex);
481 
482 	while (rc != ETIME && sdh->sd_ncalls != 0) {
483 		delay.tv_sec = 1;
484 		delay.tv_nsec = 0;
485 		rc = cond_reltimedwait(&sdh->sd_cv, &sdh->sd_mutex, &delay);
486 	}
487 
488 	if (sdh->sd_ncalls != 0)
489 		syslog(LOG_NOTICE, "smbd_door_fini[%s]: %d remaining",
490 		    sdh->sd_name, sdh->sd_ncalls);
491 
492 	(void) mutex_unlock(&sdh->sd_mutex);
493 }
494 
495 /*
496  * Null door operation: always returns success.
497  * Assumes no request or response data.
498  */
499 /*ARGSUSED*/
500 static int
501 smbd_dop_null(smbd_arg_t *arg)
502 {
503 	return (SMB_DOP_SUCCESS);
504 }
505 
506 /*
507  * Async response handler: setup the rbuf and rsize for the specified
508  * transaction.  This function is used by the kernel to collect the
509  * response half of an asynchronous door call.
510  *
511  * If a door client attempts to collect a response before the op has
512  * completed (!response_ready), mark the arg as response_abort and
513  * set an error.  The response will be discarded when the op completes.
514  */
515 static int
516 smbd_dop_async_response(smbd_arg_t *rsp_arg)
517 {
518 	list_t		*arg_list = &smbd_doorsvc.sd_async_list;
519 	smbd_arg_t	*arg;
520 
521 	(void) mutex_lock(&smbd_doorsvc.sd_mutex);
522 	arg = list_head(arg_list);
523 
524 	while (arg != NULL) {
525 		assert(arg->magic == SMBD_ARG_MAGIC);
526 
527 		if (arg->hdr.dh_txid == rsp_arg->hdr.dh_txid) {
528 			if (!arg->response_ready) {
529 				arg->response_abort = B_TRUE;
530 				rsp_arg->hdr.dh_door_rc = SMB_DOP_NOT_CALLED;
531 				syslog(LOG_NOTICE, "doorsvc[%s]: %u not ready",
532 				    arg->opname, arg->hdr.dh_txid);
533 				break;
534 			}
535 
536 			rsp_arg->rbuf = arg->rbuf;
537 			rsp_arg->rsize = arg->rsize;
538 			arg->rbuf = NULL;
539 			arg->rsize = 0;
540 			smbd_door_release_async(arg);
541 			break;
542 		}
543 
544 		arg = list_next(arg_list, arg);
545 	}
546 
547 	(void) mutex_unlock(&smbd_doorsvc.sd_mutex);
548 	return (SMB_DOP_SUCCESS);
549 }
550 
551 static int
552 smbd_dop_user_nonauth_logon(smbd_arg_t *arg)
553 {
554 	uint32_t	sid = 0;
555 
556 	if (smb_common_decode(arg->data, arg->datalen,
557 	    xdr_uint32_t, &sid) != 0)
558 		return (SMB_DOP_DECODE_ERROR);
559 
560 	smbd_user_nonauth_logon(sid);
561 	return (SMB_DOP_SUCCESS);
562 }
563 
564 static int
565 smbd_dop_user_auth_logoff(smbd_arg_t *arg)
566 {
567 	uint32_t	sid = 0;
568 
569 	if (smb_common_decode(arg->data, arg->datalen,
570 	    xdr_uint32_t, &sid) != 0)
571 		return (SMB_DOP_DECODE_ERROR);
572 
573 	smbd_user_auth_logoff(sid);
574 	return (SMB_DOP_SUCCESS);
575 }
576 
577 /*
578  * Obtains an access token on successful user authentication.
579  */
580 static int
581 smbd_dop_user_auth_logon(smbd_arg_t *arg)
582 {
583 	smb_logon_t	*user_info;
584 	smb_token_t	*token;
585 
586 	user_info = smb_logon_decode((uint8_t *)arg->data,
587 	    arg->datalen);
588 	if (user_info == NULL)
589 		return (SMB_DOP_DECODE_ERROR);
590 
591 	token = smbd_user_auth_logon(user_info);
592 
593 	smb_logon_free(user_info);
594 
595 	if (token == NULL)
596 		return (SMB_DOP_EMPTYBUF);
597 
598 	arg->rbuf = (char *)smb_token_encode(token, &arg->rsize);
599 	smb_token_destroy(token);
600 
601 	if (arg->rbuf == NULL)
602 		return (SMB_DOP_ENCODE_ERROR);
603 	return (SMB_DOP_SUCCESS);
604 }
605 
606 static int
607 smbd_dop_lookup_name(smbd_arg_t *arg)
608 {
609 	smb_domain_t	dinfo;
610 	smb_account_t	ainfo;
611 	lsa_account_t	acct;
612 	char		buf[MAXNAMELEN];
613 
614 	bzero(&acct, sizeof (lsa_account_t));
615 
616 	if (smb_common_decode(arg->data, arg->datalen,
617 	    lsa_account_xdr, &acct) != 0)
618 		return (SMB_DOP_DECODE_ERROR);
619 
620 	if (*acct.a_domain == '\0')
621 		(void) snprintf(buf, MAXNAMELEN, "%s", acct.a_name);
622 	else if (strchr(acct.a_domain, '.') != NULL)
623 		(void) snprintf(buf, MAXNAMELEN, "%s@%s", acct.a_name,
624 		    acct.a_domain);
625 	else
626 		(void) snprintf(buf, MAXNAMELEN, "%s\\%s", acct.a_domain,
627 		    acct.a_name);
628 
629 	acct.a_status = lsa_lookup_name(buf, acct.a_sidtype, &ainfo);
630 	if (acct.a_status == NT_STATUS_SUCCESS) {
631 		acct.a_sidtype = ainfo.a_type;
632 		smb_sid_tostr(ainfo.a_sid, acct.a_sid);
633 		(void) strlcpy(acct.a_name, ainfo.a_name, MAXNAMELEN);
634 
635 		if (smb_domain_lookup_name(ainfo.a_domain, &dinfo))
636 			(void) strlcpy(acct.a_domain, dinfo.di_fqname,
637 			    MAXNAMELEN);
638 		else
639 			(void) strlcpy(acct.a_domain, ainfo.a_domain,
640 			    MAXNAMELEN);
641 		smb_account_free(&ainfo);
642 	}
643 
644 	arg->rbuf = smb_common_encode(&acct, lsa_account_xdr, &arg->rsize);
645 
646 	if (arg->rbuf == NULL)
647 		return (SMB_DOP_ENCODE_ERROR);
648 	return (SMB_DOP_SUCCESS);
649 }
650 
651 static int
652 smbd_dop_lookup_sid(smbd_arg_t *arg)
653 {
654 	smb_domain_t	dinfo;
655 	smb_account_t	ainfo;
656 	lsa_account_t	acct;
657 	smb_sid_t	*sid;
658 
659 	bzero(&acct, sizeof (lsa_account_t));
660 
661 	if (smb_common_decode(arg->data, arg->datalen,
662 	    lsa_account_xdr, &acct) != 0)
663 		return (SMB_DOP_DECODE_ERROR);
664 
665 	sid = smb_sid_fromstr(acct.a_sid);
666 	acct.a_status = lsa_lookup_sid(sid, &ainfo);
667 	smb_sid_free(sid);
668 
669 	if (acct.a_status == NT_STATUS_SUCCESS) {
670 		acct.a_sidtype = ainfo.a_type;
671 		smb_sid_tostr(ainfo.a_sid, acct.a_sid);
672 		(void) strlcpy(acct.a_name, ainfo.a_name, MAXNAMELEN);
673 
674 		if (smb_domain_lookup_name(ainfo.a_domain, &dinfo))
675 			(void) strlcpy(acct.a_domain, dinfo.di_fqname,
676 			    MAXNAMELEN);
677 		else
678 			(void) strlcpy(acct.a_domain, ainfo.a_domain,
679 			    MAXNAMELEN);
680 
681 		smb_account_free(&ainfo);
682 	}
683 
684 	arg->rbuf = smb_common_encode(&acct, lsa_account_xdr, &arg->rsize);
685 
686 	if (arg->rbuf == NULL)
687 		return (SMB_DOP_ENCODE_ERROR);
688 	return (SMB_DOP_SUCCESS);
689 }
690 
691 static int
692 smbd_dop_join(smbd_arg_t *arg)
693 {
694 	smb_joininfo_t	jdi;
695 	uint32_t	status;
696 
697 	bzero(&jdi, sizeof (smb_joininfo_t));
698 
699 	if (smb_common_decode(arg->data, arg->datalen,
700 	    smb_joininfo_xdr, &jdi) != 0)
701 		return (SMB_DOP_DECODE_ERROR);
702 
703 	status = smbd_join(&jdi);
704 
705 	arg->rbuf = smb_common_encode(&status, xdr_uint32_t, &arg->rsize);
706 
707 	if (arg->rbuf == NULL)
708 		return (SMB_DOP_ENCODE_ERROR);
709 	return (SMB_DOP_SUCCESS);
710 }
711 
712 static int
713 smbd_dop_get_dcinfo(smbd_arg_t *arg)
714 {
715 	smb_domainex_t	dxi;
716 
717 	if (!smb_domain_getinfo(&dxi))
718 		return (SMB_DOP_EMPTYBUF);
719 
720 	arg->rbuf = smb_string_encode(dxi.d_dc, &arg->rsize);
721 
722 	if (arg->rbuf == NULL)
723 		return (SMB_DOP_ENCODE_ERROR);
724 	return (SMB_DOP_SUCCESS);
725 }
726 
727 /*
728  * Return the number of snapshots for a dataset
729  */
730 static int
731 smbd_dop_vss_get_count(smbd_arg_t *arg)
732 {
733 	smb_string_t	path;
734 	uint32_t	count;
735 
736 	bzero(&path, sizeof (smb_string_t));
737 	arg->rbuf = NULL;
738 
739 	if (smb_string_decode(&path, arg->data, arg->datalen) != 0)
740 		return (SMB_DOP_DECODE_ERROR);
741 
742 	if (smbd_vss_get_count(path.buf, &count) == 0)
743 		arg->rbuf = smb_common_encode(&count, xdr_uint32_t,
744 		    &arg->rsize);
745 
746 	xdr_free(smb_string_xdr, (char *)&path);
747 
748 	if (arg->rbuf == NULL)
749 		return (SMB_DOP_ENCODE_ERROR);
750 	return (SMB_DOP_SUCCESS);
751 }
752 
753 /*
754  * Return the count and list of snapshots.
755  * The list is in @GMT token format.
756  */
757 static int
758 smbd_dop_vss_get_snapshots(smbd_arg_t *arg)
759 {
760 	char				**gmtp;
761 	smb_gmttoken_query_t		request;
762 	smb_gmttoken_response_t		reply;
763 	uint_t				i;
764 
765 	bzero(&request, sizeof (smb_gmttoken_query_t));
766 	bzero(&reply, sizeof (smb_gmttoken_response_t));
767 
768 	if (smb_common_decode(arg->data, arg->datalen,
769 	    smb_gmttoken_query_xdr, &request) != 0)
770 		return (SMB_DOP_DECODE_ERROR);
771 
772 	reply.gtr_gmttokens.gtr_gmttokens_val = malloc(request.gtq_count *
773 	    sizeof (char *));
774 	bzero(reply.gtr_gmttokens.gtr_gmttokens_val, request.gtq_count *
775 	    sizeof (char *));
776 
777 	if (reply.gtr_gmttokens.gtr_gmttokens_val == NULL) {
778 		xdr_free(smb_gmttoken_query_xdr, (char *)&request);
779 		return (SMB_DOP_EMPTYBUF);
780 	}
781 
782 	smbd_vss_get_snapshots(request.gtq_path, request.gtq_count,
783 	    &reply.gtr_count,
784 	    &reply.gtr_gmttokens.gtr_gmttokens_len,
785 	    reply.gtr_gmttokens.gtr_gmttokens_val);
786 
787 	arg->rbuf = smb_common_encode(&reply, smb_gmttoken_response_xdr,
788 	    &arg->rsize);
789 	if (arg->rbuf == NULL) {
790 		xdr_free(smb_gmttoken_query_xdr, (char *)&request);
791 		return (SMB_DOP_ENCODE_ERROR);
792 	}
793 
794 	for (i = 0, gmtp = reply.gtr_gmttokens.gtr_gmttokens_val;
795 	    (i < request.gtq_count); i++) {
796 		if (*gmtp)
797 			free(*gmtp);
798 		gmtp++;
799 	}
800 
801 	free(reply.gtr_gmttokens.gtr_gmttokens_val);
802 	xdr_free(smb_gmttoken_query_xdr, (char *)&request);
803 	return (SMB_DOP_SUCCESS);
804 }
805 
806 /*
807  * Return the name of the snapshot that matches the dataset path
808  * and @GMT token.
809  */
810 static int
811 smbd_dop_vss_map_gmttoken(smbd_arg_t *arg)
812 {
813 	char			*snapname;
814 	smb_gmttoken_snapname_t	request;
815 
816 	bzero(&request, sizeof (smb_gmttoken_snapname_t));
817 
818 	if (smb_common_decode(arg->data, arg->datalen,
819 	    smb_gmttoken_snapname_xdr, &request) != 0) {
820 		xdr_free(smb_gmttoken_snapname_xdr, (char *)&request);
821 		return (SMB_DOP_DECODE_ERROR);
822 	}
823 
824 	if ((snapname = malloc(MAXPATHLEN)) == NULL) {
825 		xdr_free(smb_gmttoken_snapname_xdr, (char *)&request);
826 		return (NULL);
827 	}
828 
829 	if ((smbd_vss_map_gmttoken(request.gts_path, request.gts_gmttoken,
830 	    snapname) != 0)) {
831 		*snapname = '\0';
832 	}
833 
834 	arg->rbuf = smb_string_encode(snapname, &arg->rsize);
835 	xdr_free(smb_gmttoken_snapname_xdr, (char *)&request);
836 	free(snapname);
837 
838 	if (arg->rbuf == NULL)
839 		return (SMB_DOP_ENCODE_ERROR);
840 	return (SMB_DOP_SUCCESS);
841 }
842 
843 static int
844 smbd_dop_ads_find_host(smbd_arg_t *arg)
845 {
846 	smb_ads_host_info_t	*hinfo = NULL;
847 	char			*hostname = "";
848 	smb_string_t		fqdn;
849 
850 	bzero(&fqdn, sizeof (smb_string_t));
851 
852 	if (smb_string_decode(&fqdn, arg->data, arg->datalen) != 0)
853 		return (SMB_DOP_DECODE_ERROR);
854 
855 	if ((hinfo = smb_ads_find_host(fqdn.buf, NULL)) != NULL)
856 		hostname = hinfo->name;
857 
858 	xdr_free(smb_string_xdr, (char *)&fqdn);
859 
860 	arg->rbuf = smb_string_encode(hostname, &arg->rsize);
861 	free(hinfo);
862 
863 	if (arg->rbuf == NULL)
864 		return (SMB_DOP_ENCODE_ERROR);
865 	return (SMB_DOP_SUCCESS);
866 }
867 
868 /*
869  * Query the list of user/group quota entries for a given filesystem.
870  */
871 static int
872 smbd_dop_quota_query(smbd_arg_t *arg)
873 {
874 	smb_quota_query_t	request;
875 	smb_quota_response_t	reply;
876 	uint32_t		status;
877 
878 	bzero(&request, sizeof (smb_quota_query_t));
879 	bzero(&reply, sizeof (smb_quota_response_t));
880 
881 	if (smb_common_decode(arg->data, arg->datalen,
882 	    smb_quota_query_xdr, &request) != 0)
883 		return (SMB_DOP_DECODE_ERROR);
884 
885 	status = smb_quota_query(&request, &reply);
886 	reply.qr_status = status;
887 
888 	arg->rbuf = smb_common_encode(&reply, smb_quota_response_xdr,
889 	    &arg->rsize);
890 
891 	xdr_free(smb_quota_query_xdr, (char *)&request);
892 	smb_quota_free(&reply);
893 
894 	if (arg->rbuf == NULL)
895 		return (SMB_DOP_ENCODE_ERROR);
896 	return (SMB_DOP_SUCCESS);
897 }
898 
899 /*
900  * Set a list of user/group quota entries for a given filesystem.
901  */
902 static int
903 smbd_dop_quota_set(smbd_arg_t *arg)
904 {
905 	smb_quota_set_t	request;
906 	uint32_t	status = 0;
907 
908 	bzero(&request, sizeof (smb_quota_set_t));
909 
910 	if (smb_common_decode(arg->data, arg->datalen,
911 	    smb_quota_set_xdr, &request) != 0)
912 		return (SMB_DOP_DECODE_ERROR);
913 
914 	status = smb_quota_set(&request);
915 
916 	arg->rbuf = smb_common_encode(&status, xdr_uint32_t, &arg->rsize);
917 	xdr_free(smb_quota_set_xdr, (char *)&request);
918 
919 	if (arg->rbuf == NULL)
920 		return (SMB_DOP_ENCODE_ERROR);
921 	return (SMB_DOP_SUCCESS);
922 }
923 
924 static int
925 smbd_dop_dfs_get_referrals(smbd_arg_t *arg)
926 {
927 	dfs_referral_query_t	request;
928 	dfs_referral_response_t	reply;
929 
930 	bzero(&request, sizeof (request));
931 	bzero(&reply, sizeof (reply));
932 
933 	if (smb_common_decode(arg->data, arg->datalen,
934 	    dfs_referral_query_xdr, &request) != 0)
935 		return (SMB_DOP_DECODE_ERROR);
936 
937 	reply.rp_status = dfs_get_referrals((const char *)request.rq_path,
938 	    request.rq_type, &reply.rp_referrals);
939 
940 	if (reply.rp_status != ERROR_SUCCESS)
941 		bzero(&reply.rp_referrals, sizeof (dfs_info_t));
942 
943 	arg->rbuf = smb_common_encode(&reply, dfs_referral_response_xdr,
944 	    &arg->rsize);
945 
946 	if (reply.rp_status == ERROR_SUCCESS)
947 		dfs_info_free(&reply.rp_referrals);
948 
949 	xdr_free(dfs_referral_query_xdr, (char *)&request);
950 
951 	if (arg->rbuf == NULL)
952 		return (SMB_DOP_ENCODE_ERROR);
953 	return (SMB_DOP_SUCCESS);
954 }
955