xref: /illumos-gate/usr/src/cmd/smbsrv/smbadm/smbadm.c (revision c4ccc1f9004b70b07e4cdb57641c38ab607306c9)
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  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * This module contains smbadm CLI which offers smb configuration
28  * functionalities.
29  */
30 #include <errno.h>
31 #include <err.h>
32 #include <ctype.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <stdio.h>
36 #include <syslog.h>
37 #include <strings.h>
38 #include <limits.h>
39 #include <getopt.h>
40 #include <libintl.h>
41 #include <zone.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <libgen.h>
45 #include <netinet/in.h>
46 #include <auth_attr.h>
47 #include <locale.h>
48 #include <smbsrv/libsmb.h>
49 #include <smbsrv/libsmbns.h>
50 
51 #if !defined(TEXT_DOMAIN)
52 #define	TEXT_DOMAIN "SYS_TEST"
53 #endif
54 
55 typedef enum {
56 	HELP_ADD_MEMBER,
57 	HELP_CREATE,
58 	HELP_DELETE,
59 	HELP_DEL_MEMBER,
60 	HELP_GET,
61 	HELP_JOIN,
62 	HELP_LIST,
63 	HELP_LOOKUP,
64 	HELP_RENAME,
65 	HELP_SET,
66 	HELP_SHOW,
67 	HELP_USER_DISABLE,
68 	HELP_USER_ENABLE,
69 	HELP_USER_DELETE
70 } smbadm_help_t;
71 
72 #define	SMBADM_CMDF_NONE	0x00
73 #define	SMBADM_CMDF_USER	0x01
74 #define	SMBADM_CMDF_GROUP	0x02
75 #define	SMBADM_CMDF_TYPEMASK	0x0F
76 
77 typedef enum {
78 	SMBADM_GRP_ADDMEMBER = 0,
79 	SMBADM_GRP_DELMEMBER,
80 } smbadm_grp_action_t;
81 
82 #define	SMBADM_ANSBUFSIZ	64
83 
84 typedef struct smbadm_cmdinfo {
85 	char *name;
86 	int (*func)(int, char **);
87 	smbadm_help_t usage;
88 	uint32_t flags;
89 	char *auth;
90 } smbadm_cmdinfo_t;
91 
92 smbadm_cmdinfo_t *curcmd;
93 static char *progname;
94 
95 #define	SMBADM_ACTION_AUTH	"solaris.smf.manage.smb"
96 #define	SMBADM_VALUE_AUTH	"solaris.smf.value.smb"
97 #define	SMBADM_BASIC_AUTH	"solaris.network.hosts.read"
98 
99 static boolean_t smbadm_checkauth(const char *);
100 
101 static void smbadm_usage(boolean_t);
102 static int smbadm_join_workgroup(const char *, boolean_t);
103 static int smbadm_join_domain(const char *, const char *, boolean_t);
104 static void smbadm_extract_domain(char *, char **, char **);
105 
106 static int smbadm_join(int, char **);
107 static int smbadm_list(int, char **);
108 static int smbadm_lookup(int, char **);
109 static void smbadm_lookup_name(char *);
110 static void smbadm_lookup_sid(char *);
111 static int smbadm_group_create(int, char **);
112 static int smbadm_group_delete(int, char **);
113 static int smbadm_group_rename(int, char **);
114 static int smbadm_group_show(int, char **);
115 static void smbadm_group_show_name(const char *, const char *);
116 static int smbadm_group_getprop(int, char **);
117 static int smbadm_group_setprop(int, char **);
118 static int smbadm_group_addmember(int, char **);
119 static int smbadm_group_delmember(int, char **);
120 static int smbadm_group_add_del_member(char *, char *, smbadm_grp_action_t);
121 
122 static int smbadm_user_delete(int, char **);
123 static int smbadm_user_disable(int, char **);
124 static int smbadm_user_enable(int, char **);
125 
126 /* Please keep the order consistent with smbadm(1M) man page */
127 static smbadm_cmdinfo_t smbadm_cmdtable[] =
128 {
129 	{ "create",		smbadm_group_create,	HELP_CREATE,
130 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
131 	{ "delete",		smbadm_group_delete,	HELP_DELETE,
132 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
133 	{ "rename",		smbadm_group_rename,	HELP_RENAME,
134 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
135 	{ "show",		smbadm_group_show,	HELP_SHOW,
136 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
137 	{ "get",		smbadm_group_getprop,	HELP_GET,
138 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
139 	{ "set",		smbadm_group_setprop,	HELP_SET,
140 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
141 	{ "add-member",		smbadm_group_addmember,	HELP_ADD_MEMBER,
142 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
143 	{ "remove-member",	smbadm_group_delmember,	HELP_DEL_MEMBER,
144 		SMBADM_CMDF_GROUP,	SMBADM_ACTION_AUTH },
145 	{ "delete-user",	smbadm_user_delete,	HELP_USER_DELETE,
146 		SMBADM_CMDF_USER,	SMBADM_ACTION_AUTH },
147 	{ "disable-user",	smbadm_user_disable,	HELP_USER_DISABLE,
148 		SMBADM_CMDF_USER,	SMBADM_ACTION_AUTH },
149 	{ "enable-user",	smbadm_user_enable,	HELP_USER_ENABLE,
150 		SMBADM_CMDF_USER,	SMBADM_ACTION_AUTH },
151 	{ "join",		smbadm_join,		HELP_JOIN,
152 		SMBADM_CMDF_NONE,	SMBADM_VALUE_AUTH },
153 	{ "list",		smbadm_list,		HELP_LIST,
154 		SMBADM_CMDF_NONE,	SMBADM_BASIC_AUTH },
155 	{ "lookup",		smbadm_lookup,		HELP_LOOKUP,
156 		SMBADM_CMDF_NONE,	SMBADM_BASIC_AUTH },
157 };
158 
159 #define	SMBADM_NCMD	(sizeof (smbadm_cmdtable) / sizeof (smbadm_cmdtable[0]))
160 
161 typedef struct smbadm_prop {
162 	char *p_name;
163 	char *p_value;
164 } smbadm_prop_t;
165 
166 typedef struct smbadm_prop_handle {
167 	char *p_name;
168 	char *p_dispvalue;
169 	int (*p_setfn)(char *, smbadm_prop_t *);
170 	int (*p_getfn)(char *, smbadm_prop_t *);
171 	boolean_t (*p_chkfn)(smbadm_prop_t *);
172 } smbadm_prop_handle_t;
173 
174 static boolean_t smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval);
175 static int smbadm_prop_parse(char *arg, smbadm_prop_t *prop);
176 static smbadm_prop_handle_t *smbadm_prop_gethandle(char *pname);
177 
178 static boolean_t smbadm_chkprop_priv(smbadm_prop_t *prop);
179 static int smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop);
180 static int smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop);
181 static int smbadm_setprop_backup(char *gname, smbadm_prop_t *prop);
182 static int smbadm_getprop_backup(char *gname, smbadm_prop_t *prop);
183 static int smbadm_setprop_restore(char *gname, smbadm_prop_t *prop);
184 static int smbadm_getprop_restore(char *gname, smbadm_prop_t *prop);
185 static int smbadm_setprop_desc(char *gname, smbadm_prop_t *prop);
186 static int smbadm_getprop_desc(char *gname, smbadm_prop_t *prop);
187 
188 static smbadm_prop_handle_t smbadm_ptable[] = {
189 	{"backup",	"on|off",	smbadm_setprop_backup,
190 	smbadm_getprop_backup,	smbadm_chkprop_priv	},
191 	{"restore",	"on|off",	smbadm_setprop_restore,
192 	smbadm_getprop_restore,	smbadm_chkprop_priv	},
193 	{"take-ownership", "on|off",	smbadm_setprop_tkowner,
194 	smbadm_getprop_tkowner,	smbadm_chkprop_priv	},
195 	{"description",	"<string>",	smbadm_setprop_desc,
196 	smbadm_getprop_desc,	NULL			},
197 };
198 
199 static int smbadm_init(void);
200 static void smbadm_fini(void);
201 static const char *smbadm_pwd_strerror(int error);
202 
203 /*
204  * Number of supported properties
205  */
206 #define	SMBADM_NPROP	(sizeof (smbadm_ptable) / sizeof (smbadm_ptable[0]))
207 
208 static void
209 smbadm_cmdusage(FILE *fp, smbadm_cmdinfo_t *cmd)
210 {
211 	switch (cmd->usage) {
212 	case HELP_ADD_MEMBER:
213 		(void) fprintf(fp,
214 		    gettext("\t%s -m <member> [-m <member>]... <group>\n"),
215 		    cmd->name);
216 		return;
217 
218 	case HELP_CREATE:
219 		(void) fprintf(fp, gettext("\t%s [-d <description>] <group>\n"),
220 		    cmd->name);
221 		return;
222 
223 	case HELP_DELETE:
224 		(void) fprintf(fp, gettext("\t%s <group>\n"), cmd->name);
225 		return;
226 
227 	case HELP_USER_DELETE:
228 	case HELP_USER_DISABLE:
229 	case HELP_USER_ENABLE:
230 		(void) fprintf(fp, gettext("\t%s <username>\n"), cmd->name);
231 		return;
232 
233 	case HELP_GET:
234 		(void) fprintf(fp, gettext("\t%s [-p <property>]... <group>\n"),
235 		    cmd->name);
236 		return;
237 
238 	case HELP_JOIN:
239 #if 0	/* Don't document "-p" yet, still needs work (NEX-11960) */
240 		(void) fprintf(fp, gettext("\t%s [-y] -p <domain>\n"
241 		    "\t%s [-y] -u <username domain>\n"
242 		    "\t%s [-y] -w <workgroup>\n"),
243 		    cmd->name, cmd->name, cmd->name);
244 #else
245 		(void) fprintf(fp, gettext("\t%s [-y] -u <username> <domain>\n"
246 		    "\t%s [-y] -w <workgroup>\n"), cmd->name, cmd->name);
247 #endif
248 		return;
249 
250 	case HELP_LIST:
251 		(void) fprintf(fp, gettext("\t%s\n"), cmd->name);
252 		return;
253 
254 	case HELP_LOOKUP:
255 		(void) fprintf(fp,
256 		    gettext("\t%s <account-name>\n"),
257 		    cmd->name);
258 		return;
259 
260 	case HELP_DEL_MEMBER:
261 		(void) fprintf(fp,
262 		    gettext("\t%s -m <member> [-m <member>]... <group>\n"),
263 		    cmd->name);
264 		return;
265 
266 	case HELP_RENAME:
267 		(void) fprintf(fp, gettext("\t%s <group> <new-group>\n"),
268 		    cmd->name);
269 		return;
270 
271 	case HELP_SET:
272 		(void) fprintf(fp, gettext("\t%s -p <property>=<value> "
273 		    "[-p <property>=<value>]... <group>\n"), cmd->name);
274 		return;
275 
276 	case HELP_SHOW:
277 		(void) fprintf(fp, gettext("\t%s [-mp] [<group>]\n"),
278 		    cmd->name);
279 		return;
280 
281 	default:
282 		break;
283 	}
284 
285 	abort();
286 	/* NOTREACHED */
287 }
288 
289 static void
290 smbadm_usage(boolean_t requested)
291 {
292 	FILE *fp = requested ? stdout : stderr;
293 	boolean_t show_props = B_FALSE;
294 	int i;
295 
296 	if (curcmd == NULL) {
297 		(void) fprintf(fp,
298 		    gettext("usage: %s <subcommand> <args> ...\n"),
299 		    progname);
300 
301 		for (i = 0; i < SMBADM_NCMD; i++)
302 			smbadm_cmdusage(fp, &smbadm_cmdtable[i]);
303 
304 		(void) fprintf(fp,
305 		    gettext("\nFor property list, run %s %s|%s\n"),
306 		    progname, "get", "set");
307 
308 		exit(requested ? 0 : 2);
309 	}
310 
311 	(void) fprintf(fp, gettext("usage:\n"));
312 	smbadm_cmdusage(fp, curcmd);
313 
314 	if (strcmp(curcmd->name, "get") == 0 ||
315 	    strcmp(curcmd->name, "set") == 0)
316 		show_props = B_TRUE;
317 
318 	if (show_props) {
319 		(void) fprintf(fp,
320 		    gettext("\nThe following properties are supported:\n"));
321 
322 		(void) fprintf(fp, "\n\t%-16s   %s\n\n",
323 		    "PROPERTY", "VALUES");
324 
325 		for (i = 0; i < SMBADM_NPROP; i++) {
326 			(void) fprintf(fp, "\t%-16s   %s\n",
327 			    smbadm_ptable[i].p_name,
328 			    smbadm_ptable[i].p_dispvalue);
329 		}
330 	}
331 
332 	exit(requested ? 0 : 2);
333 }
334 
335 /*
336  * smbadm_strcasecmplist
337  *
338  * Find a string 's' within a list of strings.
339  *
340  * Returns the index of the matching string or -1 if there is no match.
341  */
342 static int
343 smbadm_strcasecmplist(const char *s, ...)
344 {
345 	va_list ap;
346 	char *p;
347 	int ndx;
348 
349 	va_start(ap, s);
350 
351 	for (ndx = 0; ((p = va_arg(ap, char *)) != NULL); ++ndx) {
352 		if (strcasecmp(s, p) == 0) {
353 			va_end(ap);
354 			return (ndx);
355 		}
356 	}
357 
358 	va_end(ap);
359 	return (-1);
360 }
361 
362 /*
363  * smbadm_answer_prompt
364  *
365  * Prompt for the answer to a question.  A default response must be
366  * specified, which will be used if the user presses <enter> without
367  * answering the question.
368  */
369 static int
370 smbadm_answer_prompt(const char *prompt, char *answer, const char *dflt)
371 {
372 	char buf[SMBADM_ANSBUFSIZ];
373 	char *p;
374 
375 	(void) printf(gettext("%s [%s]: "), prompt, dflt);
376 
377 	if (fgets(buf, SMBADM_ANSBUFSIZ, stdin) == NULL)
378 		return (-1);
379 
380 	if ((p = strchr(buf, '\n')) != NULL)
381 		*p = '\0';
382 
383 	if (*buf == '\0')
384 		(void) strlcpy(answer, dflt, SMBADM_ANSBUFSIZ);
385 	else
386 		(void) strlcpy(answer, buf, SMBADM_ANSBUFSIZ);
387 
388 	return (0);
389 }
390 
391 /*
392  * smbadm_confirm
393  *
394  * Ask a question that requires a yes/no answer.
395  * A default response must be specified.
396  */
397 static boolean_t
398 smbadm_confirm(const char *prompt, const char *dflt)
399 {
400 	char buf[SMBADM_ANSBUFSIZ];
401 
402 	for (;;) {
403 		if (smbadm_answer_prompt(prompt, buf, dflt) < 0)
404 			return (B_FALSE);
405 
406 		if (smbadm_strcasecmplist(buf, "n", "no", 0) >= 0)
407 			return (B_FALSE);
408 
409 		if (smbadm_strcasecmplist(buf, "y", "yes", 0) >= 0)
410 			return (B_TRUE);
411 
412 		(void) printf(gettext("Please answer yes or no.\n"));
413 	}
414 }
415 
416 static boolean_t
417 smbadm_join_prompt(const char *domain)
418 {
419 	(void) printf(gettext("After joining %s the smb service will be "
420 	    "restarted automatically.\n"), domain);
421 
422 	return (smbadm_confirm("Would you like to continue?", "no"));
423 }
424 
425 static void
426 smbadm_restart_service(void)
427 {
428 	if (smb_smf_restart_service() != 0) {
429 		(void) fprintf(stderr,
430 		    gettext("Unable to restart smb service. "
431 		    "Run 'svcs -xv smb/server' for more information."));
432 	}
433 }
434 
435 /*
436  * smbadm_join
437  *
438  * Join a domain or workgroup.
439  *
440  * When joining a domain, we may receive the username, password and
441  * domain name in any of the following combinations.  Note that the
442  * password is optional on the command line: if it is not provided,
443  * we will prompt for it later.
444  *
445  *	username+password domain
446  *	domain\username+password
447  *	domain/username+password
448  *	username@domain
449  *
450  * We allow domain\name+password or domain/name+password but not
451  * name+password@domain because @ is a valid password character.
452  *
453  * If the username and domain name are passed as separate command
454  * line arguments, we process them directly.  Otherwise we separate
455  * them and continue as if they were separate command line arguments.
456  */
457 static int
458 smbadm_join(int argc, char **argv)
459 {
460 	char buf[MAXHOSTNAMELEN * 2];
461 	char *domain = NULL;
462 	char *username = NULL;
463 	uint32_t mode = 0;
464 	boolean_t do_prompt = B_TRUE;
465 	char option;
466 
467 	while ((option = getopt(argc, argv, "pu:wy")) != -1) {
468 		if (mode != 0) {
469 			(void) fprintf(stderr, gettext(
470 			    "join options are mutually exclusive\n"));
471 			smbadm_usage(B_FALSE);
472 		}
473 		switch (option) {
474 		case 'p':
475 			mode = SMB_SECMODE_DOMAIN;
476 			/* leave username = NULL */
477 			break;
478 
479 		case 'u':
480 			mode = SMB_SECMODE_DOMAIN;
481 			username = optarg;
482 			break;
483 
484 		case 'w':
485 			mode = SMB_SECMODE_WORKGRP;
486 			break;
487 
488 		case 'y':
489 			do_prompt = B_FALSE;
490 			break;
491 
492 		default:
493 			smbadm_usage(B_FALSE);
494 			break;
495 		}
496 	}
497 
498 	if (optind < argc)
499 		domain = argv[optind];
500 
501 	if (username != NULL && domain == NULL) {
502 		/*
503 		 * The domain was not specified as a separate
504 		 * argument, check for the combination forms.
505 		 */
506 		(void) strlcpy(buf, username, sizeof (buf));
507 		smbadm_extract_domain(buf, &username, &domain);
508 	}
509 
510 	if ((domain == NULL) || (*domain == '\0')) {
511 		(void) fprintf(stderr, gettext("missing %s name\n"),
512 		    (mode == SMB_SECMODE_WORKGRP) ? "workgroup" : "domain");
513 		smbadm_usage(B_FALSE);
514 	}
515 
516 	if (mode == SMB_SECMODE_WORKGRP) {
517 		return (smbadm_join_workgroup(domain, do_prompt));
518 	}
519 	return (smbadm_join_domain(domain, username, do_prompt));
520 }
521 
522 /*
523  * Workgroups comprise a collection of standalone, independently administered
524  * computers that use a common workgroup name.  This is a peer-to-peer model
525  * with no formal membership mechanism.
526  */
527 static int
528 smbadm_join_workgroup(const char *workgroup, boolean_t prompt)
529 {
530 	smb_joininfo_t jdi;
531 	smb_joinres_t jdres;
532 	uint32_t status;
533 
534 	bzero(&jdres, sizeof (jdres));
535 	bzero(&jdi, sizeof (jdi));
536 	jdi.mode = SMB_SECMODE_WORKGRP;
537 	(void) strlcpy(jdi.domain_name, workgroup, sizeof (jdi.domain_name));
538 	(void) strtrim(jdi.domain_name, " \t\n");
539 
540 	if (smb_name_validate_workgroup(jdi.domain_name) != ERROR_SUCCESS) {
541 		(void) fprintf(stderr, gettext("workgroup name is invalid\n"));
542 		smbadm_usage(B_FALSE);
543 	}
544 
545 	if (prompt && !smbadm_join_prompt(jdi.domain_name))
546 		return (0);
547 
548 	if ((status = smb_join(&jdi, &jdres)) != NT_STATUS_SUCCESS) {
549 		(void) fprintf(stderr, gettext("failed to join %s: %s\n"),
550 		    jdi.domain_name, xlate_nt_status(status));
551 		return (1);
552 	}
553 
554 	(void) printf(gettext("Successfully joined %s\n"), jdi.domain_name);
555 	smbadm_restart_service();
556 	return (0);
557 }
558 
559 /*
560  * Domains comprise a centrally administered group of computers and accounts
561  * that share a common security and administration policy and database.
562  * Computers must join a domain and become domain members, which requires
563  * an administrator level account name.
564  *
565  * The '+' character is invalid within a username.  We allow the password
566  * to be appended to the username using '+' as a scripting convenience.
567  */
568 static int
569 smbadm_join_domain(const char *domain, const char *username, boolean_t prompt)
570 {
571 	smb_joininfo_t jdi;
572 	smb_joinres_t jdres;
573 	char *passwd_prompt;
574 	char *p;
575 	int len, rc;
576 
577 	bzero(&jdres, sizeof (jdres));
578 	bzero(&jdi, sizeof (jdi));
579 	jdi.mode = SMB_SECMODE_DOMAIN;
580 	(void) strlcpy(jdi.domain_name, domain, sizeof (jdi.domain_name));
581 	(void) strtrim(jdi.domain_name, " \t\n");
582 
583 	if (smb_name_validate_domain(jdi.domain_name) != ERROR_SUCCESS) {
584 		(void) fprintf(stderr, gettext("domain name is invalid\n"));
585 		smbadm_usage(B_FALSE);
586 	}
587 
588 	if (prompt && !smbadm_join_prompt(jdi.domain_name))
589 		return (0);
590 
591 	/*
592 	 * Note: username is null for "unsecure join"
593 	 * (join using a pre-created computer account)
594 	 * No password either.
595 	 */
596 	if (username != NULL) {
597 		if ((p = strchr(username, '+')) != NULL) {
598 			++p;
599 
600 			len = (int)(p - username);
601 			if (len > sizeof (jdi.domain_name))
602 				len = sizeof (jdi.domain_name);
603 
604 			(void) strlcpy(jdi.domain_username, username, len);
605 			(void) strlcpy(jdi.domain_passwd, p,
606 			    sizeof (jdi.domain_passwd));
607 		} else {
608 			(void) strlcpy(jdi.domain_username, username,
609 			    sizeof (jdi.domain_username));
610 		}
611 
612 		if (smb_name_validate_account(jdi.domain_username)
613 		    != ERROR_SUCCESS) {
614 			(void) fprintf(stderr,
615 			    gettext("username contains invalid characters\n"));
616 			smbadm_usage(B_FALSE);
617 		}
618 
619 		if (*jdi.domain_passwd == '\0') {
620 			passwd_prompt = gettext("Enter domain password: ");
621 
622 			if ((p = getpassphrase(passwd_prompt)) == NULL) {
623 				(void) fprintf(stderr, gettext(
624 				    "missing password\n"));
625 				smbadm_usage(B_FALSE);
626 			}
627 
628 			(void) strlcpy(jdi.domain_passwd, p,
629 			    sizeof (jdi.domain_passwd));
630 		}
631 	}
632 
633 	(void) printf(gettext("Joining %s ... this may take a minute ...\n"),
634 	    jdi.domain_name);
635 
636 	rc = smb_join(&jdi, &jdres);
637 	if (rc != 0) {
638 		(void) printf(gettext("Cannot call the SMB service. "
639 		    " (error %d: %s) "
640 		    "Please check the service status "
641 		    "(svcs -vx network/smb/server)\n"),
642 		    rc, strerror(rc));
643 		bzero(&jdi, sizeof (jdi));
644 		return (1);
645 	}
646 
647 	switch (jdres.status) {
648 	case NT_STATUS_SUCCESS:
649 		(void) printf(gettext(
650 		    "Successfully joined domain %s using AD server %s\n"),
651 		    jdi.domain_name, jdres.dc_name);
652 		bzero(&jdi, sizeof (jdi));
653 		smbadm_restart_service();
654 		return (0);
655 
656 	case NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND:
657 		/* See: smb_ads_lookup_msdcs */
658 		(void) fprintf(stderr, gettext(
659 		    "failed to find any AD servers for domain: %s\n"),
660 		    jdi.domain_name);
661 		goto common;
662 
663 	case NT_STATUS_BAD_NETWORK_PATH:
664 		/* See: smbrdr_ctx_new / smb_ctx_resolve */
665 		(void) fprintf(stderr, gettext(
666 		    "failed to resolve address of AD server: %s\n"),
667 		    jdres.dc_name);
668 		goto common;
669 
670 	case NT_STATUS_NETWORK_ACCESS_DENIED:
671 		/* See: smbrdr_ctx_new / smb_ctx_get_ssn */
672 		(void) fprintf(stderr, gettext(
673 		    "failed to authenticate with AD server: %s\n"),
674 		    jdres.dc_name);
675 		goto common;
676 
677 	case NT_STATUS_BAD_NETWORK_NAME:
678 		/*
679 		 * See: smbrdr_ctx_new / smb_ctx_get_tree
680 		 * and: ndr_rpc_bind / smb_fh_open
681 		 */
682 		(void) fprintf(stderr, gettext(
683 		    "failed connecting to services on AD server: %s\n"),
684 		    jdres.dc_name);
685 		goto common;
686 
687 	default:
688 		(void) fprintf(stderr, gettext(
689 		    "failed to join domain %s\n"),
690 		    jdi.domain_name);
691 		if (jdres.dc_name[0] != '\0') {
692 			(void) fprintf(stderr, gettext(
693 			    "using AD server: %s\n"),
694 			    jdres.dc_name);
695 		}
696 		/* FALLTHROUGH */
697 	common:
698 		if (jdres.join_err != 0) {
699 			(void) fprintf(stderr, "%s\n",
700 			    smb_ads_strerror(jdres.join_err));
701 		} else if (jdres.status != 0) {
702 			(void) fprintf(stderr, "(%s)\n",
703 			    xlate_nt_status(jdres.status));
704 		}
705 		(void) fprintf(stderr, gettext("Please refer to the "
706 		    "service log for more information.\n"));
707 		bzero(&jdi, sizeof (jdi));
708 		return (1);
709 	}
710 }
711 
712 /*
713  * We want to process the user and domain names as separate strings.
714  * Check for names of the forms below and separate the components as
715  * required.
716  *
717  *	name@domain
718  *	domain\name
719  *	domain/name
720  *
721  * If we encounter any of the forms above in arg, the @, / or \
722  * separator is replaced by \0 and the username and domain pointers
723  * are changed to point to the appropriate components (in arg).
724  *
725  * If none of the separators are encountered, the username and domain
726  * pointers remain unchanged.
727  */
728 static void
729 smbadm_extract_domain(char *arg, char **username, char **domain)
730 {
731 	char *p;
732 
733 	if ((p = strpbrk(arg, "/\\@")) != NULL) {
734 		if (*p == '@') {
735 			*p = '\0';
736 			++p;
737 
738 			if (strchr(arg, '+') != NULL)
739 				return;
740 
741 			*domain = p;
742 			*username = arg;
743 		} else {
744 			*p = '\0';
745 			++p;
746 			*username = p;
747 			*domain = arg;
748 		}
749 	}
750 }
751 
752 /*
753  * smbadm_list
754  *
755  * Displays current security mode and domain/workgroup name.
756  */
757 /*ARGSUSED*/
758 static int
759 smbadm_list(int argc, char **argv)
760 {
761 	char domain[MAXHOSTNAMELEN];
762 	char fqdn[MAXHOSTNAMELEN];
763 	char srvname[MAXHOSTNAMELEN];
764 	char modename[16];
765 	int rc;
766 	smb_inaddr_t srvipaddr;
767 	char ipstr[INET6_ADDRSTRLEN];
768 
769 	rc = smb_config_getstr(SMB_CI_SECURITY, modename, sizeof (modename));
770 	if (rc != SMBD_SMF_OK) {
771 		(void) fprintf(stderr,
772 		    gettext("cannot determine the operational mode\n"));
773 		return (1);
774 	}
775 
776 	if (smb_getdomainname(domain, sizeof (domain)) != 0) {
777 		(void) fprintf(stderr, gettext("failed to get the %s name\n"),
778 		    modename);
779 		return (1);
780 	}
781 
782 	if (strcmp(modename, "workgroup") == 0) {
783 		(void) printf(gettext("[*] [%s]\n"), domain);
784 		return (0);
785 	}
786 
787 	(void) printf(gettext("[*] [%s]\n"), domain);
788 	if ((smb_getfqdomainname(fqdn, sizeof (fqdn)) == 0) && (*fqdn != '\0'))
789 		(void) printf(gettext("[*] [%s]\n"), fqdn);
790 
791 	if ((smb_get_dcinfo(srvname, MAXHOSTNAMELEN, &srvipaddr)
792 	    == NT_STATUS_SUCCESS) && (*srvname != '\0') &&
793 	    (!smb_inet_iszero(&srvipaddr))) {
794 		(void) smb_inet_ntop(&srvipaddr, ipstr,
795 		    SMB_IPSTRLEN(srvipaddr.a_family));
796 		(void) printf(gettext("\t[+%s] [%s]\n"),
797 		    srvname, ipstr);
798 	}
799 
800 	/* Print the local and domain SID. */
801 	smb_domain_show();
802 	return (0);
803 }
804 
805 /*
806  * smbadm_lookup
807  *
808  * Lookup the SID for a given account (user or group)
809  */
810 static int
811 smbadm_lookup(int argc, char **argv)
812 {
813 	int i;
814 
815 	if (argc < 2) {
816 		(void) fprintf(stderr, gettext("missing account name\n"));
817 		smbadm_usage(B_FALSE);
818 	}
819 
820 	for (i = 1; i < argc; i++) {
821 		if (strncmp(argv[i], "S-1-", 4) == 0)
822 			smbadm_lookup_sid(argv[i]);
823 		else
824 			smbadm_lookup_name(argv[i]);
825 	}
826 	return (0);
827 }
828 
829 static void
830 smbadm_lookup_name(char *name)
831 {
832 	lsa_account_t	acct;
833 	int rc;
834 
835 	if ((rc = smb_lookup_name(name, SidTypeUnknown, &acct)) != 0) {
836 		(void) fprintf(stderr, gettext(
837 		    "\t\t%s: lookup name failed, rc=%d\n"),
838 		    name, rc);
839 		return;
840 	}
841 	if (acct.a_status != NT_STATUS_SUCCESS) {
842 		(void) fprintf(stderr, gettext("\t\t%s [%s]\n"),
843 		    name, xlate_nt_status(acct.a_status));
844 		return;
845 	}
846 	(void) printf("\t%s\n", acct.a_sid);
847 }
848 
849 static void
850 smbadm_lookup_sid(char *sidstr)
851 {
852 	lsa_account_t	acct;
853 	int rc;
854 
855 	if ((rc = smb_lookup_sid(sidstr, &acct)) != 0) {
856 		(void) fprintf(stderr, gettext(
857 		    "\t\t%s: lookup SID failed, rc=%d\n"),
858 		    sidstr, rc);
859 		return;
860 	}
861 	if (acct.a_status != NT_STATUS_SUCCESS) {
862 		(void) fprintf(stderr, gettext("\t\t%s [%s]\n"),
863 		    sidstr, xlate_nt_status(acct.a_status));
864 		return;
865 	}
866 	(void) printf("\t%s\\%s\n", acct.a_domain, acct.a_name);
867 }
868 
869 /*
870  * smbadm_group_create
871  *
872  * Creates a local SMB group
873  */
874 static int
875 smbadm_group_create(int argc, char **argv)
876 {
877 	char *gname = NULL;
878 	char *desc = NULL;
879 	char option;
880 	int status;
881 
882 	while ((option = getopt(argc, argv, "d:")) != -1) {
883 		switch (option) {
884 		case 'd':
885 			desc = optarg;
886 			break;
887 
888 		default:
889 			smbadm_usage(B_FALSE);
890 		}
891 	}
892 
893 	gname = argv[optind];
894 	if (optind >= argc || gname == NULL || *gname == '\0') {
895 		(void) fprintf(stderr, gettext("missing group name\n"));
896 		smbadm_usage(B_FALSE);
897 	}
898 
899 	status = smb_lgrp_add(gname, desc);
900 	if (status != SMB_LGRP_SUCCESS) {
901 		(void) fprintf(stderr,
902 		    gettext("failed to create %s (%s)\n"), gname,
903 		    smb_lgrp_strerror(status));
904 	} else {
905 		(void) printf(gettext("%s created\n"), gname);
906 	}
907 
908 	return (status);
909 }
910 
911 /*
912  * smbadm_group_dump_members
913  *
914  * Dump group members details.
915  */
916 static void
917 smbadm_group_dump_members(smb_gsid_t *members, int num)
918 {
919 	char		sidstr[SMB_SID_STRSZ];
920 	lsa_account_t	acct;
921 	int		i;
922 
923 	if (num == 0) {
924 		(void) printf(gettext("\tNo members\n"));
925 		return;
926 	}
927 
928 	(void) printf(gettext("\tMembers:\n"));
929 	for (i = 0; i < num; i++) {
930 		smb_sid_tostr(members[i].gs_sid, sidstr);
931 
932 		if (smb_lookup_sid(sidstr, &acct) == 0) {
933 			if (acct.a_status == NT_STATUS_SUCCESS)
934 				smbadm_group_show_name(acct.a_domain,
935 				    acct.a_name);
936 			else
937 				(void) printf(gettext("\t\t%s [%s]\n"),
938 				    sidstr, xlate_nt_status(acct.a_status));
939 		} else {
940 			(void) printf(gettext("\t\t%s\n"), sidstr);
941 		}
942 	}
943 }
944 
945 static void
946 smbadm_group_show_name(const char *domain, const char *name)
947 {
948 	if (strchr(domain, '.') != NULL)
949 		(void) printf("\t\t%s@%s\n", name, domain);
950 	else
951 		(void) printf("\t\t%s\\%s\n", domain, name);
952 }
953 
954 /*
955  * smbadm_group_dump_privs
956  *
957  * Dump group privilege details.
958  */
959 static void
960 smbadm_group_dump_privs(smb_privset_t *privs)
961 {
962 	smb_privinfo_t *pinfo;
963 	char *pstatus;
964 	int i;
965 
966 	(void) printf(gettext("\tPrivileges: \n"));
967 
968 	for (i = 0; i < privs->priv_cnt; i++) {
969 		pinfo = smb_priv_getbyvalue(privs->priv[i].luid.lo_part);
970 		if ((pinfo == NULL) || (pinfo->flags & PF_PRESENTABLE) == 0)
971 			continue;
972 
973 		switch (privs->priv[i].attrs) {
974 		case SE_PRIVILEGE_ENABLED:
975 			pstatus = "On";
976 			break;
977 		case SE_PRIVILEGE_DISABLED:
978 			pstatus = "Off";
979 			break;
980 		default:
981 			pstatus = "Unknown";
982 			break;
983 		}
984 		(void) printf(gettext("\t\t%s: %s\n"), pinfo->name, pstatus);
985 	}
986 
987 	if (privs->priv_cnt == 0)
988 		(void) printf(gettext("\t\tNo privileges\n"));
989 }
990 
991 /*
992  * smbadm_group_dump
993  *
994  * Dump group details.
995  */
996 static void
997 smbadm_group_dump(smb_group_t *grp, boolean_t show_mem, boolean_t show_privs)
998 {
999 	char sidstr[SMB_SID_STRSZ];
1000 
1001 	(void) printf(gettext("%s (%s)\n"), grp->sg_name, grp->sg_cmnt);
1002 
1003 	smb_sid_tostr(grp->sg_id.gs_sid, sidstr);
1004 	(void) printf(gettext("\tSID: %s\n"), sidstr);
1005 
1006 	if (show_privs)
1007 		smbadm_group_dump_privs(grp->sg_privs);
1008 
1009 	if (show_mem)
1010 		smbadm_group_dump_members(grp->sg_members, grp->sg_nmembers);
1011 }
1012 
1013 /*
1014  * smbadm_group_show
1015  *
1016  */
1017 static int
1018 smbadm_group_show(int argc, char **argv)
1019 {
1020 	char *gname = NULL;
1021 	boolean_t show_privs;
1022 	boolean_t show_members;
1023 	char option;
1024 	int status;
1025 	smb_group_t grp;
1026 	smb_giter_t gi;
1027 
1028 	show_privs = show_members = B_FALSE;
1029 
1030 	while ((option = getopt(argc, argv, "mp")) != -1) {
1031 		switch (option) {
1032 		case 'm':
1033 			show_members = B_TRUE;
1034 			break;
1035 		case 'p':
1036 			show_privs = B_TRUE;
1037 			break;
1038 
1039 		default:
1040 			smbadm_usage(B_FALSE);
1041 		}
1042 	}
1043 
1044 	gname = argv[optind];
1045 	if (optind >= argc || gname == NULL || *gname == '\0')
1046 		gname = "*";
1047 
1048 	if (strcmp(gname, "*")) {
1049 		status = smb_lgrp_getbyname(gname, &grp);
1050 		if (status == SMB_LGRP_SUCCESS) {
1051 			smbadm_group_dump(&grp, show_members, show_privs);
1052 			smb_lgrp_free(&grp);
1053 		} else {
1054 			(void) fprintf(stderr,
1055 			    gettext("failed to find %s (%s)\n"),
1056 			    gname, smb_lgrp_strerror(status));
1057 		}
1058 		return (status);
1059 	}
1060 
1061 	if ((status = smb_lgrp_iteropen(&gi)) != SMB_LGRP_SUCCESS) {
1062 		(void) fprintf(stderr, gettext("failed to list groups (%s)\n"),
1063 		    smb_lgrp_strerror(status));
1064 		return (status);
1065 	}
1066 
1067 	while ((status = smb_lgrp_iterate(&gi, &grp)) == SMB_LGRP_SUCCESS) {
1068 		smbadm_group_dump(&grp, show_members, show_privs);
1069 		smb_lgrp_free(&grp);
1070 	}
1071 
1072 	smb_lgrp_iterclose(&gi);
1073 
1074 	if ((status != SMB_LGRP_NO_MORE) || smb_lgrp_itererror(&gi)) {
1075 		if (status != SMB_LGRP_NO_MORE)
1076 			smb_syslog(LOG_ERR, "smb_lgrp_iterate: %s",
1077 			    smb_lgrp_strerror(status));
1078 
1079 		(void) fprintf(stderr,
1080 		    gettext("\nAn error occurred while retrieving group data.\n"
1081 		    "Check the system log for more information.\n"));
1082 		return (status);
1083 	}
1084 
1085 	return (0);
1086 }
1087 
1088 /*
1089  * smbadm_group_delete
1090  */
1091 static int
1092 smbadm_group_delete(int argc, char **argv)
1093 {
1094 	char *gname = NULL;
1095 	int status;
1096 
1097 	gname = argv[optind];
1098 	if (optind >= argc || gname == NULL || *gname == '\0') {
1099 		(void) fprintf(stderr, gettext("missing group name\n"));
1100 		smbadm_usage(B_FALSE);
1101 	}
1102 
1103 	status = smb_lgrp_delete(gname);
1104 	if (status != SMB_LGRP_SUCCESS) {
1105 		(void) fprintf(stderr,
1106 		    gettext("failed to delete %s (%s)\n"), gname,
1107 		    smb_lgrp_strerror(status));
1108 	} else {
1109 		(void) printf(gettext("%s deleted\n"), gname);
1110 	}
1111 
1112 	return (status);
1113 }
1114 
1115 /*
1116  * smbadm_group_rename
1117  */
1118 static int
1119 smbadm_group_rename(int argc, char **argv)
1120 {
1121 	char *gname = NULL;
1122 	char *ngname = NULL;
1123 	int status;
1124 
1125 	gname = argv[optind];
1126 	if (optind++ >= argc || gname == NULL || *gname == '\0') {
1127 		(void) fprintf(stderr, gettext("missing group name\n"));
1128 		smbadm_usage(B_FALSE);
1129 	}
1130 
1131 	ngname = argv[optind];
1132 	if (optind >= argc || ngname == NULL || *ngname == '\0') {
1133 		(void) fprintf(stderr, gettext("missing new group name\n"));
1134 		smbadm_usage(B_FALSE);
1135 	}
1136 
1137 	status = smb_lgrp_rename(gname, ngname);
1138 	if (status != SMB_LGRP_SUCCESS) {
1139 		if (status == SMB_LGRP_EXISTS)
1140 			(void) fprintf(stderr,
1141 			    gettext("failed to rename '%s' (%s already "
1142 			    "exists)\n"), gname, ngname);
1143 		else
1144 			(void) fprintf(stderr,
1145 			    gettext("failed to rename '%s' (%s)\n"), gname,
1146 			    smb_lgrp_strerror(status));
1147 	} else {
1148 		(void) printf(gettext("'%s' renamed to '%s'\n"), gname, ngname);
1149 	}
1150 
1151 	return (status);
1152 }
1153 
1154 /*
1155  * smbadm_group_setprop
1156  *
1157  * Set the group properties.
1158  */
1159 static int
1160 smbadm_group_setprop(int argc, char **argv)
1161 {
1162 	char *gname = NULL;
1163 	smbadm_prop_t props[SMBADM_NPROP];
1164 	smbadm_prop_handle_t *phandle;
1165 	char option;
1166 	int pcnt = 0;
1167 	int ret;
1168 	int p;
1169 
1170 	bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t));
1171 
1172 	while ((option = getopt(argc, argv, "p:")) != -1) {
1173 		switch (option) {
1174 		case 'p':
1175 			if (pcnt >= SMBADM_NPROP) {
1176 				(void) fprintf(stderr,
1177 				    gettext("exceeded number of supported"
1178 				    " properties\n"));
1179 				smbadm_usage(B_FALSE);
1180 			}
1181 
1182 			if (smbadm_prop_parse(optarg, &props[pcnt++]) != 0)
1183 				smbadm_usage(B_FALSE);
1184 			break;
1185 
1186 		default:
1187 			smbadm_usage(B_FALSE);
1188 		}
1189 	}
1190 
1191 	if (pcnt == 0) {
1192 		(void) fprintf(stderr,
1193 		    gettext("missing property=value argument\n"));
1194 		smbadm_usage(B_FALSE);
1195 	}
1196 
1197 	gname = argv[optind];
1198 	if (optind >= argc || gname == NULL || *gname == '\0') {
1199 		(void) fprintf(stderr, gettext("missing group name\n"));
1200 		smbadm_usage(B_FALSE);
1201 	}
1202 
1203 	for (p = 0; p < pcnt; p++) {
1204 		phandle = smbadm_prop_gethandle(props[p].p_name);
1205 		if (phandle) {
1206 			if (phandle->p_setfn(gname, &props[p]) != 0)
1207 				ret = 1;
1208 		}
1209 	}
1210 
1211 	return (ret);
1212 }
1213 
1214 /*
1215  * smbadm_group_getprop
1216  *
1217  * Get the group properties.
1218  */
1219 static int
1220 smbadm_group_getprop(int argc, char **argv)
1221 {
1222 	char *gname = NULL;
1223 	smbadm_prop_t props[SMBADM_NPROP];
1224 	smbadm_prop_handle_t *phandle;
1225 	char option;
1226 	int pcnt = 0;
1227 	int ret;
1228 	int p;
1229 
1230 	bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t));
1231 
1232 	while ((option = getopt(argc, argv, "p:")) != -1) {
1233 		switch (option) {
1234 		case 'p':
1235 			if (pcnt >= SMBADM_NPROP) {
1236 				(void) fprintf(stderr,
1237 				    gettext("exceeded number of supported"
1238 				    " properties\n"));
1239 				smbadm_usage(B_FALSE);
1240 			}
1241 
1242 			if (smbadm_prop_parse(optarg, &props[pcnt++]) != 0)
1243 				smbadm_usage(B_FALSE);
1244 			break;
1245 
1246 		default:
1247 			smbadm_usage(B_FALSE);
1248 		}
1249 	}
1250 
1251 	gname = argv[optind];
1252 	if (optind >= argc || gname == NULL || *gname == '\0') {
1253 		(void) fprintf(stderr, gettext("missing group name\n"));
1254 		smbadm_usage(B_FALSE);
1255 	}
1256 
1257 	if (pcnt == 0) {
1258 		/*
1259 		 * If no property has be specified then get
1260 		 * all the properties.
1261 		 */
1262 		pcnt = SMBADM_NPROP;
1263 		for (p = 0; p < pcnt; p++)
1264 			props[p].p_name = smbadm_ptable[p].p_name;
1265 	}
1266 
1267 	for (p = 0; p < pcnt; p++) {
1268 		phandle = smbadm_prop_gethandle(props[p].p_name);
1269 		if (phandle) {
1270 			if (phandle->p_getfn(gname, &props[p]) != 0)
1271 				ret = 1;
1272 		}
1273 	}
1274 
1275 	return (ret);
1276 }
1277 
1278 /*
1279  * smbadm_group_addmember
1280  *
1281  */
1282 static int
1283 smbadm_group_addmember(int argc, char **argv)
1284 {
1285 	char *gname = NULL;
1286 	char **mname;
1287 	char option;
1288 	int mcnt = 0;
1289 	int ret = 0;
1290 	int i;
1291 
1292 
1293 	mname = (char **)malloc(argc * sizeof (char *));
1294 	if (mname == NULL) {
1295 		warn(gettext("failed to add group member"));
1296 		return (1);
1297 	}
1298 	bzero(mname, argc * sizeof (char *));
1299 
1300 	while ((option = getopt(argc, argv, "m:")) != -1) {
1301 		switch (option) {
1302 		case 'm':
1303 			mname[mcnt++] = optarg;
1304 			break;
1305 
1306 		default:
1307 			free(mname);
1308 			smbadm_usage(B_FALSE);
1309 		}
1310 	}
1311 
1312 	if (mcnt == 0) {
1313 		(void) fprintf(stderr, gettext("missing member name\n"));
1314 		free(mname);
1315 		smbadm_usage(B_FALSE);
1316 	}
1317 
1318 	gname = argv[optind];
1319 	if (optind >= argc || gname == NULL || *gname == 0) {
1320 		(void) fprintf(stderr, gettext("missing group name\n"));
1321 		free(mname);
1322 		smbadm_usage(B_FALSE);
1323 	}
1324 
1325 	for (i = 0; i < mcnt; i++) {
1326 		if (mname[i] == NULL)
1327 			continue;
1328 		ret |= smbadm_group_add_del_member(
1329 		    gname, mname[i], SMBADM_GRP_ADDMEMBER);
1330 	}
1331 
1332 	free(mname);
1333 	return (ret);
1334 }
1335 
1336 /*
1337  * smbadm_group_delmember
1338  */
1339 static int
1340 smbadm_group_delmember(int argc, char **argv)
1341 {
1342 	char *gname = NULL;
1343 	char **mname;
1344 	char option;
1345 	int mcnt = 0;
1346 	int ret = 0;
1347 	int i;
1348 
1349 	mname = (char **)malloc(argc * sizeof (char *));
1350 	if (mname == NULL) {
1351 		warn(gettext("failed to delete group member"));
1352 		return (1);
1353 	}
1354 	bzero(mname, argc * sizeof (char *));
1355 
1356 	while ((option = getopt(argc, argv, "m:")) != -1) {
1357 		switch (option) {
1358 		case 'm':
1359 			mname[mcnt++] = optarg;
1360 			break;
1361 
1362 		default:
1363 			free(mname);
1364 			smbadm_usage(B_FALSE);
1365 		}
1366 	}
1367 
1368 	if (mcnt == 0) {
1369 		(void) fprintf(stderr, gettext("missing member name\n"));
1370 		free(mname);
1371 		smbadm_usage(B_FALSE);
1372 	}
1373 
1374 	gname = argv[optind];
1375 	if (optind >= argc || gname == NULL || *gname == 0) {
1376 		(void) fprintf(stderr, gettext("missing group name\n"));
1377 		free(mname);
1378 		smbadm_usage(B_FALSE);
1379 	}
1380 
1381 
1382 	for (i = 0; i < mcnt; i++) {
1383 		ret = 0;
1384 		if (mname[i] == NULL)
1385 			continue;
1386 		ret |= smbadm_group_add_del_member(
1387 		    gname, mname[i], SMBADM_GRP_DELMEMBER);
1388 	}
1389 
1390 	free(mname);
1391 	return (ret);
1392 }
1393 
1394 static int
1395 smbadm_group_add_del_member(char *gname, char *mname,
1396     smbadm_grp_action_t act)
1397 {
1398 	lsa_account_t	acct;
1399 	smb_gsid_t msid;
1400 	char *sidstr;
1401 	char *act_str;
1402 	int rc;
1403 
1404 	if (strncmp(mname, "S-1-", 4) == 0) {
1405 		/*
1406 		 * We are given a SID.  Just use it.
1407 		 *
1408 		 * We'd like the real account type if we can get it,
1409 		 * but don't want to error out if we can't get it.
1410 		 * Lacking other info, assume it's a group.
1411 		 */
1412 		sidstr = mname;
1413 		rc = smb_lookup_sid(sidstr, &acct);
1414 		if ((rc != 0) || (acct.a_status != NT_STATUS_SUCCESS))
1415 			acct.a_sidtype = SidTypeGroup;
1416 	} else {
1417 		rc = smb_lookup_name(mname, SidTypeUnknown, &acct);
1418 		if ((rc != 0) || (acct.a_status != NT_STATUS_SUCCESS)) {
1419 			(void) fprintf(stderr,
1420 			    gettext("%s: name lookup failed\n"), mname);
1421 			return (1);
1422 		}
1423 		sidstr = acct.a_sid;
1424 	}
1425 
1426 	msid.gs_type = acct.a_sidtype;
1427 	if ((msid.gs_sid = smb_sid_fromstr(sidstr)) == NULL) {
1428 		(void) fprintf(stderr,
1429 		    gettext("%s: no memory for SID\n"), sidstr);
1430 		return (1);
1431 	}
1432 
1433 	switch (act) {
1434 	case SMBADM_GRP_ADDMEMBER:
1435 		act_str = gettext("add");
1436 		rc = smb_lgrp_add_member(gname,
1437 		    msid.gs_sid, msid.gs_type);
1438 		break;
1439 	case SMBADM_GRP_DELMEMBER:
1440 		act_str = gettext("remove");
1441 		rc = smb_lgrp_del_member(gname,
1442 		    msid.gs_sid, msid.gs_type);
1443 		break;
1444 	default:
1445 		rc = SMB_LGRP_INTERNAL_ERROR;
1446 		break;
1447 	}
1448 
1449 	smb_sid_free(msid.gs_sid);
1450 
1451 	if (rc != SMB_LGRP_SUCCESS) {
1452 		(void) fprintf(stderr,
1453 		    gettext("failed to %s %s (%s)\n"),
1454 		    act_str, mname, smb_lgrp_strerror(rc));
1455 		return (1);
1456 	}
1457 	return (0);
1458 }
1459 
1460 static int
1461 smbadm_user_delete(int argc, char **argv)
1462 {
1463 	int error;
1464 	char *user = NULL;
1465 
1466 	user = argv[optind];
1467 	if (optind >= argc || user == NULL || *user == '\0') {
1468 		(void) fprintf(stderr, gettext("missing user name\n"));
1469 		smbadm_usage(B_FALSE);
1470 	}
1471 
1472 	error = smb_pwd_setcntl(user, SMB_PWC_DELETE);
1473 	if (error == SMB_PWE_SUCCESS)
1474 		(void) printf(gettext("%s has been deleted.\n"), user);
1475 	else
1476 		(void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error));
1477 
1478 	return (error);
1479 }
1480 
1481 static int
1482 smbadm_user_disable(int argc, char **argv)
1483 {
1484 	int error;
1485 	char *user = NULL;
1486 
1487 	user = argv[optind];
1488 	if (optind >= argc || user == NULL || *user == '\0') {
1489 		(void) fprintf(stderr, gettext("missing user name\n"));
1490 		smbadm_usage(B_FALSE);
1491 	}
1492 
1493 	error = smb_pwd_setcntl(user, SMB_PWC_DISABLE);
1494 	if (error == SMB_PWE_SUCCESS)
1495 		(void) printf(gettext("%s is disabled.\n"), user);
1496 	else
1497 		(void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error));
1498 
1499 	return (error);
1500 }
1501 
1502 static int
1503 smbadm_user_enable(int argc, char **argv)
1504 {
1505 	int error;
1506 	char *user = NULL;
1507 
1508 	user = argv[optind];
1509 	if (optind >= argc || user == NULL || *user == '\0') {
1510 		(void) fprintf(stderr, gettext("missing user name\n"));
1511 		smbadm_usage(B_FALSE);
1512 	}
1513 
1514 	error = smb_pwd_setcntl(user, SMB_PWC_ENABLE);
1515 	if (error == SMB_PWE_SUCCESS)
1516 		(void) printf(gettext("%s is enabled.\n"), user);
1517 	else
1518 		(void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error));
1519 
1520 	return (error);
1521 }
1522 
1523 
1524 int
1525 main(int argc, char **argv)
1526 {
1527 	int ret;
1528 	int i;
1529 
1530 	(void) setlocale(LC_ALL, "");
1531 	(void) textdomain(TEXT_DOMAIN);
1532 
1533 	(void) malloc(0);	/* satisfy libumem dependency */
1534 
1535 	progname = basename(argv[0]);
1536 
1537 	if (is_system_labeled()) {
1538 		(void) fprintf(stderr,
1539 		    gettext("Trusted Extensions not supported\n"));
1540 		return (1);
1541 	}
1542 
1543 	if (argc < 2) {
1544 		(void) fprintf(stderr, gettext("missing command\n"));
1545 		smbadm_usage(B_FALSE);
1546 	}
1547 
1548 	/*
1549 	 * Special case "cmd --help/-?"
1550 	 */
1551 	if (strcmp(argv[1], "-?") == 0 ||
1552 	    strcmp(argv[1], "--help") == 0 ||
1553 	    strcmp(argv[1], "-h") == 0)
1554 		smbadm_usage(B_TRUE);
1555 
1556 	for (i = 0; i < SMBADM_NCMD; ++i) {
1557 		curcmd = &smbadm_cmdtable[i];
1558 		if (strcasecmp(argv[1], curcmd->name) == 0) {
1559 			if (argc > 2) {
1560 				/* cmd subcmd --help/-? */
1561 				if (strcmp(argv[2], "-?") == 0 ||
1562 				    strcmp(argv[2], "--help") == 0 ||
1563 				    strcmp(argv[2], "-h") == 0)
1564 					smbadm_usage(B_TRUE);
1565 			}
1566 
1567 			if (!smbadm_checkauth(curcmd->auth)) {
1568 				(void) fprintf(stderr,
1569 				    gettext("%s: %s: authorization denied\n"),
1570 				    progname, curcmd->name);
1571 				return (1);
1572 			}
1573 
1574 			if ((ret = smbadm_init()) != 0)
1575 				return (ret);
1576 
1577 			ret = curcmd->func(argc - 1, &argv[1]);
1578 
1579 			smbadm_fini();
1580 			return (ret);
1581 		}
1582 	}
1583 
1584 	curcmd = NULL;
1585 	(void) fprintf(stderr, gettext("unknown subcommand (%s)\n"), argv[1]);
1586 	smbadm_usage(B_FALSE);
1587 	return (2);
1588 }
1589 
1590 static int
1591 smbadm_init(void)
1592 {
1593 	int rc;
1594 
1595 	switch (curcmd->flags & SMBADM_CMDF_TYPEMASK) {
1596 	case SMBADM_CMDF_GROUP:
1597 		if ((rc = smb_lgrp_start()) != SMB_LGRP_SUCCESS) {
1598 			(void) fprintf(stderr,
1599 			    gettext("failed to initialize (%s)\n"),
1600 			    smb_lgrp_strerror(rc));
1601 			return (1);
1602 		}
1603 		break;
1604 
1605 	case SMBADM_CMDF_USER:
1606 		smb_pwd_init(B_FALSE);
1607 		break;
1608 
1609 	default:
1610 		break;
1611 	}
1612 
1613 	return (0);
1614 }
1615 
1616 static void
1617 smbadm_fini(void)
1618 {
1619 	switch (curcmd->flags & SMBADM_CMDF_TYPEMASK) {
1620 	case SMBADM_CMDF_GROUP:
1621 		smb_lgrp_stop();
1622 		break;
1623 
1624 	case SMBADM_CMDF_USER:
1625 		smb_pwd_fini();
1626 		break;
1627 
1628 	default:
1629 		break;
1630 	}
1631 }
1632 
1633 static boolean_t
1634 smbadm_checkauth(const char *auth)
1635 {
1636 	struct passwd *pw;
1637 
1638 	if ((pw = getpwuid(getuid())) == NULL)
1639 		return (B_FALSE);
1640 
1641 	if (chkauthattr(auth, pw->pw_name) == 0)
1642 		return (B_FALSE);
1643 
1644 	return (B_TRUE);
1645 }
1646 
1647 static boolean_t
1648 smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval)
1649 {
1650 	smbadm_prop_handle_t *pinfo;
1651 	int i;
1652 
1653 	for (i = 0; i < SMBADM_NPROP; i++) {
1654 		pinfo = &smbadm_ptable[i];
1655 		if (strcmp(pinfo->p_name, prop->p_name) == 0) {
1656 			if (pinfo->p_chkfn && chkval)
1657 				return (pinfo->p_chkfn(prop));
1658 
1659 			return (B_TRUE);
1660 		}
1661 	}
1662 
1663 	(void) fprintf(stderr, gettext("unrecognized property '%s'\n"),
1664 	    prop->p_name);
1665 
1666 	return (B_FALSE);
1667 }
1668 
1669 static int
1670 smbadm_prop_parse(char *arg, smbadm_prop_t *prop)
1671 {
1672 	boolean_t parse_value;
1673 	char *equal;
1674 
1675 	if (arg == NULL)
1676 		return (2);
1677 
1678 	prop->p_name = prop->p_value = NULL;
1679 
1680 	if (strcmp(curcmd->name, "set") == 0)
1681 		parse_value = B_TRUE;
1682 	else
1683 		parse_value = B_FALSE;
1684 
1685 	prop->p_name = arg;
1686 
1687 	if (parse_value) {
1688 		equal = strchr(arg, '=');
1689 		if (equal == NULL)
1690 			return (2);
1691 
1692 		*equal++ = '\0';
1693 		prop->p_value = equal;
1694 	}
1695 
1696 	if (smbadm_prop_validate(prop, parse_value) == B_FALSE)
1697 		return (2);
1698 
1699 	return (0);
1700 }
1701 
1702 static smbadm_prop_handle_t *
1703 smbadm_prop_gethandle(char *pname)
1704 {
1705 	int i;
1706 
1707 	for (i = 0; i < SMBADM_NPROP; i++)
1708 		if (strcmp(pname, smbadm_ptable[i].p_name) == 0)
1709 			return (&smbadm_ptable[i]);
1710 
1711 	return (NULL);
1712 }
1713 
1714 static int
1715 smbadm_setprop_desc(char *gname, smbadm_prop_t *prop)
1716 {
1717 	int status;
1718 
1719 	status = smb_lgrp_setcmnt(gname, prop->p_value);
1720 	if (status != SMB_LGRP_SUCCESS) {
1721 		(void) fprintf(stderr,
1722 		    gettext("failed to modify the group description (%s)\n"),
1723 		    smb_lgrp_strerror(status));
1724 		return (1);
1725 	}
1726 
1727 	(void) printf(gettext("%s: description modified\n"), gname);
1728 	return (0);
1729 }
1730 
1731 static int
1732 smbadm_getprop_desc(char *gname, smbadm_prop_t *prop)
1733 {
1734 	char *cmnt = NULL;
1735 	int status;
1736 
1737 	status = smb_lgrp_getcmnt(gname, &cmnt);
1738 	if (status != SMB_LGRP_SUCCESS) {
1739 		(void) fprintf(stderr,
1740 		    gettext("failed to get the group description (%s)\n"),
1741 		    smb_lgrp_strerror(status));
1742 		return (1);
1743 	}
1744 
1745 	(void) printf(gettext("\t%s: %s\n"), prop->p_name, cmnt);
1746 	free(cmnt);
1747 	return (0);
1748 }
1749 
1750 static int
1751 smbadm_group_setpriv(char *gname, uint8_t priv_id, smbadm_prop_t *prop)
1752 {
1753 	boolean_t enable;
1754 	int status;
1755 	int ret;
1756 
1757 	if (strcasecmp(prop->p_value, "on") == 0) {
1758 		(void) printf(gettext("Enabling %s privilege "), prop->p_name);
1759 		enable = B_TRUE;
1760 	} else {
1761 		(void) printf(gettext("Disabling %s privilege "), prop->p_name);
1762 		enable = B_FALSE;
1763 	}
1764 
1765 	status = smb_lgrp_setpriv(gname, priv_id, enable);
1766 	if (status == SMB_LGRP_SUCCESS) {
1767 		(void) printf(gettext("succeeded\n"));
1768 		ret = 0;
1769 	} else {
1770 		(void) printf(gettext("failed: %s\n"),
1771 		    smb_lgrp_strerror(status));
1772 		ret = 1;
1773 	}
1774 
1775 	return (ret);
1776 }
1777 
1778 static int
1779 smbadm_group_getpriv(char *gname, uint8_t priv_id, smbadm_prop_t *prop)
1780 {
1781 	boolean_t enable;
1782 	int status;
1783 
1784 	status = smb_lgrp_getpriv(gname, priv_id, &enable);
1785 	if (status != SMB_LGRP_SUCCESS) {
1786 		(void) fprintf(stderr, gettext("failed to get %s (%s)\n"),
1787 		    prop->p_name, smb_lgrp_strerror(status));
1788 		return (1);
1789 	}
1790 
1791 	(void) printf(gettext("\t%s: %s\n"), prop->p_name,
1792 	    (enable) ? "On" : "Off");
1793 
1794 	return (0);
1795 }
1796 
1797 static int
1798 smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop)
1799 {
1800 	return (smbadm_group_setpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop));
1801 }
1802 
1803 static int
1804 smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop)
1805 {
1806 	return (smbadm_group_getpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop));
1807 }
1808 
1809 static int
1810 smbadm_setprop_backup(char *gname, smbadm_prop_t *prop)
1811 {
1812 	return (smbadm_group_setpriv(gname, SE_BACKUP_LUID, prop));
1813 }
1814 
1815 static int
1816 smbadm_getprop_backup(char *gname, smbadm_prop_t *prop)
1817 {
1818 	return (smbadm_group_getpriv(gname, SE_BACKUP_LUID, prop));
1819 }
1820 
1821 static int
1822 smbadm_setprop_restore(char *gname, smbadm_prop_t *prop)
1823 {
1824 	return (smbadm_group_setpriv(gname, SE_RESTORE_LUID, prop));
1825 }
1826 
1827 static int
1828 smbadm_getprop_restore(char *gname, smbadm_prop_t *prop)
1829 {
1830 	return (smbadm_group_getpriv(gname, SE_RESTORE_LUID, prop));
1831 }
1832 
1833 static boolean_t
1834 smbadm_chkprop_priv(smbadm_prop_t *prop)
1835 {
1836 	if (prop->p_value == NULL || *prop->p_value == '\0') {
1837 		(void) fprintf(stderr,
1838 		    gettext("missing value for '%s'\n"), prop->p_name);
1839 		return (B_FALSE);
1840 	}
1841 
1842 	if (strcasecmp(prop->p_value, "on") == 0)
1843 		return (B_TRUE);
1844 
1845 	if (strcasecmp(prop->p_value, "off") == 0)
1846 		return (B_TRUE);
1847 
1848 	(void) fprintf(stderr,
1849 	    gettext("%s: unrecognized value for '%s' property\n"),
1850 	    prop->p_value, prop->p_name);
1851 
1852 	return (B_FALSE);
1853 }
1854 
1855 static const char *
1856 smbadm_pwd_strerror(int error)
1857 {
1858 	switch (error) {
1859 	case SMB_PWE_SUCCESS:
1860 		return (gettext("Success."));
1861 
1862 	case SMB_PWE_USER_UNKNOWN:
1863 		return (gettext("User does not exist."));
1864 
1865 	case SMB_PWE_USER_DISABLE:
1866 		return (gettext("User is disabled."));
1867 
1868 	case SMB_PWE_CLOSE_FAILED:
1869 	case SMB_PWE_OPEN_FAILED:
1870 	case SMB_PWE_WRITE_FAILED:
1871 	case SMB_PWE_UPDATE_FAILED:
1872 		return (gettext("Unexpected failure. "
1873 		    "SMB password database unchanged."));
1874 
1875 	case SMB_PWE_STAT_FAILED:
1876 		return (gettext("stat of SMB password file failed."));
1877 
1878 	case SMB_PWE_BUSY:
1879 		return (gettext("SMB password database busy. "
1880 		    "Try again later."));
1881 
1882 	case SMB_PWE_DENIED:
1883 		return (gettext("Operation not permitted."));
1884 
1885 	case SMB_PWE_SYSTEM_ERROR:
1886 		return (gettext("System error."));
1887 
1888 	default:
1889 		break;
1890 	}
1891 
1892 	return (gettext("Unknown error code."));
1893 }
1894 
1895 /*
1896  * Enable libumem debugging by default on DEBUG builds.
1897  */
1898 #ifdef DEBUG
1899 const char *
1900 _umem_debug_init(void)
1901 {
1902 	return ("default,verbose"); /* $UMEM_DEBUG setting */
1903 }
1904 
1905 const char *
1906 _umem_logging_init(void)
1907 {
1908 	return ("fail,contents"); /* $UMEM_LOGGING setting */
1909 }
1910 #endif
1911