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