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