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