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