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