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