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 *); 121 static void smbadm_lookup_sid(char *); 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 <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 978 if (argc < 2) { 979 (void) fprintf(stderr, gettext("missing account name\n")); 980 smbadm_usage(B_FALSE); 981 } 982 983 for (i = 1; i < argc; i++) { 984 if (strncmp(argv[i], "S-1-", 4) == 0) 985 smbadm_lookup_sid(argv[i]); 986 else 987 smbadm_lookup_name(argv[i]); 988 } 989 return (0); 990 } 991 992 static void 993 smbadm_lookup_name(char *name) 994 { 995 lsa_account_t acct; 996 int rc; 997 998 if ((rc = smb_lookup_name(name, SidTypeUnknown, &acct)) != 0) { 999 (void) fprintf(stderr, gettext( 1000 "\t\t%s: lookup name failed, rc=%d\n"), 1001 name, rc); 1002 return; 1003 } 1004 if (acct.a_status != NT_STATUS_SUCCESS) { 1005 (void) fprintf(stderr, gettext("\t\t%s [%s]\n"), 1006 name, xlate_nt_status(acct.a_status)); 1007 return; 1008 } 1009 (void) printf("\t%s\n", acct.a_sid); 1010 } 1011 1012 static void 1013 smbadm_lookup_sid(char *sidstr) 1014 { 1015 lsa_account_t acct; 1016 int rc; 1017 1018 if ((rc = smb_lookup_sid(sidstr, &acct)) != 0) { 1019 (void) fprintf(stderr, gettext( 1020 "\t\t%s: lookup SID failed, rc=%d\n"), 1021 sidstr, rc); 1022 return; 1023 } 1024 if (acct.a_status != NT_STATUS_SUCCESS) { 1025 (void) fprintf(stderr, gettext("\t\t%s [%s]\n"), 1026 sidstr, xlate_nt_status(acct.a_status)); 1027 return; 1028 } 1029 (void) printf("\t%s\\%s\n", acct.a_domain, acct.a_name); 1030 } 1031 1032 /* 1033 * smbadm_group_create 1034 * 1035 * Creates a local SMB group 1036 */ 1037 static int 1038 smbadm_group_create(int argc, char **argv) 1039 { 1040 char *gname = NULL; 1041 char *desc = NULL; 1042 int option; 1043 int status; 1044 1045 while ((option = getopt(argc, argv, "d:")) != -1) { 1046 switch (option) { 1047 case 'd': 1048 desc = optarg; 1049 break; 1050 1051 default: 1052 smbadm_usage(B_FALSE); 1053 } 1054 } 1055 1056 gname = argv[optind]; 1057 if (optind >= argc || gname == NULL || *gname == '\0') { 1058 (void) fprintf(stderr, gettext("missing group name\n")); 1059 smbadm_usage(B_FALSE); 1060 } 1061 1062 status = smb_lgrp_add(gname, desc); 1063 if (status != SMB_LGRP_SUCCESS) { 1064 (void) fprintf(stderr, 1065 gettext("failed to create %s (%s)\n"), gname, 1066 smb_lgrp_strerror(status)); 1067 } else { 1068 (void) printf(gettext("%s created\n"), gname); 1069 } 1070 1071 return (status); 1072 } 1073 1074 /* 1075 * smbadm_group_dump_members 1076 * 1077 * Dump group members details. 1078 */ 1079 static void 1080 smbadm_group_dump_members(smb_gsid_t *members, int num, boolean_t show_sids) 1081 { 1082 char sidstr[SMB_SID_STRSZ]; 1083 lsa_account_t acct; 1084 int i; 1085 1086 if (num == 0) { 1087 (void) printf(gettext("\tNo members\n")); 1088 return; 1089 } 1090 1091 (void) printf(gettext("\tMembers:\n")); 1092 for (i = 0; i < num; i++) { 1093 smb_sid_tostr(members[i].gs_sid, sidstr); 1094 1095 if (!show_sids && 1096 smb_lookup_sid(sidstr, &acct) == 0) { 1097 if (acct.a_status == NT_STATUS_SUCCESS) 1098 smbadm_group_show_name(acct.a_domain, 1099 acct.a_name); 1100 else 1101 (void) printf(gettext("\t\t%s [%s]\n"), 1102 sidstr, xlate_nt_status(acct.a_status)); 1103 } else { 1104 (void) printf(gettext("\t\t%s\n"), sidstr); 1105 } 1106 } 1107 } 1108 1109 static void 1110 smbadm_group_show_name(const char *domain, const char *name) 1111 { 1112 if (strchr(domain, '.') != NULL) 1113 (void) printf("\t\t%s@%s\n", name, domain); 1114 else 1115 (void) printf("\t\t%s\\%s\n", domain, name); 1116 } 1117 1118 /* 1119 * smbadm_group_dump_privs 1120 * 1121 * Dump group privilege details. 1122 */ 1123 static void 1124 smbadm_group_dump_privs(smb_privset_t *privs) 1125 { 1126 smb_privinfo_t *pinfo; 1127 char *pstatus; 1128 int i; 1129 1130 (void) printf(gettext("\tPrivileges: \n")); 1131 1132 for (i = 0; i < privs->priv_cnt; i++) { 1133 pinfo = smb_priv_getbyvalue(privs->priv[i].luid.lo_part); 1134 if ((pinfo == NULL) || (pinfo->flags & PF_PRESENTABLE) == 0) 1135 continue; 1136 1137 switch (privs->priv[i].attrs) { 1138 case SE_PRIVILEGE_ENABLED: 1139 pstatus = "On"; 1140 break; 1141 case SE_PRIVILEGE_DISABLED: 1142 pstatus = "Off"; 1143 break; 1144 default: 1145 pstatus = "Unknown"; 1146 break; 1147 } 1148 (void) printf(gettext("\t\t%s: %s\n"), pinfo->name, pstatus); 1149 } 1150 1151 if (privs->priv_cnt == 0) 1152 (void) printf(gettext("\t\tNo privileges\n")); 1153 } 1154 1155 /* 1156 * smbadm_group_dump 1157 * 1158 * Dump group details. 1159 */ 1160 static void 1161 smbadm_group_dump(smb_group_t *grp, boolean_t show_mem, boolean_t show_privs, 1162 boolean_t show_sids) 1163 { 1164 char sidstr[SMB_SID_STRSZ]; 1165 1166 (void) printf(gettext("%s (%s)\n"), grp->sg_name, grp->sg_cmnt); 1167 1168 smb_sid_tostr(grp->sg_id.gs_sid, sidstr); 1169 (void) printf(gettext("\tSID: %s\n"), sidstr); 1170 1171 if (show_privs) 1172 smbadm_group_dump_privs(grp->sg_privs); 1173 1174 if (show_mem) 1175 smbadm_group_dump_members(grp->sg_members, grp->sg_nmembers, 1176 show_sids); 1177 } 1178 1179 /* 1180 * smbadm_group_show 1181 * 1182 */ 1183 static int 1184 smbadm_group_show(int argc, char **argv) 1185 { 1186 char *gname = NULL; 1187 boolean_t show_members = B_FALSE; 1188 boolean_t show_privs = B_FALSE; 1189 boolean_t show_sids = B_FALSE; 1190 int option; 1191 int status; 1192 smb_group_t grp; 1193 smb_giter_t gi; 1194 1195 while ((option = getopt(argc, argv, "mps")) != -1) { 1196 switch (option) { 1197 case 'm': 1198 show_members = B_TRUE; 1199 break; 1200 case 'p': 1201 show_privs = B_TRUE; 1202 break; 1203 case 's': 1204 show_sids = B_TRUE; 1205 break; 1206 1207 default: 1208 smbadm_usage(B_FALSE); 1209 } 1210 } 1211 1212 gname = argv[optind]; 1213 if (optind >= argc || gname == NULL || *gname == '\0') 1214 gname = "*"; 1215 1216 if (strcmp(gname, "*")) { 1217 status = smb_lgrp_getbyname(gname, &grp); 1218 if (status == SMB_LGRP_SUCCESS) { 1219 smbadm_group_dump(&grp, show_members, show_privs, 1220 show_sids); 1221 smb_lgrp_free(&grp); 1222 } else { 1223 (void) fprintf(stderr, 1224 gettext("failed to find %s (%s)\n"), 1225 gname, smb_lgrp_strerror(status)); 1226 } 1227 return (status); 1228 } 1229 1230 if ((status = smb_lgrp_iteropen(&gi)) != SMB_LGRP_SUCCESS) { 1231 (void) fprintf(stderr, gettext("failed to list groups (%s)\n"), 1232 smb_lgrp_strerror(status)); 1233 return (status); 1234 } 1235 1236 while ((status = smb_lgrp_iterate(&gi, &grp)) == SMB_LGRP_SUCCESS) { 1237 smbadm_group_dump(&grp, show_members, show_privs, show_sids); 1238 smb_lgrp_free(&grp); 1239 } 1240 1241 smb_lgrp_iterclose(&gi); 1242 1243 if ((status != SMB_LGRP_NO_MORE) || smb_lgrp_itererror(&gi)) { 1244 if (status != SMB_LGRP_NO_MORE) 1245 smb_syslog(LOG_ERR, "smb_lgrp_iterate: %s", 1246 smb_lgrp_strerror(status)); 1247 1248 (void) fprintf(stderr, 1249 gettext("\nAn error occurred while retrieving group data.\n" 1250 "Check the system log for more information.\n")); 1251 return (status); 1252 } 1253 1254 return (0); 1255 } 1256 1257 /* 1258 * smbadm_group_delete 1259 */ 1260 static int 1261 smbadm_group_delete(int argc, char **argv) 1262 { 1263 char *gname = NULL; 1264 int status; 1265 1266 gname = argv[optind]; 1267 if (optind >= argc || gname == NULL || *gname == '\0') { 1268 (void) fprintf(stderr, gettext("missing group name\n")); 1269 smbadm_usage(B_FALSE); 1270 } 1271 1272 status = smb_lgrp_delete(gname); 1273 if (status != SMB_LGRP_SUCCESS) { 1274 (void) fprintf(stderr, 1275 gettext("failed to delete %s (%s)\n"), gname, 1276 smb_lgrp_strerror(status)); 1277 } else { 1278 (void) printf(gettext("%s deleted\n"), gname); 1279 } 1280 1281 return (status); 1282 } 1283 1284 /* 1285 * smbadm_group_rename 1286 */ 1287 static int 1288 smbadm_group_rename(int argc, char **argv) 1289 { 1290 char *gname = NULL; 1291 char *ngname = NULL; 1292 int status; 1293 1294 gname = argv[optind]; 1295 if (optind++ >= argc || gname == NULL || *gname == '\0') { 1296 (void) fprintf(stderr, gettext("missing group name\n")); 1297 smbadm_usage(B_FALSE); 1298 } 1299 1300 ngname = argv[optind]; 1301 if (optind >= argc || ngname == NULL || *ngname == '\0') { 1302 (void) fprintf(stderr, gettext("missing new group name\n")); 1303 smbadm_usage(B_FALSE); 1304 } 1305 1306 status = smb_lgrp_rename(gname, ngname); 1307 if (status != SMB_LGRP_SUCCESS) { 1308 if (status == SMB_LGRP_EXISTS) 1309 (void) fprintf(stderr, 1310 gettext("failed to rename '%s' (%s already " 1311 "exists)\n"), gname, ngname); 1312 else 1313 (void) fprintf(stderr, 1314 gettext("failed to rename '%s' (%s)\n"), gname, 1315 smb_lgrp_strerror(status)); 1316 } else { 1317 (void) printf(gettext("'%s' renamed to '%s'\n"), gname, ngname); 1318 } 1319 1320 return (status); 1321 } 1322 1323 /* 1324 * smbadm_group_setprop 1325 * 1326 * Set the group properties. 1327 */ 1328 static int 1329 smbadm_group_setprop(int argc, char **argv) 1330 { 1331 char *gname = NULL; 1332 smbadm_prop_t props[SMBADM_NPROP]; 1333 smbadm_prop_handle_t *phandle; 1334 int option; 1335 int pcnt = 0; 1336 int ret = 0; 1337 int p; 1338 1339 bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t)); 1340 1341 while ((option = getopt(argc, argv, "p:")) != -1) { 1342 switch (option) { 1343 case 'p': 1344 if (pcnt >= SMBADM_NPROP) { 1345 (void) fprintf(stderr, 1346 gettext("exceeded number of supported" 1347 " properties\n")); 1348 smbadm_usage(B_FALSE); 1349 } 1350 1351 if (smbadm_prop_parse(optarg, &props[pcnt++]) != 0) 1352 smbadm_usage(B_FALSE); 1353 break; 1354 1355 default: 1356 smbadm_usage(B_FALSE); 1357 } 1358 } 1359 1360 if (pcnt == 0) { 1361 (void) fprintf(stderr, 1362 gettext("missing property=value argument\n")); 1363 smbadm_usage(B_FALSE); 1364 } 1365 1366 gname = argv[optind]; 1367 if (optind >= argc || gname == NULL || *gname == '\0') { 1368 (void) fprintf(stderr, gettext("missing group name\n")); 1369 smbadm_usage(B_FALSE); 1370 } 1371 1372 for (p = 0; p < pcnt; p++) { 1373 phandle = smbadm_prop_gethandle(props[p].p_name); 1374 if (phandle) { 1375 if (phandle->p_setfn(gname, &props[p]) != 0) 1376 ret = 1; 1377 } 1378 } 1379 1380 return (ret); 1381 } 1382 1383 /* 1384 * smbadm_group_getprop 1385 * 1386 * Get the group properties. 1387 */ 1388 static int 1389 smbadm_group_getprop(int argc, char **argv) 1390 { 1391 char *gname = NULL; 1392 smbadm_prop_t props[SMBADM_NPROP]; 1393 smbadm_prop_handle_t *phandle; 1394 int option; 1395 int pcnt = 0; 1396 int ret = 0; 1397 int p; 1398 1399 bzero(props, SMBADM_NPROP * sizeof (smbadm_prop_t)); 1400 1401 while ((option = getopt(argc, argv, "p:")) != -1) { 1402 switch (option) { 1403 case 'p': 1404 if (pcnt >= SMBADM_NPROP) { 1405 (void) fprintf(stderr, 1406 gettext("exceeded number of supported" 1407 " properties\n")); 1408 smbadm_usage(B_FALSE); 1409 } 1410 1411 if (smbadm_prop_parse(optarg, &props[pcnt++]) != 0) 1412 smbadm_usage(B_FALSE); 1413 break; 1414 1415 default: 1416 smbadm_usage(B_FALSE); 1417 } 1418 } 1419 1420 gname = argv[optind]; 1421 if (optind >= argc || gname == NULL || *gname == '\0') { 1422 (void) fprintf(stderr, gettext("missing group name\n")); 1423 smbadm_usage(B_FALSE); 1424 } 1425 1426 if (pcnt == 0) { 1427 /* 1428 * If no property has be specified then get 1429 * all the properties. 1430 */ 1431 pcnt = SMBADM_NPROP; 1432 for (p = 0; p < pcnt; p++) 1433 props[p].p_name = smbadm_ptable[p].p_name; 1434 } 1435 1436 for (p = 0; p < pcnt; p++) { 1437 phandle = smbadm_prop_gethandle(props[p].p_name); 1438 if (phandle) { 1439 if (phandle->p_getfn(gname, &props[p]) != 0) 1440 ret = 1; 1441 } 1442 } 1443 1444 return (ret); 1445 } 1446 1447 /* 1448 * smbadm_group_addmember 1449 * 1450 */ 1451 static int 1452 smbadm_group_addmember(int argc, char **argv) 1453 { 1454 char *gname = NULL; 1455 char **mname; 1456 int option; 1457 int mcnt = 0; 1458 int ret = 0; 1459 int i; 1460 1461 1462 mname = (char **)malloc(argc * sizeof (char *)); 1463 if (mname == NULL) { 1464 warn(gettext("failed to add group member")); 1465 return (1); 1466 } 1467 bzero(mname, argc * sizeof (char *)); 1468 1469 while ((option = getopt(argc, argv, "m:")) != -1) { 1470 switch (option) { 1471 case 'm': 1472 mname[mcnt++] = optarg; 1473 break; 1474 1475 default: 1476 free(mname); 1477 smbadm_usage(B_FALSE); 1478 } 1479 } 1480 1481 if (mcnt == 0) { 1482 (void) fprintf(stderr, gettext("missing member name\n")); 1483 free(mname); 1484 smbadm_usage(B_FALSE); 1485 } 1486 1487 gname = argv[optind]; 1488 if (optind >= argc || gname == NULL || *gname == 0) { 1489 (void) fprintf(stderr, gettext("missing group name\n")); 1490 free(mname); 1491 smbadm_usage(B_FALSE); 1492 } 1493 1494 for (i = 0; i < mcnt; i++) { 1495 if (mname[i] == NULL) 1496 continue; 1497 ret |= smbadm_group_add_del_member( 1498 gname, mname[i], SMBADM_GRP_ADDMEMBER); 1499 } 1500 1501 free(mname); 1502 return (ret); 1503 } 1504 1505 /* 1506 * smbadm_group_delmember 1507 */ 1508 static int 1509 smbadm_group_delmember(int argc, char **argv) 1510 { 1511 char *gname = NULL; 1512 char **mname; 1513 int option; 1514 int mcnt = 0; 1515 int ret = 0; 1516 int i; 1517 1518 mname = (char **)malloc(argc * sizeof (char *)); 1519 if (mname == NULL) { 1520 warn(gettext("failed to delete group member")); 1521 return (1); 1522 } 1523 bzero(mname, argc * sizeof (char *)); 1524 1525 while ((option = getopt(argc, argv, "m:")) != -1) { 1526 switch (option) { 1527 case 'm': 1528 mname[mcnt++] = optarg; 1529 break; 1530 1531 default: 1532 free(mname); 1533 smbadm_usage(B_FALSE); 1534 } 1535 } 1536 1537 if (mcnt == 0) { 1538 (void) fprintf(stderr, gettext("missing member name\n")); 1539 free(mname); 1540 smbadm_usage(B_FALSE); 1541 } 1542 1543 gname = argv[optind]; 1544 if (optind >= argc || gname == NULL || *gname == 0) { 1545 (void) fprintf(stderr, gettext("missing group name\n")); 1546 free(mname); 1547 smbadm_usage(B_FALSE); 1548 } 1549 1550 1551 for (i = 0; i < mcnt; i++) { 1552 ret = 0; 1553 if (mname[i] == NULL) 1554 continue; 1555 ret |= smbadm_group_add_del_member( 1556 gname, mname[i], SMBADM_GRP_DELMEMBER); 1557 } 1558 1559 free(mname); 1560 return (ret); 1561 } 1562 1563 static int 1564 smbadm_group_add_del_member(char *gname, char *mname, 1565 smbadm_grp_action_t act) 1566 { 1567 lsa_account_t acct; 1568 smb_gsid_t msid; 1569 char *sidstr; 1570 char *act_str = NULL; 1571 int rc; 1572 1573 if (strncmp(mname, "S-1-", 4) == 0) { 1574 /* 1575 * We are given a SID. Just use it. 1576 * 1577 * We'd like the real account type if we can get it, 1578 * but don't want to error out if we can't get it. 1579 * Lacking other info, assume it's a group. 1580 */ 1581 sidstr = mname; 1582 rc = smb_lookup_sid(sidstr, &acct); 1583 if ((rc != 0) || (acct.a_status != NT_STATUS_SUCCESS)) 1584 acct.a_sidtype = SidTypeGroup; 1585 } else { 1586 rc = smb_lookup_name(mname, SidTypeUnknown, &acct); 1587 if ((rc != 0) || (acct.a_status != NT_STATUS_SUCCESS)) { 1588 (void) fprintf(stderr, 1589 gettext("%s: name lookup failed\n"), mname); 1590 return (1); 1591 } 1592 sidstr = acct.a_sid; 1593 } 1594 1595 msid.gs_type = acct.a_sidtype; 1596 if ((msid.gs_sid = smb_sid_fromstr(sidstr)) == NULL) { 1597 (void) fprintf(stderr, 1598 gettext("%s: no memory for SID\n"), sidstr); 1599 return (1); 1600 } 1601 1602 switch (act) { 1603 case SMBADM_GRP_ADDMEMBER: 1604 act_str = gettext("add"); 1605 rc = smb_lgrp_add_member(gname, 1606 msid.gs_sid, msid.gs_type); 1607 break; 1608 case SMBADM_GRP_DELMEMBER: 1609 act_str = gettext("remove"); 1610 rc = smb_lgrp_del_member(gname, 1611 msid.gs_sid, msid.gs_type); 1612 break; 1613 default: 1614 rc = SMB_LGRP_INTERNAL_ERROR; 1615 break; 1616 } 1617 1618 smb_sid_free(msid.gs_sid); 1619 1620 if (rc != SMB_LGRP_SUCCESS) { 1621 (void) fprintf(stderr, 1622 gettext("failed to %s %s (%s)\n"), 1623 act_str, mname, smb_lgrp_strerror(rc)); 1624 return (1); 1625 } 1626 return (0); 1627 } 1628 1629 static int 1630 smbadm_user_delete(int argc, char **argv) 1631 { 1632 int error; 1633 char *user = NULL; 1634 1635 user = argv[optind]; 1636 if (optind >= argc || user == NULL || *user == '\0') { 1637 (void) fprintf(stderr, gettext("missing user name\n")); 1638 smbadm_usage(B_FALSE); 1639 } 1640 1641 error = smb_pwd_setcntl(user, SMB_PWC_DELETE); 1642 if (error == SMB_PWE_SUCCESS) 1643 (void) printf(gettext("%s has been deleted.\n"), user); 1644 else 1645 (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); 1646 1647 return (error); 1648 } 1649 1650 static int 1651 smbadm_user_disable(int argc, char **argv) 1652 { 1653 int error; 1654 char *user = NULL; 1655 1656 user = argv[optind]; 1657 if (optind >= argc || user == NULL || *user == '\0') { 1658 (void) fprintf(stderr, gettext("missing user name\n")); 1659 smbadm_usage(B_FALSE); 1660 } 1661 1662 error = smb_pwd_setcntl(user, SMB_PWC_DISABLE); 1663 if (error == SMB_PWE_SUCCESS) 1664 (void) printf(gettext("%s is disabled.\n"), user); 1665 else 1666 (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); 1667 1668 return (error); 1669 } 1670 1671 static int 1672 smbadm_user_enable(int argc, char **argv) 1673 { 1674 int error; 1675 char *user = NULL; 1676 1677 user = argv[optind]; 1678 if (optind >= argc || user == NULL || *user == '\0') { 1679 (void) fprintf(stderr, gettext("missing user name\n")); 1680 smbadm_usage(B_FALSE); 1681 } 1682 1683 error = smb_pwd_setcntl(user, SMB_PWC_ENABLE); 1684 if (error == SMB_PWE_SUCCESS) 1685 (void) printf(gettext("%s is enabled.\n"), user); 1686 else 1687 (void) fprintf(stderr, "%s\n", smbadm_pwd_strerror(error)); 1688 1689 return (error); 1690 } 1691 1692 1693 int 1694 main(int argc, char **argv) 1695 { 1696 int ret; 1697 int i; 1698 1699 (void) setlocale(LC_ALL, ""); 1700 (void) textdomain(TEXT_DOMAIN); 1701 1702 (void) malloc(0); /* satisfy libumem dependency */ 1703 1704 progname = basename(argv[0]); 1705 1706 if (is_system_labeled()) { 1707 (void) fprintf(stderr, 1708 gettext("Trusted Extensions not supported\n")); 1709 return (1); 1710 } 1711 1712 if (argc < 2) { 1713 (void) fprintf(stderr, gettext("missing command\n")); 1714 smbadm_usage(B_FALSE); 1715 } 1716 1717 /* 1718 * Special case "cmd --help/-?" 1719 */ 1720 if (strcmp(argv[1], "-?") == 0 || 1721 strcmp(argv[1], "--help") == 0 || 1722 strcmp(argv[1], "-h") == 0) 1723 smbadm_usage(B_TRUE); 1724 1725 for (i = 0; i < SMBADM_NCMD; ++i) { 1726 curcmd = &smbadm_cmdtable[i]; 1727 if (strcasecmp(argv[1], curcmd->name) == 0) { 1728 if (argc > 2) { 1729 /* cmd subcmd --help/-? */ 1730 if (strcmp(argv[2], "-?") == 0 || 1731 strcmp(argv[2], "--help") == 0 || 1732 strcmp(argv[2], "-h") == 0) 1733 smbadm_usage(B_TRUE); 1734 } 1735 1736 if (!smbadm_checkauth(curcmd->auth)) { 1737 (void) fprintf(stderr, 1738 gettext("%s: %s: authorization denied\n"), 1739 progname, curcmd->name); 1740 return (1); 1741 } 1742 1743 if ((ret = smbadm_init()) != 0) 1744 return (ret); 1745 1746 ret = curcmd->func(argc - 1, &argv[1]); 1747 1748 smbadm_fini(); 1749 return (ret); 1750 } 1751 } 1752 1753 curcmd = NULL; 1754 (void) fprintf(stderr, gettext("unknown subcommand (%s)\n"), argv[1]); 1755 smbadm_usage(B_FALSE); 1756 return (2); 1757 } 1758 1759 static int 1760 smbadm_init(void) 1761 { 1762 int rc; 1763 1764 if ((curcmd->flags & SMBADM_CMDF_KMOD) != 0) { 1765 if ((rc = smb_kmod_bind(B_FALSE)) != 0) { 1766 (void) fprintf(stderr, 1767 gettext("failed to open driver (%s)\n"), 1768 strerror(rc)); 1769 return (1); 1770 } 1771 } 1772 1773 if ((curcmd->flags & SMBADM_CMDF_GROUP) != 0) { 1774 if ((rc = smb_lgrp_start()) != SMB_LGRP_SUCCESS) { 1775 (void) fprintf(stderr, 1776 gettext("failed to initialize (%s)\n"), 1777 smb_lgrp_strerror(rc)); 1778 return (1); 1779 } 1780 } 1781 1782 if ((curcmd->flags & SMBADM_CMDF_USER) != 0) { 1783 smb_pwd_init(B_FALSE); 1784 } 1785 1786 return (0); 1787 } 1788 1789 static void 1790 smbadm_fini(void) 1791 { 1792 1793 if ((curcmd->flags & SMBADM_CMDF_KMOD) != 0) { 1794 smb_kmod_unbind(); 1795 } 1796 1797 if ((curcmd->flags & SMBADM_CMDF_GROUP) != 0) { 1798 smb_lgrp_stop(); 1799 } 1800 1801 if ((curcmd->flags & SMBADM_CMDF_USER) != 0) { 1802 smb_pwd_fini(); 1803 } 1804 } 1805 1806 static boolean_t 1807 smbadm_checkauth(const char *auth) 1808 { 1809 struct passwd *pw; 1810 1811 if ((pw = getpwuid(getuid())) == NULL) 1812 return (B_FALSE); 1813 1814 if (chkauthattr(auth, pw->pw_name) == 0) 1815 return (B_FALSE); 1816 1817 return (B_TRUE); 1818 } 1819 1820 static boolean_t 1821 smbadm_prop_validate(smbadm_prop_t *prop, boolean_t chkval) 1822 { 1823 smbadm_prop_handle_t *pinfo; 1824 int i; 1825 1826 for (i = 0; i < SMBADM_NPROP; i++) { 1827 pinfo = &smbadm_ptable[i]; 1828 if (strcmp(pinfo->p_name, prop->p_name) == 0) { 1829 if (pinfo->p_chkfn && chkval) 1830 return (pinfo->p_chkfn(prop)); 1831 1832 return (B_TRUE); 1833 } 1834 } 1835 1836 (void) fprintf(stderr, gettext("unrecognized property '%s'\n"), 1837 prop->p_name); 1838 1839 return (B_FALSE); 1840 } 1841 1842 static int 1843 smbadm_prop_parse(char *arg, smbadm_prop_t *prop) 1844 { 1845 boolean_t parse_value; 1846 char *equal; 1847 1848 if (arg == NULL) 1849 return (2); 1850 1851 prop->p_name = prop->p_value = NULL; 1852 1853 if (strcmp(curcmd->name, "set") == 0) 1854 parse_value = B_TRUE; 1855 else 1856 parse_value = B_FALSE; 1857 1858 prop->p_name = arg; 1859 1860 if (parse_value) { 1861 equal = strchr(arg, '='); 1862 if (equal == NULL) 1863 return (2); 1864 1865 *equal++ = '\0'; 1866 prop->p_value = equal; 1867 } 1868 1869 if (smbadm_prop_validate(prop, parse_value) == B_FALSE) 1870 return (2); 1871 1872 return (0); 1873 } 1874 1875 static smbadm_prop_handle_t * 1876 smbadm_prop_gethandle(char *pname) 1877 { 1878 int i; 1879 1880 for (i = 0; i < SMBADM_NPROP; i++) 1881 if (strcmp(pname, smbadm_ptable[i].p_name) == 0) 1882 return (&smbadm_ptable[i]); 1883 1884 return (NULL); 1885 } 1886 1887 static int 1888 smbadm_setprop_desc(char *gname, smbadm_prop_t *prop) 1889 { 1890 int status; 1891 1892 status = smb_lgrp_setcmnt(gname, prop->p_value); 1893 if (status != SMB_LGRP_SUCCESS) { 1894 (void) fprintf(stderr, 1895 gettext("failed to modify the group description (%s)\n"), 1896 smb_lgrp_strerror(status)); 1897 return (1); 1898 } 1899 1900 (void) printf(gettext("%s: description modified\n"), gname); 1901 return (0); 1902 } 1903 1904 static int 1905 smbadm_getprop_desc(char *gname, smbadm_prop_t *prop) 1906 { 1907 char *cmnt = NULL; 1908 int status; 1909 1910 status = smb_lgrp_getcmnt(gname, &cmnt); 1911 if (status != SMB_LGRP_SUCCESS) { 1912 (void) fprintf(stderr, 1913 gettext("failed to get the group description (%s)\n"), 1914 smb_lgrp_strerror(status)); 1915 return (1); 1916 } 1917 1918 (void) printf(gettext("\t%s: %s\n"), prop->p_name, cmnt); 1919 free(cmnt); 1920 return (0); 1921 } 1922 1923 static int 1924 smbadm_group_setpriv(char *gname, uint8_t priv_id, smbadm_prop_t *prop) 1925 { 1926 boolean_t enable; 1927 int status; 1928 int ret; 1929 1930 if (strcasecmp(prop->p_value, "on") == 0) { 1931 (void) printf(gettext("Enabling %s privilege "), prop->p_name); 1932 enable = B_TRUE; 1933 } else { 1934 (void) printf(gettext("Disabling %s privilege "), prop->p_name); 1935 enable = B_FALSE; 1936 } 1937 1938 status = smb_lgrp_setpriv(gname, priv_id, enable); 1939 if (status == SMB_LGRP_SUCCESS) { 1940 (void) printf(gettext("succeeded\n")); 1941 ret = 0; 1942 } else { 1943 (void) printf(gettext("failed: %s\n"), 1944 smb_lgrp_strerror(status)); 1945 ret = 1; 1946 } 1947 1948 return (ret); 1949 } 1950 1951 static int 1952 smbadm_group_getpriv(char *gname, uint8_t priv_id, smbadm_prop_t *prop) 1953 { 1954 boolean_t enable; 1955 int status; 1956 1957 status = smb_lgrp_getpriv(gname, priv_id, &enable); 1958 if (status != SMB_LGRP_SUCCESS) { 1959 (void) fprintf(stderr, gettext("failed to get %s (%s)\n"), 1960 prop->p_name, smb_lgrp_strerror(status)); 1961 return (1); 1962 } 1963 1964 (void) printf(gettext("\t%s: %s\n"), prop->p_name, 1965 (enable) ? "On" : "Off"); 1966 1967 return (0); 1968 } 1969 1970 static int 1971 smbadm_setprop_tkowner(char *gname, smbadm_prop_t *prop) 1972 { 1973 return (smbadm_group_setpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop)); 1974 } 1975 1976 static int 1977 smbadm_getprop_tkowner(char *gname, smbadm_prop_t *prop) 1978 { 1979 return (smbadm_group_getpriv(gname, SE_TAKE_OWNERSHIP_LUID, prop)); 1980 } 1981 1982 static int 1983 smbadm_setprop_readfile(char *gname, smbadm_prop_t *prop) 1984 { 1985 return (smbadm_group_setpriv(gname, SE_READ_FILE_LUID, prop)); 1986 } 1987 1988 static int 1989 smbadm_getprop_readfile(char *gname, smbadm_prop_t *prop) 1990 { 1991 return (smbadm_group_getpriv(gname, SE_READ_FILE_LUID, prop)); 1992 } 1993 1994 static int 1995 smbadm_setprop_writefile(char *gname, smbadm_prop_t *prop) 1996 { 1997 return (smbadm_group_setpriv(gname, SE_WRITE_FILE_LUID, prop)); 1998 } 1999 2000 static int 2001 smbadm_getprop_writefile(char *gname, smbadm_prop_t *prop) 2002 { 2003 return (smbadm_group_getpriv(gname, SE_WRITE_FILE_LUID, prop)); 2004 } 2005 2006 static int 2007 smbadm_setprop_backup(char *gname, smbadm_prop_t *prop) 2008 { 2009 return (smbadm_group_setpriv(gname, SE_BACKUP_LUID, prop)); 2010 } 2011 2012 static int 2013 smbadm_getprop_backup(char *gname, smbadm_prop_t *prop) 2014 { 2015 return (smbadm_group_getpriv(gname, SE_BACKUP_LUID, prop)); 2016 } 2017 2018 static int 2019 smbadm_setprop_restore(char *gname, smbadm_prop_t *prop) 2020 { 2021 return (smbadm_group_setpriv(gname, SE_RESTORE_LUID, prop)); 2022 } 2023 2024 static int 2025 smbadm_getprop_restore(char *gname, smbadm_prop_t *prop) 2026 { 2027 return (smbadm_group_getpriv(gname, SE_RESTORE_LUID, prop)); 2028 } 2029 2030 static boolean_t 2031 smbadm_chkprop_priv(smbadm_prop_t *prop) 2032 { 2033 if (prop->p_value == NULL || *prop->p_value == '\0') { 2034 (void) fprintf(stderr, 2035 gettext("missing value for '%s'\n"), prop->p_name); 2036 return (B_FALSE); 2037 } 2038 2039 if (strcasecmp(prop->p_value, "on") == 0) 2040 return (B_TRUE); 2041 2042 if (strcasecmp(prop->p_value, "off") == 0) 2043 return (B_TRUE); 2044 2045 (void) fprintf(stderr, 2046 gettext("%s: unrecognized value for '%s' property\n"), 2047 prop->p_value, prop->p_name); 2048 2049 return (B_FALSE); 2050 } 2051 2052 static const char * 2053 smbadm_pwd_strerror(int error) 2054 { 2055 switch (error) { 2056 case SMB_PWE_SUCCESS: 2057 return (gettext("Success.")); 2058 2059 case SMB_PWE_USER_UNKNOWN: 2060 return (gettext("User does not exist.")); 2061 2062 case SMB_PWE_USER_DISABLE: 2063 return (gettext("User is disabled.")); 2064 2065 case SMB_PWE_CLOSE_FAILED: 2066 case SMB_PWE_OPEN_FAILED: 2067 case SMB_PWE_WRITE_FAILED: 2068 case SMB_PWE_UPDATE_FAILED: 2069 return (gettext("Unexpected failure. " 2070 "SMB password database unchanged.")); 2071 2072 case SMB_PWE_STAT_FAILED: 2073 return (gettext("stat of SMB password file failed.")); 2074 2075 case SMB_PWE_BUSY: 2076 return (gettext("SMB password database busy. " 2077 "Try again later.")); 2078 2079 case SMB_PWE_DENIED: 2080 return (gettext("Operation not permitted.")); 2081 2082 case SMB_PWE_SYSTEM_ERROR: 2083 return (gettext("System error.")); 2084 2085 default: 2086 break; 2087 } 2088 2089 return (gettext("Unknown error code.")); 2090 } 2091 2092 /* 2093 * Enable libumem debugging by default on DEBUG builds. 2094 */ 2095 #ifdef DEBUG 2096 const char * 2097 _umem_debug_init(void) 2098 { 2099 return ("default,verbose"); /* $UMEM_DEBUG setting */ 2100 } 2101 2102 const char * 2103 _umem_logging_init(void) 2104 { 2105 return ("fail,contents"); /* $UMEM_LOGGING setting */ 2106 } 2107 #endif 2108