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