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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <locale.h> 31 #include <strings.h> 32 #include "idmap_engine.h" 33 #include "idmap_priv.h" 34 35 /* Initialization values for pids/rids: */ 36 37 #define UNDEFINED_UID (uid_t)-1 38 #define UNDEFINED_GID (gid_t)-1 39 #define UNDEFINED_RID (idmap_rid_t)-1; 40 41 /* is_user values */ 42 43 #define I_YES 1 44 #define I_NO 0 45 #define I_UNKNOWN -1 46 47 /* Directions */ 48 49 #define DIR_W2U 1 50 #define DIR_U2W 2 51 #define DIR_BI 0 52 #define DIR_UNKNOWN -1 53 54 /* 55 * used in do_show for the type of argument, which can be winname, 56 * unixname, uid, gid, sid or not given at all: 57 */ 58 59 #define TYPE_SID 0x010 /* sid */ 60 #define TYPE_WN 0x110 /* winname */ 61 #define TYPE_UID 0x001 /* uid */ 62 #define TYPE_GID 0x002 /* gid */ 63 #define TYPE_PID 0x000 /* pid */ 64 #define TYPE_UN 0x100 /* unixname */ 65 66 #define IS_WIN 0x010 /* mask for the windows types */ 67 #define IS_NAME 0x100 /* mask for string name types */ 68 #define IS_GROUP 0x002 /* mask for, well, TYPE_GID */ 69 70 71 /* Identity type strings */ 72 73 #define ID_WINNAME "winname" 74 #define ID_UNIXNAME "unixname" 75 #define ID_SID "sid" 76 #define ID_UID "uid" 77 #define ID_GID "gid" 78 79 /* Flags */ 80 81 #define g_FLAG 'g' 82 #define u_FLAG 'u' 83 #define f_FLAG 'f' 84 #define t_FLAG 't' 85 #define d_FLAG 'd' 86 #define F_FLAG 'F' 87 #define a_FLAG 'a' 88 #define n_FLAG 'n' 89 #define c_FLAG 'c' 90 91 92 /* used in the function do_import */ 93 #define MAX_INPUT_LINE_SZ 2047 94 95 96 typedef struct { 97 int is_user; 98 int direction; 99 boolean_t is_nt4; 100 char *unixname; 101 char *winname; 102 char *windomain; 103 char *sidprefix; 104 idmap_rid_t rid; 105 uid_t pid; 106 } name_mapping_t; 107 108 /* 109 * Formats of the output: 110 * 111 * Idmap reads/prints mappings in several formats: ordinary mappings, 112 * name mappings in Samba username map format (smbusers), Netapp 113 * usermap.cfg. 114 * 115 * DEFAULT_FORMAT are in fact the idmap subcommands suitable for 116 * piping to idmap standart input. For example 117 * add -u -d winname:bob@foo.com unixname:fred 118 * add -u -d winname:bob2bar.com unixname:fred 119 * 120 * SMBUSERS is the format of Samba username map (smbusers). For full 121 * documentation, search for "username map" in smb.conf manpage. 122 * The format is for example 123 * fred = bob@foo.com bob2@bar.com 124 * 125 * USERMAP_CFG is the format of Netapp usermap.cfg file. Search 126 * http://www.netapp.com/ for more documentation. IP qualifiers are not 127 * supported. 128 * The format is for example 129 * bob@foo.com => fred 130 * "Bob With Spaces"@bar.com => fred #comment 131 * 132 * The previous formats were for name rules. MAPPING_NAME and 133 * MAPPING_ID are for the actual mappings, as seen in show/dump 134 * commands. MAPPING_NAME prefers the string names of the user over 135 * their numerical identificators. MAPPING_ID prints just the 136 * identificators. 137 * Example of the MAPPING_NAME: 138 * winname:bob@foo.com -> unixname:fred 139 * 140 * Example of the MAPPING_ID: 141 * sid:S-1-2-3-4 -> uid:5678 142 */ 143 144 typedef enum { 145 UNDEFINED_FORMAT = -1, 146 DEFAULT_FORMAT = 0, 147 MAPPING_ID, 148 MAPPING_NAME, 149 USERMAP_CFG, 150 SMBUSERS 151 } format_t; 152 153 /* Gives the format to use. Set in print_mapping_init */ 154 static format_t pnm_format; 155 156 /* The file for print_mapping_init output. Mostly just stdout. */ 157 static FILE *pnm_file; 158 159 /* In smbusers format, more unixnames can be aggregated to one line. */ 160 static char *pnm_last_unixname; 161 162 /* 163 * idmap_api batch related variables: 164 * 165 * idmap can operate in two modes. It the batch mode, the idmap_api 166 * batch is commited at the end of a batch of several 167 * commands. At the end of input file, typically. This mode is used 168 * for processing input from a file. 169 * In the non-batch mode, each command is commited immediately. This 170 * mode is used for tty input. 171 */ 172 173 /* Are we in the batch mode? */ 174 static int batch_mode = 0; 175 176 /* Handles for idmap_api batch */ 177 static idmap_handle_t *handle = NULL; 178 static idmap_udt_handle_t *udt = NULL; 179 180 /* Do we need to commit the udt batch at the end? */ 181 static int udt_used; 182 183 /* Command handlers */ 184 185 static int do_show_mapping(flag_t *f, int argc, char **argv); 186 static int do_dump(flag_t *f, int argc, char **argv); 187 static int do_import(flag_t *f, int argc, char **argv); 188 static int do_list_name_mappings(flag_t *f, int argc, char **argv); 189 static int do_add_name_mapping(flag_t *f, int argc, char **argv); 190 static int do_remove_name_mapping(flag_t *f, int argc, char **argv); 191 static int do_exit(flag_t *f, int argc, char **argv); 192 static int do_export(flag_t *f, int argc, char **argv); 193 static int do_help(flag_t *f, int argc, char **argv); 194 195 /* Command names and their hanlers to be passed to idmap_engine */ 196 197 static cmd_ops_t commands[] = { 198 { 199 "show", 200 "c(create)", 201 do_show_mapping 202 }, 203 { 204 "dump", 205 "n(names)g(group)u(user)", 206 do_dump 207 }, 208 { 209 "import", 210 "F(flush)f:(file)", 211 do_import 212 }, 213 { 214 "export", 215 "f:(file)", 216 do_export 217 }, 218 { 219 "list", 220 "g(group)u(user)", 221 do_list_name_mappings 222 }, 223 { 224 "add", 225 "g(group)u(user)d(directional)", 226 do_add_name_mapping 227 }, 228 { 229 "remove", 230 "a(all)u(user)g(group)t(to)f(from)d(directional)", 231 do_remove_name_mapping 232 }, 233 { 234 "exit", 235 "", 236 do_exit 237 }, 238 { 239 "help", 240 "", 241 do_help 242 } 243 }; 244 245 /* Print help message */ 246 static void 247 help() { 248 (void) fprintf(stderr, 249 "idmap\n" 250 "idmap -f command-file\n" 251 "idmap show [-c] identity [targettype]\n" 252 "idmap dump [-u|-g] [-n]\n" 253 "idmap add -u|-g [-d] name1 name2\n" 254 "idmap remove -u|-g -a\n" 255 "idmap remove -u|-g name\n" 256 "idmap remove -u|-g [-d] name1 name2\n" 257 "idmap list [-u|-g]\n" 258 "idmap import [-F] [-f file] format\n" 259 "idmap export [-f file] format\n" 260 "idmap help\n"); 261 } 262 263 /* The handler for the "help" command. */ 264 static int 265 /* LINTED E_FUNC_ARG_UNUSED */ 266 do_help(flag_t *f, int argc, char **argv) 267 { 268 help(); 269 return (0); 270 } 271 272 /* Initialization of the idmap api batch */ 273 static int 274 init_batch() { 275 idmap_stat stat; 276 277 stat = idmap_init(&handle); 278 if (stat < 0) { 279 (void) fprintf(stderr, 280 gettext("Connection not established (%s)\n"), 281 idmap_stat2string(NULL, stat)); 282 return (-1); 283 } 284 285 return (0); 286 } 287 288 /* Initialization common to all commands */ 289 static int 290 init_command() { 291 if (batch_mode) 292 return (0); 293 294 return (init_batch()); 295 } 296 297 /* Finalization common to all commands */ 298 static void 299 fini_command() { 300 if (batch_mode) 301 return; 302 (void) idmap_fini(handle); 303 handle = NULL; 304 } 305 306 /* Initialization of the commands which perform write operations */ 307 static int 308 init_udt_batch() { 309 idmap_stat stat; 310 311 if (init_batch()) 312 return (-1); 313 314 stat = idmap_udt_create(handle, &udt); 315 if (stat < 0) { 316 (void) fprintf(stderr, 317 gettext("Error initiating transaction (%s)"), 318 idmap_stat2string(handle, stat)); 319 return (-1); 320 } 321 return (0); 322 } 323 324 325 /* Finalization of the write commands */ 326 static int 327 init_udt_command() { 328 udt_used = 1; 329 if (batch_mode) 330 return (0); 331 332 return (init_udt_batch()); 333 } 334 335 336 /* If everythings is OK, send the udt batch to idmapd */ 337 static void 338 fini_udt_command(int ok) { 339 idmap_stat stat; 340 341 if (batch_mode) 342 return; 343 if (udt == NULL) 344 return; 345 346 if (ok && udt_used) { 347 stat = idmap_udt_commit(udt); 348 if (stat < 0) { 349 (void) fprintf(stderr, 350 gettext("Error commiting transaction (%s)\n"), 351 idmap_stat2string(handle, stat)); 352 } 353 } 354 355 idmap_udt_destroy(udt); 356 udt = NULL; 357 udt_used = 0; 358 fini_command(); 359 } 360 361 362 /* Convert numeric expression of the direction to it's string form */ 363 static char * 364 direction2string(int direction) { 365 switch (direction) { 366 case DIR_BI: 367 return ("=="); 368 case DIR_W2U: 369 return ("=>"); 370 case DIR_U2W: 371 return ("<="); 372 default: 373 (void) fprintf(stderr, gettext("Internal error.\n")); 374 return (""); 375 } 376 /* never reached */ 377 } 378 379 /* Do we need quotation marks around winname in the USERMAP_CFG format? */ 380 static int 381 needs_protection(char *what) { 382 if (strchr(what, ' ') != NULL) 383 return (1); 384 385 if (strchr(what, '\t') != NULL) 386 return (1); 387 388 if (strchr(what, '#') != NULL) 389 return (1); 390 391 return (0); 392 } 393 394 /* Protect all shell-special characters by '\\' */ 395 static int 396 shell_app(char **res, char *string) { 397 size_t res_len = 0; 398 size_t res_size = 24; 399 int i; 400 char c; 401 402 *res = (char *)malloc(res_size * sizeof (char)); 403 if (*res == NULL) { 404 (void) fprintf(stderr, gettext("Not enough memory.\n")); 405 return (-1); 406 } 407 408 for (i = 0; string[i] != '\0'; i++) { 409 c = string[i]; 410 411 if (strchr("\"\\ \t#$", c) != NULL) 412 (*res)[res_len++] = '\\'; 413 (*res)[res_len++] = c; 414 415 if (res_size - 1 <= res_len) { 416 res_size *= 2; 417 *res = (char *)realloc(*res, res_size * sizeof (char)); 418 if (*res == NULL) { 419 (void) fprintf(stderr, 420 gettext("Not enough memory.\n")); 421 return (-1); 422 } 423 } 424 } 425 426 (*res)[res_len++] = '\0'; 427 return (0); 428 } 429 430 /* Assemble string form sid */ 431 static char * 432 sid_format(char *sidprefix, idmap_rid_t rid) { 433 char *to; 434 size_t len; 435 436 /* 'sid:' + sidprefix + '-' + rid + '\0' */ 437 len = strlen(sidprefix) + 6 + 3 * sizeof (rid); 438 to = (char *)malloc(len * sizeof (char)); 439 if (to == NULL) 440 return (NULL); 441 442 (void) snprintf(to, len, "sid:%s-%u", sidprefix, rid); 443 return (to); 444 } 445 446 /* Assemble string form uid or gid */ 447 static char * 448 pid_format(uid_t from, int is_user) { 449 char *to; 450 size_t len; 451 452 /* ID_UID ":" + uid + '\0' */ 453 len = 5 + 3 * sizeof (uid_t); 454 to = (char *)malloc(len * sizeof (char)); 455 if (to == NULL) 456 return (NULL); 457 458 (void) snprintf(to, 16, "%s:%u", is_user ? ID_UID : ID_GID, from); 459 return (to); 460 } 461 462 /* Assemble winname, e.g. "winname:bob@foo.sun.com", from name_mapping_t */ 463 static int 464 nm2winqn(name_mapping_t *nm, char **winqn) { 465 char *out; 466 size_t length = 0; 467 int is_domain = 1; 468 469 /* Sometimes there are no text names. Return a sid, then. */ 470 if (nm->winname == NULL) { 471 if (nm->sidprefix == NULL) 472 return (-1); 473 474 *winqn = sid_format(nm->sidprefix, nm->rid); 475 return (0); 476 } 477 478 length = strlen(ID_WINNAME ":") + strlen(nm->winname); 479 480 /* Windomain is not mandatory: */ 481 if (nm->windomain == NULL || 482 *nm->winname == '\0' || 483 strcmp(nm->winname, "\"\"") == 0) 484 is_domain = 0; 485 else 486 length += strlen(nm->windomain) + 1; 487 488 out = (char *)malloc((length + 1) * sizeof (char)); 489 if (out == NULL) { 490 (void) fprintf(stderr, 491 gettext("Not enough memory.\n")); 492 return (-1); 493 } 494 495 (void) strcpy(out, ID_WINNAME ":"); 496 497 if (!is_domain) 498 (void) strcat(out, nm->winname); 499 else if (nm->is_nt4) { 500 (void) strcat(out, nm->windomain); 501 (void) strcat(out, "\\"); 502 (void) strcat(out, nm->winname); 503 } else { 504 (void) strcat(out, nm->winname); 505 (void) strcat(out, "@"); 506 (void) strcat(out, nm->windomain); 507 } 508 509 *winqn = out; 510 return (0); 511 } 512 513 /* Assemble a text unixname, e.g. unixname:fred */ 514 static int 515 nm2unixname(name_mapping_t *nm, char **unixname) { 516 size_t length = 0; 517 char *out; 518 char *it; 519 520 /* Sometimes there is no name, just pid: */ 521 if (nm->unixname == NULL) { 522 if (nm->pid == UNDEFINED_UID) 523 return (-1); 524 525 *unixname = pid_format(nm->pid, nm->is_user); 526 return (0); 527 } 528 529 if (shell_app(&it, nm->unixname)) 530 return (-1); 531 532 length = strlen(ID_UNIXNAME ":") + strlen(it); 533 534 out = (char *)malloc((length + 1) * sizeof (char)); 535 if (out == NULL) { 536 (void) fprintf(stderr, 537 gettext("Not enough memory.\n")); 538 free(it); 539 return (-1); 540 } 541 542 (void) strcpy(out, ID_UNIXNAME ":"); 543 (void) strcat(out, it); 544 free(it); 545 546 *unixname = out; 547 return (0); 548 } 549 550 /* Initialize print_mapping variables. Must be called before print_mapping */ 551 static int 552 print_mapping_init(format_t f, FILE *fi) { 553 pnm_format = f; 554 pnm_file = fi; 555 556 switch (pnm_format) { 557 case SMBUSERS: 558 pnm_last_unixname = NULL; 559 break; 560 default: 561 ; 562 } 563 564 return (0); 565 } 566 567 /* Finalize print_mapping. */ 568 static int 569 print_mapping_fini() { 570 switch (pnm_format) { 571 case SMBUSERS: 572 if (pnm_last_unixname != NULL) { 573 (void) fprintf(pnm_file, "\n"); 574 free(pnm_last_unixname); 575 } 576 break; 577 default: 578 ; 579 } 580 581 pnm_file = stderr; 582 pnm_format = UNDEFINED_FORMAT; 583 584 return (0); 585 } 586 587 /* 588 * This prints both name rules and ordinary mappings, based on the pnm_format 589 * set in print_mapping_init(). 590 */ 591 592 static int 593 print_mapping(name_mapping_t *nm) 594 { 595 char *dirstring; 596 char *winname_qm, *windomain_qm, *unixname_qm; 597 char type; 598 char *winname = NULL; 599 char *winname1 = NULL; 600 char *unixname = NULL; 601 FILE *f = pnm_file; 602 603 604 switch (pnm_format) { 605 case MAPPING_NAME: 606 if (nm2winqn(nm, &winname) < 0) 607 return (-1); 608 if (nm2unixname(nm, &unixname) < 0) { 609 free(winname); 610 return (-1); 611 } 612 /* LINTED E_CASE_FALLTHRU */ 613 case MAPPING_ID: 614 if (pnm_format == MAPPING_ID) { 615 if (nm->sidprefix == NULL) { 616 (void) fprintf(stderr, 617 gettext("SID not given.\n")); 618 return (-1); 619 } 620 winname = sid_format(nm->sidprefix, nm->rid); 621 if (winname == NULL) 622 return (-1); 623 unixname = pid_format(nm->pid, nm->is_user); 624 if (unixname == NULL) { 625 free(winname); 626 return (-1); 627 } 628 } 629 630 dirstring = direction2string(nm->direction); 631 632 (void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring, 633 unixname); 634 635 free(winname); 636 free(unixname); 637 break; 638 case SMBUSERS: 639 if (!nm->is_user) { 640 (void) fprintf(stderr, 641 gettext("Group rule: ")); 642 f = stderr; 643 } else if (nm->direction == DIR_U2W) { 644 (void) fprintf(stderr, 645 gettext("Opposite direction of the mapping: ")); 646 f = stderr; 647 } 648 if (shell_app(&winname, nm->winname)) 649 return (-1); 650 651 if (pnm_file != f) { 652 (void) fprintf(f, "%s = %s\n", nm->unixname, winname); 653 } else if (pnm_last_unixname != NULL && 654 strcmp(pnm_last_unixname, nm->unixname) == 0) { 655 (void) fprintf(f, " %s", winname); 656 } else { 657 if (pnm_last_unixname != NULL) { 658 (void) fprintf(f, "\n"); 659 free(pnm_last_unixname); 660 } 661 pnm_last_unixname = strdup(nm->unixname); 662 (void) fprintf(f, "%s = %s", nm->unixname, winname); 663 } 664 665 free(winname); 666 667 break; 668 case USERMAP_CFG: 669 if (!nm->is_user) { 670 (void) fprintf(stderr, 671 gettext("Group rule: ")); 672 f = stderr; 673 } 674 675 dirstring = direction2string(nm->direction); 676 677 winname_qm = needs_protection(nm->winname) ? "\"" : ""; 678 windomain_qm = nm->windomain && 679 needs_protection(nm->windomain) ? "\"" : ""; 680 unixname_qm = needs_protection(nm->unixname) ? "\"" : ""; 681 682 if (nm->windomain == NULL) 683 (void) fprintf(f, "%s%s%s\t%s\t%s%s%s\n", 684 winname_qm, 685 nm->winname, winname_qm, dirstring, 686 unixname_qm, nm->unixname, unixname_qm); 687 else 688 (void) fprintf(f, nm->is_nt4 ? 689 "%s%s%1$s\\%3$s%4$s%3$s\t%5$s\t%6$s%7$s%6$s\n" : 690 "%3$s%4$s%3$s@%1$s%2$s%1$s\t%5$s\t%6$s%7$s%6$s\n", 691 windomain_qm, nm->windomain, 692 winname_qm, nm->winname, 693 dirstring, 694 unixname_qm, nm->unixname); 695 break; 696 697 case DEFAULT_FORMAT: 698 /* 'u', 'g' refer to -u, -g switch of idmap add */ 699 type = nm->is_user ? 'u' : 'g'; 700 if (nm2winqn(nm, &winname1) < 0) 701 return (-1); 702 703 if (shell_app(&winname, winname1)) { 704 free(winname1); 705 return (-1); 706 } 707 708 free(winname1); 709 710 if (nm2unixname(nm, &unixname)) { 711 free(winname); 712 return (-1); 713 } 714 715 if (nm->direction == DIR_U2W) { 716 (void) fprintf(f, 717 "add -%c -d\t%s\t%s\n", 718 type, unixname, winname); 719 } else { 720 (void) fprintf(f, 721 "add -%c %s\t%s\t%s\n", 722 type, nm->direction == DIR_BI ? "" : "-d", 723 winname, unixname); 724 } 725 free(winname); 726 free(unixname); 727 break; 728 default: 729 (void) fprintf(stderr, gettext("Internal error.\n")); 730 return (-1); 731 } 732 733 return (0); 734 } 735 736 /* Allocate a new name_mapping_t and initialize the values. */ 737 static name_mapping_t * 738 name_mapping_init() { 739 name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t)); 740 if (nm == NULL) { 741 (void) fprintf(stderr, gettext("Not enough memory.\n")); 742 return (NULL); 743 } 744 nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL; 745 nm->rid = UNDEFINED_RID; 746 nm->is_nt4 = B_FALSE; 747 nm->is_user = I_UNKNOWN; 748 nm->direction = DIR_UNKNOWN; 749 nm->pid = UNDEFINED_UID; 750 return (nm); 751 } 752 753 /* Free name_mapping_t */ 754 static void 755 name_mapping_fini(name_mapping_t *nm) { 756 757 free(nm->winname); 758 free(nm->windomain); 759 free(nm->unixname); 760 free(nm->sidprefix); 761 762 free(nm); 763 } 764 765 /* Is there exactly one of -g, -u flags? */ 766 static int 767 is_type_determined(flag_t *f) 768 { 769 if (f[u_FLAG] == NULL && f[g_FLAG] == NULL || /* none */ 770 f[u_FLAG] != NULL && f[g_FLAG] != NULL) /* both */ { 771 (void) fprintf(stderr, 772 gettext("Type (-u|-g) not determined.\n")); 773 return (0); 774 } 775 return (1); 776 } 777 778 /* Does user request a user-related operation? */ 779 static int 780 is_user_wanted(flag_t *f) { 781 if (f[u_FLAG] != NULL || f[g_FLAG] == NULL) 782 return (1); 783 return (0); 784 } 785 786 /* Does user request a group-related operation? */ 787 static int 788 is_group_wanted(flag_t *f) { 789 if (f[g_FLAG] != NULL || f[u_FLAG] == NULL) 790 return (1); 791 return (0); 792 } 793 794 795 /* dump command handler */ 796 static int 797 /* LINTED E_FUNC_ARG_UNUSED */ 798 do_dump(flag_t *f, int argc, char **argv) 799 { 800 idmap_stat stat; 801 idmap_iter_t *ihandle; 802 int is_user; 803 int rc = 0; 804 805 if (init_command()) 806 return (-1); 807 808 (void) print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID, 809 stdout); 810 811 for (is_user = I_YES; is_user >= I_NO; is_user--) { 812 /* 813 * If there is exactly one of -u, -g flags, we print 814 * only that type. Otherwise both of them: 815 */ 816 if (!is_user_wanted(f) && is_user || 817 !is_group_wanted(f) && !is_user) 818 continue; 819 820 stat = idmap_iter_mappings(handle, is_user, &ihandle); 821 if (stat < 0) { 822 (void) fprintf(stderr, 823 gettext("Iteration handle not obtained (%s)\n"), 824 idmap_stat2string(handle, stat)); 825 rc = -1; 826 goto cleanup; 827 } 828 829 do { 830 name_mapping_t *nm = name_mapping_init(); 831 if (nm == NULL) { 832 rc = -1; 833 goto cleanup; 834 } 835 nm->is_user = is_user; 836 837 838 stat = idmap_iter_next_mapping(ihandle, 839 &nm->sidprefix, &nm->rid, &nm->pid, 840 &nm->winname, &nm->windomain, 841 &nm->unixname, &nm->direction); 842 843 if (stat >= 0) 844 (void) print_mapping(nm); 845 846 name_mapping_fini(nm); 847 848 } while (stat > 0); 849 850 /* IDMAP_ERR_NOTFOUND indicates end of the list */ 851 if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) { 852 (void) fprintf(stderr, 853 gettext("Error during iteration (%s)\n"), 854 idmap_stat2string(handle, stat)); 855 rc = -1; 856 goto cleanup; 857 } 858 859 idmap_iter_destroy(ihandle); 860 } 861 cleanup: 862 (void) print_mapping_fini(); 863 fini_command(); 864 return (rc); 865 } 866 867 /* 868 * The same as strdup, but length chars is duplicated, no matter on 869 * '\0'. The caller must guarantee "length" chars in "from". 870 */ 871 static char * 872 strndup(char *from, size_t length) { 873 char *out = (char *)malloc((length + 1) * sizeof (char)); 874 if (out == NULL) { 875 (void) fprintf(stderr, gettext("Not enough memory\n")); 876 return (NULL); 877 } 878 (void) strncpy(out, from, length); 879 out[length] = '\0'; 880 return (out); 881 } 882 883 /* Does line start with USERMAP_CFG IP qualifier? */ 884 static int 885 ucp_is_IP_qualifier(char *line) { 886 char *it; 887 it = line + strcspn(line, " \t\n#:"); 888 return (*(it + 1) == ':' ? 1 : 0); 889 } 890 891 892 /* 893 * returns interior of quotation marks in USERMAP_CFG. In this format, 894 * there cannot be a protected quotation mark inside. 895 */ 896 static char * 897 ucp_qm_interior(char **line, int line_num) { 898 char *out; 899 char *qm = strchr(*line + 1, '"'); 900 if (qm == NULL) { 901 (void) fprintf(stderr, 902 gettext("Line %d: Unclosed quotations\n"), 903 line_num); 904 return (NULL); 905 } 906 907 out = strndup(*line + 1, qm - *line - 1); 908 *line = qm + 1; 909 return (out); 910 } 911 912 /* 913 * Grab next token from the line in USERMAP_CFG format. terminators, 914 * the 3rd parameter, contains all the characters which can terminate 915 * the token. line_num is the line number of input used for error 916 * reporting. 917 */ 918 static char * 919 ucp_grab_token(char **line, int line_num, const char *terminators) { 920 char *token; 921 if (**line == '"') 922 token = ucp_qm_interior(line, line_num); 923 else { 924 int length = strcspn(*line, terminators); 925 token = strndup(*line, length); 926 *line += length; 927 } 928 929 return (token); 930 } 931 932 933 /* 934 * Convert a line in usermap.cfg format to name_mapping. line_num is 935 * the line number of input used for error reporting. 936 * 937 * Return values: -1 for error, 0 for empty line, 1 for a mapping 938 * found. 939 */ 940 static int 941 ucp_line2nm(char *line, int line_num, name_mapping_t *nm) { 942 char *it; 943 char *token; 944 char *token2; 945 char separator; 946 int is_direction = 0; 947 948 it = line + strspn(line, " \t\n"); 949 950 /* empty or comment lines are OK: */ 951 if (*it == '\0' || *it == '#') 952 return (0); 953 954 /* We do not support network qualifiers */ 955 if (ucp_is_IP_qualifier(it)) { 956 (void) fprintf(stderr, 957 gettext("Line %d: unable to handle network qualifier.\n"), 958 line_num); 959 return (-1); 960 } 961 962 /* The windows name: */ 963 token = ucp_grab_token(&it, line_num, " \t#\\\n@=<"); 964 if (token == NULL) 965 return (-1); 966 967 separator = *it; 968 969 /* Didn't we bump to the end of line? */ 970 if (separator == '\0' || separator == '#') { 971 free(token); 972 (void) fprintf(stderr, 973 gettext("Line %d: UNIX_name not found.\n"), 974 line_num); 975 return (-1); 976 } 977 978 /* Do we have a domainname? */ 979 if (separator == '\\' || separator == '@') { 980 it ++; 981 token2 = ucp_grab_token(&it, line_num, " \t\n#"); 982 if (token2 == NULL) { 983 free(token); 984 return (-1); 985 } else if (*it == '\0' || *it == '#') { 986 free(token); 987 free(token2); 988 (void) fprintf(stderr, 989 gettext("Line %d: UNIX_name not found.\n"), 990 line_num); 991 } 992 993 if (separator == '\\') { 994 nm->windomain = token; 995 nm->winname = token2; 996 nm->is_nt4 = 1; 997 } else { 998 nm->windomain = token2; 999 nm->winname = token; 1000 nm->is_nt4 = 0; 1001 1002 } 1003 } else { 1004 nm->windomain = NULL; 1005 nm->winname = token; 1006 nm->is_nt4 = 0; 1007 } 1008 1009 1010 it = it + strspn(it, " \t\n"); 1011 1012 /* Direction string is optional: */ 1013 if (strncmp(it, "==", 2) == 0) { 1014 nm->direction = DIR_BI; 1015 is_direction = 1; 1016 } else if (strncmp(it, "<=", 2) == 0) { 1017 nm->direction = DIR_U2W; 1018 is_direction = 1; 1019 } else if (strncmp(it, "=>", 2) == 0) { 1020 nm->direction = DIR_W2U; 1021 is_direction = 1; 1022 } else { 1023 nm->direction = DIR_BI; 1024 is_direction = 0; 1025 } 1026 1027 if (is_direction) { 1028 it += 2; 1029 it += strspn(it, " \t\n"); 1030 1031 if (*it == '\0' || *it == '#') { 1032 (void) fprintf(stderr, 1033 gettext("Line %d: UNIX_name not found.\n"), 1034 line_num); 1035 return (-1); 1036 } 1037 } 1038 1039 /* Now unixname: */ 1040 it += strspn(it, " \t\n"); 1041 token = ucp_grab_token(&it, line_num, " \t\n#"); 1042 1043 if (token == NULL) 1044 /* nm->winname to be freed by name_mapping_fini */ 1045 return (-1); 1046 1047 /* Neither here we support IP qualifiers */ 1048 if (ucp_is_IP_qualifier(token)) { 1049 (void) fprintf(stderr, 1050 gettext("Line %d: unable to handle network qualifier.\n"), 1051 line_num); 1052 free(token); 1053 return (-1); 1054 } 1055 1056 nm->unixname = token; 1057 1058 it += strspn(it, " \t\n"); 1059 1060 /* Does something remain on the line */ 1061 if (*it != '\0' && *it != '#') { 1062 (void) fprintf(stderr, 1063 gettext("Line %d: unrecognized parameters \"%s\".\n"), 1064 line_num, it); 1065 return (-1); 1066 } 1067 1068 return (1); 1069 } 1070 1071 /* 1072 * Parse SMBUSERS line to name_mapping_t. if line is NULL, then 1073 * pasrsing of the previous line is continued. line_num is input line 1074 * number used for error reporting. 1075 * Return values: 1076 * rc -1: error 1077 * rc = 0: mapping found and the line is finished, 1078 * rc = 1: mapping found and there remains other on the line 1079 */ 1080 static int 1081 sup_line2nm(char *line, int line_num, name_mapping_t *nm) { 1082 static char *ll = NULL; 1083 static char *unixname = NULL; 1084 static size_t unixname_l = 0; 1085 char *token; 1086 1087 if (line != NULL) { 1088 ll = line; 1089 1090 unixname = ll += strspn(ll, " \t"); 1091 if (*ll == '\0' || *ll == '#') 1092 return (0); 1093 1094 unixname_l = strcspn(ll, " \t:=#\n"); 1095 ll += unixname_l; 1096 1097 if (*ll == '\0'|| *ll == '#') 1098 return (0); 1099 1100 ll += strspn(ll, " \t:=#\n"); 1101 1102 } 1103 1104 if (*ll == '\0'|| *ll == '#') 1105 return (0); 1106 1107 token = ucp_grab_token(&ll, line_num, " \t\n"); 1108 if (token == NULL) 1109 return (-1); 1110 1111 nm->is_nt4 = 0; 1112 nm->direction = DIR_W2U; 1113 1114 nm->windomain = NULL; 1115 nm->winname = token; 1116 nm->unixname = strndup(unixname, unixname_l); 1117 if (nm->unixname == NULL) 1118 return (-1); 1119 1120 ll += strspn(ll, " \t\n"); 1121 return (1); 1122 } 1123 1124 /* Parse line to name_mapping_t. Basicaly just a format switch. */ 1125 static int 1126 line2nm(char *line, int line_num, name_mapping_t *nm, format_t f) { 1127 switch (f) { 1128 case USERMAP_CFG: 1129 if (line == NULL) 1130 return (0); 1131 else 1132 return (ucp_line2nm(line, line_num, nm)); 1133 case SMBUSERS: 1134 return (sup_line2nm(line, line_num, nm)); 1135 default: 1136 (void) fprintf(stderr, gettext("Internal error.\n")); 1137 } 1138 1139 return (-1); 1140 } 1141 1142 1143 /* Examine -f flag and return the appropriate format_t */ 1144 static format_t 1145 ff2format(char *ff, int is_mandatory) { 1146 1147 if (ff == NULL && is_mandatory) { 1148 (void) fprintf(stderr, gettext("Format not given.\n")); 1149 return (UNDEFINED_FORMAT); 1150 } 1151 1152 if (ff == NULL) 1153 return (DEFAULT_FORMAT); 1154 1155 if (strcasecmp(ff, "usermap.cfg") == 0) 1156 return (USERMAP_CFG); 1157 1158 if (strcasecmp(ff, "smbusers") == 0) 1159 return (SMBUSERS); 1160 1161 (void) fprintf(stderr, 1162 gettext("The only known formats are: \"usermap.cfg\" and " 1163 "\"smbusers\".\n")); 1164 return (UNDEFINED_FORMAT); 1165 } 1166 1167 /* Delete all namerules of the given type */ 1168 static int 1169 flush_nm(boolean_t is_user) 1170 { 1171 idmap_stat stat; 1172 1173 stat = idmap_udt_flush_namerules(udt, is_user); 1174 if (stat < 0) { 1175 (void) fprintf(stderr, 1176 is_user ? gettext("Unable to flush users (%s).\n") 1177 : gettext("Unable to flush groups (%s).\n"), 1178 idmap_stat2string(handle, stat)); 1179 return (-1); 1180 } 1181 return (0); 1182 } 1183 1184 /* import command handler */ 1185 static int 1186 /* LINTED E_FUNC_ARG_UNUSED */ 1187 do_import(flag_t *f, int argc, char **argv) 1188 { 1189 name_mapping_t *nm; 1190 char line[MAX_INPUT_LINE_SZ]; 1191 format_t format; 1192 int rc = 0; 1193 idmap_stat stat; 1194 int line_num; 1195 FILE *file = NULL; 1196 1197 if (batch_mode) { 1198 (void) fprintf(stderr, 1199 gettext("Import is not allowed in the batch mode.\n")); 1200 return (-1); 1201 } 1202 1203 format = ff2format(argv[0], 1); 1204 if (format == UNDEFINED_FORMAT) 1205 return (-1); 1206 1207 if (init_udt_command()) 1208 return (-1); 1209 1210 /* We don't flush groups in the usermap.cfg nor smbusers format */ 1211 if (f[F_FLAG] != NULL && 1212 flush_nm(B_TRUE) < 0 && 1213 (format == USERMAP_CFG || format == SMBUSERS || 1214 flush_nm(B_FALSE) < 0)) { 1215 rc = -1; 1216 goto cleanup; 1217 } 1218 1219 line_num = 0; 1220 1221 /* Where we import from? */ 1222 if (f[f_FLAG] == NULL) 1223 file = stdin; 1224 else { 1225 file = fopen(f[f_FLAG], "r"); 1226 if (file == NULL) { 1227 perror(f[f_FLAG]); 1228 goto cleanup; 1229 } 1230 } 1231 1232 1233 while (fgets(line, MAX_INPUT_LINE_SZ, file)) { 1234 char *line2 = line; 1235 line_num++; 1236 1237 /* 1238 * In SMBUSERS format there can be more mappings on 1239 * each line. So we need the internal cycle for each line. 1240 */ 1241 do { 1242 nm = name_mapping_init(); 1243 if (nm == NULL) { 1244 rc = -1; 1245 goto cleanup; 1246 } 1247 1248 rc = line2nm(line2, line_num, nm, format); 1249 line2 = NULL; 1250 1251 if (rc < 1) { 1252 name_mapping_fini(nm); 1253 break; 1254 } 1255 1256 stat = idmap_udt_add_namerule(udt, nm->windomain, 1257 nm->is_user ? B_TRUE : B_FALSE, nm->winname, 1258 nm->unixname, nm->is_nt4, nm->direction); 1259 if (stat < 0) { 1260 (void) fprintf(stderr, 1261 gettext("Transaction error (%s)\n"), 1262 idmap_stat2string(handle, stat)); 1263 rc = -1; 1264 } 1265 1266 name_mapping_fini(nm); 1267 1268 } while (rc >= 0); 1269 1270 if (rc < 0) { 1271 (void) fprintf(stderr, 1272 gettext("Import canceled.\n")); 1273 break; 1274 } 1275 } 1276 1277 cleanup: 1278 fini_udt_command(rc < 0 ? 0 : 1); 1279 if (file != NULL && file != stdin) 1280 (void) fclose(file); 1281 return (rc); 1282 } 1283 1284 1285 /* 1286 * List name mappings in the format specified. list_users / 1287 * list_groups determine which type to list. The output goes to the 1288 * file fi. 1289 */ 1290 static int 1291 list_name_mappings(int list_users, int list_groups, format_t format, FILE *fi) 1292 { 1293 idmap_stat stat; 1294 idmap_iter_t *ihandle; 1295 name_mapping_t *nm; 1296 int is_user; 1297 1298 for (is_user = I_YES; is_user >= I_NO; is_user--) { 1299 if (is_user && !list_users) 1300 continue; 1301 if (!is_user && !list_groups) 1302 continue; 1303 /* Only users can be in USERMAP_CFG format, not a group */ 1304 if (!is_user && format == USERMAP_CFG) 1305 continue; 1306 1307 stat = idmap_iter_namerules(handle, NULL, is_user, NULL, 1308 NULL, &ihandle); 1309 if (stat < 0) { 1310 (void) fprintf(stderr, 1311 gettext("Iteration handle not obtained (%s)\n"), 1312 idmap_stat2string(handle, stat)); 1313 idmap_iter_destroy(ihandle); 1314 return (-1); 1315 } 1316 1317 (void) print_mapping_init(format, fi); 1318 1319 do { 1320 nm = name_mapping_init(); 1321 if (nm == NULL) { 1322 idmap_iter_destroy(ihandle); 1323 return (-1); 1324 } 1325 1326 stat = idmap_iter_next_namerule(ihandle, &nm->windomain, 1327 &nm->winname, &nm->unixname, &nm->is_nt4, 1328 &nm->direction); 1329 if (stat >= 0) { 1330 nm->is_user = is_user; 1331 (void) print_mapping(nm); 1332 } 1333 1334 name_mapping_fini(nm); 1335 1336 } while (stat > 0); 1337 1338 (void) print_mapping_fini(); 1339 1340 if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) { 1341 (void) fprintf(stderr, 1342 gettext("Error during iteration (%s)\n"), 1343 idmap_stat2string(handle, stat)); 1344 idmap_iter_destroy(ihandle); 1345 return (-1); 1346 } 1347 1348 idmap_iter_destroy(ihandle); 1349 } 1350 return (0); 1351 } 1352 1353 /* Export command handler */ 1354 static int 1355 /* LINTED E_FUNC_ARG_UNUSED */ 1356 do_export(flag_t *f, int argc, char **argv) { 1357 int rc; 1358 format_t format; 1359 FILE *fi; 1360 1361 format = ff2format(argv[0], 1); 1362 if (format == UNDEFINED_FORMAT) 1363 return (-1); 1364 1365 /* Where do we output to? */ 1366 if (f[f_FLAG] == NULL) 1367 fi = stdout; 1368 else { 1369 fi = fopen(f[f_FLAG], "w"); 1370 if (fi == NULL) { 1371 perror(f[f_FLAG]); 1372 return (-1); 1373 } 1374 } 1375 1376 if (init_command() < 0) { 1377 rc = -1; 1378 goto cleanup; 1379 } 1380 1381 /* List the requested types: */ 1382 rc = list_name_mappings(is_user_wanted(f), 1383 is_group_wanted(f), 1384 format, 1385 fi); 1386 1387 fini_command(); 1388 1389 cleanup: 1390 if (fi != NULL && fi != stdout) 1391 (void) fclose(fi); 1392 return (rc); 1393 } 1394 1395 /* List command handler */ 1396 static int 1397 /* LINTED E_FUNC_ARG_UNUSED */ 1398 do_list_name_mappings(flag_t *f, int argc, char **argv) 1399 { 1400 int rc; 1401 1402 if (init_command()) { 1403 return (-1); 1404 } 1405 1406 /* List the requested types: */ 1407 rc = list_name_mappings(is_user_wanted(f), 1408 is_group_wanted(f), 1409 DEFAULT_FORMAT, 1410 stdout); 1411 1412 fini_command(); 1413 return (rc); 1414 } 1415 1416 /* This is just a debug function for dumping flags */ 1417 static void 1418 print_flags(flag_t *f) 1419 { 1420 int c; 1421 for (c = 0; c < FLAG_ALPHABET_SIZE; c++) { 1422 if (f[c] == FLAG_SET) 1423 (void) printf("FLAG: -%c, VALUE: %p\n", c, 1424 (void *) f[c]); 1425 else if (f[c]) 1426 (void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]); 1427 } 1428 } 1429 1430 /* 1431 * Compare two strings just like strcmp, but stop before the end of 1432 * the s2 1433 */ 1434 static int 1435 strcmp_no0(const char *s1, const char *s2) { 1436 return (strncmp(s1, s2, strlen(s2))); 1437 } 1438 1439 /* The same as strcmp_no0, but case insensitive. */ 1440 static int 1441 strcasecmp_no0(const char *s1, const char *s2) { 1442 return (strncasecmp(s1, s2, strlen(s2))); 1443 } 1444 1445 /* 1446 * This function splits name to the relevant pieces: is_user, winname, 1447 * windomain unixname. Sometimes it is not possible to determine OS 1448 * side, because it could be determined by the opposite name in idmap 1449 * show. So this function must be called several times. 1450 * 1451 * Return values: -1 ... clear syntax error 1452 * 0 ... it wasnt possible to determine 1453 * 1 ... determined 1454 */ 1455 static int 1456 name2parts(char *name, name_mapping_t *nm) { 1457 char *it; 1458 int is_win = I_NO; 1459 int is_unix = I_NO; 1460 1461 if (nm->winname != NULL && nm->unixname != NULL) 1462 return (0); 1463 1464 /* If it starts with type string, that is easy: */ 1465 if (it = strchr(name, ':')) { 1466 if (strcmp_no0(name, ID_UNIXNAME ":") == 0) { 1467 if (nm->unixname != NULL) 1468 return (0); 1469 is_unix = I_YES; 1470 } else if (strcmp_no0(name, ID_WINNAME ":") == 0) { 1471 if (nm->winname != NULL) 1472 return (0); 1473 is_win = I_YES; 1474 } else { 1475 (void) fprintf(stderr, 1476 gettext("Error: invalid identity type\n")); 1477 return (-1); 1478 } 1479 name = it + 1; 1480 } 1481 1482 /* If it contains '@' or '\\', then it is a winname with domain */ 1483 if (!is_unix && nm->winname == NULL) { 1484 if ((it = strchr(name, '@')) != NULL) { 1485 int length = it-name+1; 1486 nm->winname = (char *)malloc(length * sizeof (char)); 1487 (void) strncpy(nm->winname, name, length - 1); 1488 nm->winname[length - 1] = '\0'; 1489 nm->windomain = strdup(it + 1); 1490 return (1); 1491 } else if ((it = strrchr(name, '\\')) != NULL) { 1492 int length = it-name+1; 1493 nm->windomain = (char *)malloc(length * sizeof (char)); 1494 (void) strncpy(nm->windomain, name, length - 1); 1495 nm->windomain[length - 1] = '\0'; 1496 nm->winname = strdup(it + 1); 1497 nm->is_nt4 = B_TRUE; 1498 return (1); 1499 } 1500 } 1501 1502 /* 1503 * if is_unix/is_win is not yet determined, then the last 1504 * hope is that the opposite side is known already. In that 1505 * case, it is the only remaining side. 1506 */ 1507 if (is_unix || nm->unixname == NULL && nm->winname != NULL) { 1508 if (strlen(name) == 0) 1509 nm->unixname = strdup("\"\""); 1510 else 1511 nm->unixname = strdup(name); 1512 return (1); 1513 } else if (is_win || nm->unixname != NULL && nm->winname == NULL) { 1514 if (strlen(name) == 0) 1515 nm->winname = strdup("\"\""); 1516 else 1517 nm->winname = strdup(name); 1518 nm->windomain = NULL; 1519 return (1); 1520 } 1521 1522 return (0); 1523 } 1524 1525 /* add command handler. */ 1526 static int 1527 do_add_name_mapping(flag_t *f, int argc, char **argv) 1528 { 1529 name_mapping_t *nm; 1530 int rc = 0; 1531 int i; 1532 int is_argv0_unix = -1; 1533 idmap_stat stat; 1534 1535 1536 /* Two arguments and exactly one of -u, -g must be specified */ 1537 if (argc < 2) { 1538 (void) fprintf(stderr, gettext("Not enough arguments.\n")); 1539 return (-1); 1540 } else if (argc > 2) { 1541 (void) fprintf(stderr, gettext("Too many arguments.\n")); 1542 return (-1); 1543 } else if (!is_type_determined(f)) 1544 return (-1); 1545 1546 /* 1547 * Direction can be determined by the opposite name, so we 1548 * need to run name2parts twice for the first name, i.e. 3x in 1549 * total. 1550 */ 1551 nm = name_mapping_init(); 1552 if (nm == NULL) 1553 return (-1); 1554 1555 nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO; 1556 1557 for (i = 0; i < 3; i++) { 1558 switch (name2parts(argv[i % 2], nm)) { 1559 case -1: 1560 name_mapping_fini(nm); 1561 return (-1); 1562 case 1: 1563 if (is_argv0_unix < 0) 1564 is_argv0_unix = 1565 i % 2 ^ (nm->unixname != NULL ? 1 : 0); 1566 break; 1567 } 1568 } 1569 1570 if (nm->winname == NULL || nm->unixname == NULL) { 1571 (void) fprintf(stderr, gettext("Name types not determined.\n")); 1572 name_mapping_fini(nm); 1573 return (-1); 1574 } 1575 1576 if (f[d_FLAG] != NULL) 1577 nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U; 1578 else 1579 nm->direction = DIR_BI; 1580 1581 /* Now let us write it: */ 1582 1583 if (init_udt_command()) { 1584 name_mapping_fini(nm); 1585 return (-1); 1586 } 1587 1588 stat = idmap_udt_add_namerule(udt, nm->windomain, 1589 nm->is_user ? B_TRUE : B_FALSE, nm->winname, nm->unixname, 1590 nm->is_nt4, nm->direction); 1591 1592 /* We echo the mapping */ 1593 (void) print_mapping_init(DEFAULT_FORMAT, stdout); 1594 (void) print_mapping(nm); 1595 (void) print_mapping_fini(); 1596 1597 if (stat < 0) { 1598 (void) fprintf(stderr, 1599 gettext("Mapping not created (%s)\n"), 1600 idmap_stat2string(handle, stat)); 1601 rc = -1; 1602 } 1603 1604 cleanup: 1605 name_mapping_fini(nm); 1606 fini_udt_command(1); 1607 return (rc); 1608 } 1609 1610 /* remove command handler */ 1611 static int 1612 do_remove_name_mapping(flag_t *f, int argc, char **argv) 1613 { 1614 name_mapping_t *nm; 1615 int rc = 0; 1616 int i; 1617 int is_argv0_unix = -1; 1618 idmap_stat stat; 1619 1620 /* "-a" means we flush all of them */ 1621 if (f[a_FLAG] != NULL) { 1622 if (argc) { 1623 (void) fprintf(stderr, 1624 gettext("Too many arguments.\n")); 1625 return (-1); 1626 } 1627 1628 if (!is_type_determined(f)) 1629 return (-1); 1630 1631 if (init_udt_command()) 1632 return (-1); 1633 rc = flush_nm(f[u_FLAG] != NULL ? B_TRUE : B_FALSE); 1634 1635 fini_udt_command(rc ? 0 : 1); 1636 return (rc); 1637 } 1638 1639 /* Contrary to add_name_mapping, we can have only one argument */ 1640 if (argc < 1) { 1641 (void) fprintf(stderr, gettext("Not enough arguments.\n")); 1642 return (-1); 1643 } else if (argc > 2) { 1644 (void) fprintf(stderr, gettext("Too many arguments.\n")); 1645 return (-1); 1646 } else if (!is_type_determined(f)) { 1647 return (-1); 1648 } else if ( 1649 /* both -f and -t: */ 1650 f[f_FLAG] != NULL && f[t_FLAG] != NULL || 1651 /* -d with a single argument: */ 1652 argc == 1 && f[d_FLAG] != NULL || 1653 /* -f or -t with two arguments: */ 1654 argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) { 1655 (void) fprintf(stderr, 1656 gettext("Direction ambiguous.\n")); 1657 return (-1); 1658 } 1659 1660 1661 /* 1662 * Similar to do_add_name_mapping - see the comments 1663 * there. Except we may have only one argument here. 1664 */ 1665 nm = name_mapping_init(); 1666 if (nm == NULL) 1667 return (-1); 1668 1669 nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO; 1670 1671 for (i = 0; i < 2 * argc - 1; i++) { 1672 switch (name2parts(argv[i % 2], nm)) { 1673 case -1: 1674 name_mapping_fini(nm); 1675 return (-1); 1676 case 1: 1677 if (is_argv0_unix < 0) 1678 is_argv0_unix = i % 2 ^ (nm->unixname ? 1 : 0); 1679 break; 1680 } 1681 } 1682 1683 1684 if (nm->winname == NULL && nm->unixname == NULL) { 1685 (void) fprintf(stderr, gettext("Name types not determined.\n")); 1686 name_mapping_fini(nm); 1687 return (-1); 1688 } 1689 1690 /* 1691 * If the direction is not specified by a -d/-f/-t flag, then it 1692 * is DIR_UNKNOWN, because in that case we want to remove any 1693 * mapping. If it was DIR_BI, idmap_api would delete a 1694 * bidirectional one only. 1695 */ 1696 if (f[d_FLAG] != NULL || f[f_FLAG] != NULL) 1697 nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U; 1698 else if (f[t_FLAG] != NULL) 1699 nm->direction = is_argv0_unix ? DIR_W2U : DIR_U2W; 1700 else 1701 nm->direction = DIR_UNKNOWN; 1702 1703 if (init_udt_command()) { 1704 name_mapping_fini(nm); 1705 return (-1); 1706 } 1707 1708 stat = idmap_udt_rm_namerule(udt, nm->is_user ? B_TRUE : B_FALSE, 1709 nm->windomain, nm->winname, nm->unixname, nm->direction); 1710 1711 if (stat < 0) { 1712 (void) fprintf(stderr, 1713 gettext("Mapping not deleted (%s)\n"), 1714 idmap_stat2string(handle, stat)); 1715 rc = -1; 1716 } 1717 1718 cleanup: 1719 name_mapping_fini(nm); 1720 fini_udt_command(1); 1721 return (rc); 1722 } 1723 1724 1725 /* exit command handler */ 1726 static int 1727 /* LINTED E_FUNC_ARG_UNUSED */ 1728 do_exit(flag_t *f, int argc, char **argv) { 1729 return (0); 1730 } 1731 1732 1733 /* debug command handler: just print the parameters */ 1734 static int 1735 /* LINTED E_STATIC_UNUSED */ 1736 debug_print_params(flag_t *f, int argc, char **argv) 1737 { 1738 int i; 1739 #if 0 1740 char *leaktest = (char *)malloc(100); 1741 #endif 1742 1743 print_flags(f); 1744 1745 for (i = 0; i < argc; i++) { 1746 (void) printf("Argument %d: %s\n", i, argv[i]); 1747 } 1748 1749 (void) fflush(stdout); 1750 return (0); 1751 } 1752 1753 /* 1754 * Return a pointer after a given prefix. If there is no such prefix, 1755 * return NULL 1756 */ 1757 static char * 1758 get_root(char *string, char *typestring) { 1759 if (strcasecmp_no0(string, typestring) != 0) 1760 return (NULL); 1761 return (string + strlen(typestring)); 1762 } 1763 1764 /* 1765 * From name_mapping_t, asseble a string containing identity of the 1766 * given type. 1767 */ 1768 static int 1769 nm2type(name_mapping_t *nm, int type, char **to) { 1770 switch (type) { 1771 case TYPE_SID: 1772 if (nm->sidprefix == NULL) 1773 return (-1); 1774 *to = sid_format(nm->sidprefix, nm->rid); 1775 return (0); 1776 case TYPE_WN: 1777 return (nm2winqn(nm, to)); 1778 case TYPE_UID: 1779 case TYPE_GID: 1780 case TYPE_PID: 1781 *to = pid_format(nm->pid, nm->is_user); 1782 if (*to == NULL) 1783 return (-1); 1784 else 1785 return (0); 1786 case TYPE_UN: 1787 return (nm2unixname(nm, to)); 1788 default: 1789 (void) fprintf(stderr, gettext("Internal error.\n")); 1790 return (-1); 1791 } 1792 /* never reached */ 1793 } 1794 1795 /* show command handler */ 1796 static int 1797 do_show_mapping(flag_t *f, int argc, char **argv) 1798 { 1799 idmap_stat stat = 0; 1800 int flag; 1801 idmap_stat map_stat = 0; 1802 int type_from; 1803 int type_to; 1804 char *root; 1805 name_mapping_t *nm = NULL; 1806 char *fromname; 1807 char *toname; 1808 1809 if (argc == 0) { 1810 (void) fprintf(stderr, 1811 gettext("No identity given\n")); 1812 return (-1); 1813 } else if (argc > 2) { 1814 (void) fprintf(stderr, 1815 gettext("Too many arguments.\n")); 1816 return (-1); 1817 } 1818 1819 flag = f[c_FLAG] != NULL ? 0 : IDMAP_REQ_FLG_NO_NEW_ID_ALLOC; 1820 1821 if (init_command()) 1822 return (-1); 1823 1824 nm = name_mapping_init(); 1825 if (nm == NULL) 1826 goto cleanup; 1827 1828 /* First, determine type_from: */ 1829 if ((root = get_root(argv[0], ID_UID ":")) != NULL) 1830 type_from = TYPE_UID; 1831 else if ((root = get_root(argv[0], ID_GID ":")) != NULL) 1832 type_from = TYPE_GID; 1833 else if ((root = get_root(argv[0], ID_SID ":")) != NULL) 1834 type_from = TYPE_SID; 1835 else if (name2parts(argv[0], nm) > 0) { 1836 if (nm->unixname != NULL) 1837 type_from = TYPE_UN; 1838 else 1839 type_from = TYPE_WN; 1840 } else { 1841 (void) fprintf(stderr, 1842 gettext("Invalid type.\n")); 1843 stat = IDMAP_ERR_ARG; 1844 goto cleanup; 1845 } 1846 1847 /* Second, determine type_to: */ 1848 if (argc < 2) { 1849 type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID; 1850 if (type_from & IS_NAME) 1851 type_to |= IS_NAME; 1852 } else if (strcasecmp(argv[1], ID_UID) == 0) 1853 type_to = TYPE_UID; 1854 else if (strcasecmp(argv[1], ID_GID) == 0) 1855 type_to = TYPE_GID; 1856 else if (strcasecmp(argv[1], ID_SID) == 0) 1857 type_to = TYPE_SID; 1858 else if (strcmp(argv[1], ID_UNIXNAME) == 0) 1859 type_to = TYPE_UN; 1860 else if (strcmp(argv[1], ID_WINNAME) == 0) 1861 type_to = TYPE_WN; 1862 else { 1863 (void) fprintf(stderr, 1864 gettext("Ivnalid target type.\n")); 1865 stat = IDMAP_ERR_ARG; 1866 goto cleanup; 1867 } 1868 1869 /* Are both arguments the same OS side? */ 1870 if (!(type_from & IS_WIN ^ type_to & IS_WIN)) { 1871 (void) fprintf(stderr, 1872 gettext("Direction ambiguous.\n")); 1873 stat = IDMAP_ERR_ARG; 1874 goto cleanup; 1875 } 1876 1877 if (type_from == TYPE_SID) { 1878 char *p, *end, *sid; 1879 sid = argv[0] + 4; 1880 if ((p = strrchr(sid, '-')) == NULL) { 1881 (void) fprintf(stderr, 1882 gettext("Invalid SID %s\n"), sid); 1883 goto cleanup; 1884 } 1885 /* Replace '-' by string terminator so that sid = sidprefix */ 1886 *p = 0; 1887 nm->sidprefix = strdup(sid); 1888 nm->rid = strtoll(p + 1, &end, 10); 1889 /* Restore '-' */ 1890 *p = '-'; 1891 1892 } else if (type_from == TYPE_UID || type_from == TYPE_GID) { 1893 nm->pid = (uid_t)atol(root); 1894 } 1895 1896 /* 1897 * We have two interfaces for retrieving the mappings: 1898 * idmap_get_sidbyuid & comp (the batch interface) and 1899 * idmap_get_w2u_mapping & comp. We want to use both of them, because 1900 * the former mimicks kernel interface better and the later offers the 1901 * string names. In the batch case, our batch has always size 1. 1902 */ 1903 if (type_from & IS_NAME || type_to & IS_NAME) { 1904 if (type_from & IS_WIN) { 1905 if (type_to == TYPE_UID) 1906 nm->is_user = I_YES; 1907 else if (type_to == TYPE_GID) 1908 nm->is_user = I_NO; 1909 1910 map_stat = idmap_get_w2u_mapping(handle, 1911 nm->sidprefix, 1912 &nm->rid, 1913 nm->winname, 1914 nm->windomain, 1915 flag, 1916 &nm->is_user, 1917 &nm->pid, 1918 &nm->unixname, 1919 &nm->direction); 1920 } else { 1921 if (type_from == TYPE_UID) 1922 nm->is_user = I_YES; 1923 else if (type_from == TYPE_GID) 1924 nm->is_user = I_NO; 1925 1926 map_stat = idmap_get_u2w_mapping(handle, 1927 &nm->pid, 1928 nm->unixname, 1929 flag, 1930 nm->is_user, 1931 &nm->sidprefix, 1932 &nm->rid, 1933 &nm->winname, 1934 &nm->windomain, 1935 &nm->direction); 1936 } 1937 1938 } else { 1939 /* batch handle */ 1940 idmap_get_handle_t *ghandle = NULL; 1941 /* To be passed to idmap_get_uidbysid */ 1942 gid_t gid = UNDEFINED_GID; 1943 /* To be passed to idmap_get_gidbysid */ 1944 uid_t uid = UNDEFINED_UID; 1945 1946 1947 /* Create an in-memory structure for all the batch: */ 1948 stat = idmap_get_create(handle, &ghandle); 1949 if (stat < 0) { 1950 (void) fprintf(stderr, 1951 gettext("Unable to create handle for communicating" 1952 " with idmapd(1M) (%s)\n"), 1953 idmap_stat2string(handle, stat)); 1954 idmap_get_destroy(ghandle); 1955 goto cleanup; 1956 } 1957 1958 /* Schedule the request: */ 1959 if (type_from == TYPE_SID && type_to == TYPE_UID) { 1960 stat = idmap_get_uidbysid(ghandle, 1961 nm->sidprefix, 1962 nm->rid, 1963 flag, 1964 &uid, 1965 &map_stat); 1966 nm->is_user = I_YES; 1967 } else if (type_from == TYPE_SID && type_to == TYPE_GID) { 1968 stat = idmap_get_gidbysid(ghandle, 1969 nm->sidprefix, 1970 nm->rid, 1971 flag, 1972 &gid, 1973 &map_stat); 1974 nm->is_user = I_NO; 1975 } else if (type_from == TYPE_SID && type_to == TYPE_PID) 1976 stat = idmap_get_pidbysid(ghandle, 1977 nm->sidprefix, 1978 nm->rid, 1979 flag, 1980 &nm->pid, 1981 &nm->is_user, 1982 &map_stat); 1983 else if (type_from == TYPE_UID && type_to == TYPE_SID) { 1984 stat = idmap_get_sidbyuid(ghandle, 1985 nm->pid, 1986 flag, 1987 &nm->sidprefix, 1988 &nm->rid, 1989 &map_stat); 1990 nm->is_user = I_YES; 1991 } else if (type_from == TYPE_GID && type_to == TYPE_SID) { 1992 stat = idmap_get_sidbygid(ghandle, 1993 (gid_t)nm->pid, 1994 flag, 1995 &nm->sidprefix, 1996 &nm->rid, 1997 &map_stat); 1998 nm->is_user = I_NO; 1999 } else { 2000 (void) fprintf(stderr, gettext("Internal error.\n")); 2001 exit(1); 2002 } 2003 2004 if (stat < 0) { 2005 (void) fprintf(stderr, 2006 gettext("Request for %.3s not sent (%s)\n"), 2007 argv[0], idmap_stat2string(handle, stat)); 2008 idmap_get_destroy(ghandle); 2009 goto cleanup; 2010 } 2011 2012 /* Send the batch to idmapd and obtain results: */ 2013 stat = idmap_get_mappings(ghandle); 2014 if (stat < 0) { 2015 (void) fprintf(stderr, 2016 gettext("Mappings not obtained because of" 2017 " RPC problem (%s)\n"), 2018 idmap_stat2string(handle, stat)); 2019 idmap_get_destroy(ghandle); 2020 goto cleanup; 2021 } 2022 2023 /* Destroy the batch handle: */ 2024 idmap_get_destroy(ghandle); 2025 2026 if (type_to == TYPE_UID) 2027 nm->pid = uid; 2028 else if (type_to == TYPE_GID) 2029 nm->pid = (uid_t)gid; 2030 2031 } 2032 2033 /* 2034 * If there was -c flag, we do output whatever we can even in 2035 * the case of error: 2036 */ 2037 if (map_stat < 0) { 2038 (void) fprintf(stderr, 2039 gettext("%s\n"), 2040 idmap_stat2string(handle, map_stat)); 2041 if (flag == IDMAP_REQ_FLG_NO_NEW_ID_ALLOC) 2042 goto cleanup; 2043 } 2044 2045 2046 if (nm2type(nm, type_from, &fromname) < 0) 2047 goto cleanup; 2048 2049 if (nm2type(nm, type_to, &toname) < 0) { 2050 if (flag == 0) 2051 (void) printf("%s -> %s:%u\n", 2052 fromname, 2053 (type_from | type_to) & IS_GROUP ? ID_GID : ID_UID, 2054 UID_NOBODY); 2055 free(fromname); 2056 goto cleanup; 2057 } 2058 2059 (void) printf("%s -> %s\n", fromname, toname); 2060 free(fromname); 2061 free(toname); 2062 2063 cleanup: 2064 if (nm != NULL) 2065 name_mapping_fini(nm); 2066 fini_command(); 2067 return (stat < 0 || map_stat < 0 ? -1 : 0); 2068 } 2069 2070 /* main function. Returns 1 for error, 0 otherwise */ 2071 int 2072 main(int argc, char *argv[]) { 2073 int rc; 2074 2075 /* set locale and domain for internationalization */ 2076 (void) setlocale(LC_ALL, ""); 2077 (void) textdomain(TEXT_DOMAIN); 2078 2079 /* idmap_engine determines the batch_mode: */ 2080 rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t), 2081 commands, 2082 argc - 1, 2083 argv + 1, 2084 &batch_mode); 2085 2086 if (rc < 0) { 2087 (void) engine_fini(); 2088 if (rc == IDMAP_ENG_ERROR_SILENT) 2089 help(); 2090 return (1); 2091 } 2092 2093 udt_used = 0; 2094 if (batch_mode) { 2095 if (init_udt_batch() < 0) 2096 return (1); 2097 } 2098 2099 rc = run_engine(argc - 1, argv + 1); 2100 2101 if (batch_mode) { 2102 batch_mode = 0; 2103 fini_udt_command(rc == 0 ? 1 : 0); 2104 } 2105 2106 (void) engine_fini(); 2107 return (rc == 0 ? 0 : 1); 2108 } 2109