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 <errno.h> 33 #include <limits.h> 34 #include <sys/varargs.h> 35 #include "idmap_engine.h" 36 #include "idmap_priv.h" 37 38 /* Initialization values for pids/rids: */ 39 40 #define UNDEFINED_UID (uid_t)-1 41 #define UNDEFINED_GID (gid_t)-1 42 #define UNDEFINED_RID (idmap_rid_t)-1; 43 44 /* is_user values */ 45 46 #define I_YES 1 47 #define I_NO 0 48 #define I_UNKNOWN -1 49 50 /* 51 * used in do_show for the type of argument, which can be winname, 52 * unixname, uid, gid, sid or not given at all: 53 */ 54 55 #define TYPE_SID 0x010 /* sid */ 56 #define TYPE_WN 0x110 /* winname */ 57 #define TYPE_WU 0x111 /* winuser */ 58 #define TYPE_WG 0x112 /* wingroup */ 59 #define TYPE_UID 0x001 /* uid */ 60 #define TYPE_GID 0x002 /* gid */ 61 #define TYPE_PID 0x000 /* pid */ 62 #define TYPE_UN 0x100 /* unixname */ 63 #define TYPE_UU 0x101 /* unixuser */ 64 #define TYPE_UG 0x102 /* unixgroup */ 65 66 #define IS_WIN 0x010 /* mask for the windows types */ 67 #define IS_NAME 0x100 /* mask for string name types */ 68 #define IS_USER 0x001 /* mask for user types */ 69 #define IS_GROUP 0x002 /* mask for group types */ 70 71 72 /* Identity type strings */ 73 74 #define ID_WINNAME "winname" 75 #define ID_UNIXNAME "unixname" 76 #define ID_UNIXUSER "unixuser" 77 #define ID_UNIXGROUP "unixgroup" 78 #define ID_WINUSER "winuser" 79 #define ID_WINGROUP "wingroup" 80 #define ID_SID "sid" 81 #define ID_UID "uid" 82 #define ID_GID "gid" 83 84 /* Flags */ 85 86 #define f_FLAG 'f' 87 #define t_FLAG 't' 88 #define d_FLAG 'd' 89 #define F_FLAG 'F' 90 #define a_FLAG 'a' 91 #define n_FLAG 'n' 92 #define c_FLAG 'c' 93 94 95 /* used in the function do_import */ 96 #define MAX_INPUT_LINE_SZ 2047 97 98 99 typedef struct { 100 int is_user; 101 int direction; 102 boolean_t is_nt4; 103 char *unixname; 104 char *winname; 105 char *windomain; 106 char *sidprefix; 107 idmap_rid_t rid; 108 uid_t pid; 109 } name_mapping_t; 110 111 /* 112 * Formats of the output: 113 * 114 * Idmap reads/prints mappings in several formats: ordinary mappings, 115 * name mappings in Samba username map format (smbusers), Netapp 116 * usermap.cfg. 117 * 118 * DEFAULT_FORMAT are in fact the idmap subcommands suitable for 119 * piping to idmap standart input. For example 120 * add -d winuser:bob@foo.com unixuser:fred 121 * add -d winuser:bob2bar.com unixuser:fred 122 * 123 * SMBUSERS is the format of Samba username map (smbusers). For full 124 * documentation, search for "username map" in smb.conf manpage. 125 * The format is for example 126 * fred = bob@foo.com bob2@bar.com 127 * 128 * USERMAP_CFG is the format of Netapp usermap.cfg file. Search 129 * http://www.netapp.com/ for more documentation. IP qualifiers are not 130 * supported. 131 * The format is for example 132 * bob@foo.com => fred 133 * "Bob With Spaces"@bar.com => fred #comment 134 * 135 * The previous formats were for name rules. MAPPING_NAME and 136 * MAPPING_ID are for the actual mappings, as seen in show/dump 137 * commands. MAPPING_NAME prefers the string names of the user over 138 * their numerical identificators. MAPPING_ID prints just the 139 * identificators. 140 * Example of the MAPPING_NAME: 141 * winname:bob@foo.com -> unixname:fred 142 * 143 * Example of the MAPPING_ID: 144 * sid:S-1-2-3-4 -> uid:5678 145 */ 146 147 typedef enum { 148 UNDEFINED_FORMAT = -1, 149 DEFAULT_FORMAT = 0, 150 MAPPING_ID, 151 MAPPING_NAME, 152 USERMAP_CFG, 153 SMBUSERS 154 } format_t; 155 156 /* Gives the format to use. Set in print_mapping_init */ 157 static format_t pnm_format; 158 159 /* The file for print_mapping_init output. Mostly just stdout. */ 160 static FILE *pnm_file; 161 162 /* In smbusers format, more unixnames can be aggregated to one line. */ 163 static char *pnm_last_unixname; 164 165 /* 166 * idmap_api batch related variables: 167 * 168 * idmap can operate in two modes. It the batch mode, the idmap_api 169 * batch is committed at the end of a batch of several 170 * commands. At the end of input file, typically. This mode is used 171 * for processing input from a file. 172 * In the non-batch mode, each command is committed immediately. This 173 * mode is used for tty input. 174 */ 175 176 /* Are we in the batch mode? */ 177 static int batch_mode = 0; 178 179 /* Self describing stricture for positions */ 180 struct pos_sds { 181 int size; 182 int last; 183 cmd_pos_t *pos[1]; 184 }; 185 186 static struct pos_sds *positions; 187 188 /* Handles for idmap_api batch */ 189 static idmap_handle_t *handle = NULL; 190 static idmap_udt_handle_t *udt = NULL; 191 192 /* Do we need to commit the udt batch at the end? */ 193 static int udt_used; 194 195 /* Command handlers */ 196 197 static int do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos); 198 static int do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos); 199 static int do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos); 200 static int do_list_name_mappings(flag_t *f, int argc, char **argv, 201 cmd_pos_t *pos); 202 static int do_add_name_mapping(flag_t *f, int argc, char **argv, 203 cmd_pos_t *pos); 204 static int do_remove_name_mapping(flag_t *f, int argc, char **argv, 205 cmd_pos_t *pos); 206 static int do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos); 207 static int do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos); 208 static int do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos); 209 210 /* Command names and their hanlers to be passed to idmap_engine */ 211 212 static cmd_ops_t commands[] = { 213 { 214 "show", 215 "c(create)", 216 do_show_mapping 217 }, 218 { 219 "dump", 220 "n(names)", 221 do_dump 222 }, 223 { 224 "import", 225 "F(flush)f:(file)", 226 do_import 227 }, 228 { 229 "export", 230 "f:(file)", 231 do_export 232 }, 233 { 234 "list", 235 "", 236 do_list_name_mappings 237 }, 238 { 239 "add", 240 "d(directional)", 241 do_add_name_mapping 242 }, 243 { 244 "remove", 245 "a(all)t(to)f(from)d(directional)", 246 do_remove_name_mapping 247 }, 248 { 249 "exit", 250 "", 251 do_exit 252 }, 253 { 254 "help", 255 "", 256 do_help 257 } 258 }; 259 260 /* Print error message, possibly with a position */ 261 /* printflike */ 262 static void 263 print_error(cmd_pos_t *pos, const char *format, ...) { 264 size_t length; 265 266 va_list ap; 267 268 va_start(ap, format); 269 270 if (pos != NULL) { 271 length = strlen(pos->line); 272 273 /* Skip newlines etc at the end: */ 274 while (length > 0 && isspace(pos->line[length - 1])) 275 length--; 276 277 (void) fprintf(stderr, 278 gettext("Error at line %d: %.*s\n"), 279 pos->linenum, 280 length, 281 pos->line); 282 } 283 (void) vfprintf(stderr, format, ap); 284 285 va_end(ap); 286 } 287 288 /* Inits positions sds. 0 means everything went OK, -1 for errors */ 289 static int 290 init_positions() { 291 int init_size = 32; /* Initial size of the positions array */ 292 293 positions = (struct pos_sds *) malloc(sizeof (struct pos_sds) + 294 (init_size - 1) * sizeof (cmd_pos_t *)); 295 296 if (positions == NULL) { 297 print_error(NULL, gettext("Not enough memory.\n")); 298 return (-1); 299 } 300 301 positions->size = init_size; 302 positions->last = 0; 303 return (0); 304 } 305 306 /* Free the positions array */ 307 static void 308 fini_positions() { 309 int i; 310 for (i = 0; i < positions->last; i++) { 311 if (positions->pos[i] == NULL) 312 continue; 313 free(positions->pos[i]->line); 314 free(positions->pos[i]); 315 } 316 free(positions); 317 318 positions = NULL; 319 } 320 321 /* 322 * Add another position to the positions array. 0 means everything 323 * went OK, -1 for errors 324 */ 325 static int 326 positions_add(cmd_pos_t *pos) { 327 if (positions->last >= positions->size) { 328 positions->size *= 2; 329 positions = (struct pos_sds *)realloc(positions, 330 sizeof (struct pos_sds) + 331 (positions->size - 1) * sizeof (cmd_pos_t *)); 332 if (positions == NULL) 333 goto nomemory; 334 } 335 336 if (pos == NULL) 337 positions->pos[positions->last] = NULL; 338 else { 339 positions->pos[positions->last] = (cmd_pos_t *)calloc(1, 340 sizeof (cmd_pos_t)); 341 if (positions->pos[positions->last] == NULL) 342 goto nomemory; 343 344 *positions->pos[positions->last] = *pos; 345 positions->pos[positions->last]->line = strdup(pos->line); 346 if (positions->pos[positions->last]->line == NULL) 347 goto nomemory; 348 } 349 350 positions->last++; 351 return (0); 352 353 nomemory: 354 print_error(NULL, gettext("Not enough memory.\n")); 355 return (-1); 356 } 357 358 359 360 361 /* 362 * Compare two strings just like strcmp, but stop before the end of 363 * the s2 364 */ 365 static int 366 strcmp_no0(const char *s1, const char *s2) { 367 return (strncmp(s1, s2, strlen(s2))); 368 } 369 370 /* The same as strcmp_no0, but case insensitive. */ 371 static int 372 strcasecmp_no0(const char *s1, const char *s2) { 373 return (strncasecmp(s1, s2, strlen(s2))); 374 } 375 376 377 /* Print help message */ 378 static void 379 help() { 380 (void) fprintf(stderr, 381 "idmap\n" 382 "idmap -f command-file\n" 383 "idmap show [-c] identity [targettype]\n" 384 "idmap dump [-n]\n" 385 "idmap add [-d] name1 name2\n" 386 "idmap remove -a\n" 387 "idmap remove name\n" 388 "idmap remove [-d] name1 name2\n" 389 "idmap list\n" 390 "idmap import [-F] [-f file] format\n" 391 "idmap export [-f file] format\n" 392 "idmap help\n"); 393 } 394 395 /* The handler for the "help" command. */ 396 static int 397 /* LINTED E_FUNC_ARG_UNUSED */ 398 do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 399 { 400 help(); 401 return (0); 402 } 403 404 /* Initialization of the idmap api batch */ 405 static int 406 init_batch() { 407 idmap_stat stat; 408 409 stat = idmap_init(&handle); 410 if (stat < 0) { 411 print_error(NULL, 412 gettext("Connection not established (%s)\n"), 413 idmap_stat2string(NULL, stat)); 414 return (-1); 415 } 416 417 return (0); 418 } 419 420 /* Initialization common to all commands */ 421 static int 422 init_command() { 423 if (batch_mode) 424 return (0); 425 426 return (init_batch()); 427 } 428 429 /* Finalization common to all commands */ 430 static void 431 fini_command() { 432 if (batch_mode) 433 return; 434 (void) idmap_fini(handle); 435 handle = NULL; 436 } 437 438 /* Initialization of the commands which perform write operations */ 439 static int 440 init_udt_batch() { 441 idmap_stat stat; 442 443 if (init_batch()) 444 return (-1); 445 446 stat = idmap_udt_create(handle, &udt); 447 if (stat < 0) { 448 print_error(NULL, 449 gettext("Error initiating transaction (%s)"), 450 idmap_stat2string(handle, stat)); 451 return (-1); 452 } 453 454 if (init_positions() < 0) 455 return (-1); 456 457 return (0); 458 } 459 460 461 /* Finalization of the write commands */ 462 static int 463 init_udt_command() { 464 udt_used = 1; 465 if (batch_mode) 466 return (0); 467 468 return (init_udt_batch()); 469 } 470 471 472 /* If everythings is OK, send the udt batch to idmapd */ 473 static int 474 fini_udt_command(int ok, cmd_pos_t *pos) { 475 int rc = 0; 476 int64_t failpos; 477 idmap_stat stat, stat1; 478 cmd_pos_t *reported_pos; 479 480 if (batch_mode) 481 return (0); 482 if (udt == NULL) { 483 print_error(pos, 484 gettext("Internal error: uninitiated batch.\n")); 485 return (-1); 486 } 487 488 if (ok && udt_used) { 489 stat = idmap_udt_commit(udt); 490 if (stat == IDMAP_SUCCESS) 491 goto out; 492 493 rc = -1; 494 495 stat1 = idmap_udt_get_error_index(udt, &failpos); 496 if (stat1 != IDMAP_SUCCESS) { 497 print_error(NULL, 498 gettext("Error diagnosing transaction (%s)\n"), 499 idmap_stat2string(handle, stat1)); 500 goto out; 501 } 502 503 504 if (failpos < 0) 505 reported_pos = pos; 506 else 507 reported_pos = positions->pos[failpos]; 508 509 print_error(reported_pos, 510 gettext("Error commiting transaction (%s)\n"), 511 idmap_stat2string(handle, stat)); 512 } 513 514 out: 515 idmap_udt_destroy(udt); 516 udt = NULL; 517 udt_used = 0; 518 fini_command(); 519 fini_positions(); 520 return (rc); 521 } 522 523 /* Convert numeric expression of the direction to it's string form */ 524 static char * 525 direction2string(int direction) { 526 switch (direction) { 527 case IDMAP_DIRECTION_BI: 528 return ("=="); 529 case IDMAP_DIRECTION_W2U: 530 return ("=>"); 531 case IDMAP_DIRECTION_U2W: 532 return ("<="); 533 default: 534 /* This can never happen: */ 535 print_error(NULL, 536 gettext("Internal error: invalid direction.\n")); 537 return (""); 538 } 539 /* never reached */ 540 } 541 542 /* Do we need quotation marks around winname in the USERMAP_CFG format? */ 543 static int 544 needs_protection(char *what) { 545 if (strchr(what, ' ') != NULL) 546 return (1); 547 548 if (strchr(what, '\t') != NULL) 549 return (1); 550 551 if (strchr(what, '#') != NULL) 552 return (1); 553 554 return (0); 555 } 556 557 558 /* 559 * Returns 1 if c is a shell-meta-character requiring quoting, 0 560 * otherwise. 561 * 562 * We don't quote '*' and ':' because they cannot do any harm 563 * a) they have no meaning to idmap_engine b) even ifsomebody copy & 564 * paste idmap output to a shell commandline, there is the identity 565 * type string in front of them. On the other hand, '*' and ':' are 566 * everywhere. 567 */ 568 static int 569 is_shell_special(char c) { 570 if (isspace(c)) 571 return (1); 572 573 if (strchr("&^{}#;'\"\\`!$()[]><|~", c) != NULL) 574 return (1); 575 576 return (0); 577 } 578 579 /* 580 * Returns 1 if c is a shell-meta-character requiring quoting even 581 * inside double quotes, 0 otherwise. It means \, " and $ . 582 * 583 * This set of characters is a subset of those in is_shell_special(). 584 */ 585 static int 586 is_dq_special(char c) { 587 if (strchr("\\\"$", c) != NULL) 588 return (1); 589 return (0); 590 } 591 592 593 594 595 /* 596 * Quote any shell meta-characters in the given string. If 'quote' is 597 * true then use double-quotes to quote the whole string, else use 598 * back-slash to quote each individual meta-character. 599 * 600 * The resulting string is placed in *res. Callers must free *res if the 601 * return value isn't 0 (even if the given string had no meta-chars). 602 * If there are any errors this returns -1, else 0. 603 */ 604 static int 605 shell_app(char **res, char *string, int quote) { 606 int i, j; 607 uint_t noss = 0; /* Number Of Shell Special chars in the input */ 608 uint_t noqb = 0; /* Number Of Quotes and Backslahes in the input */ 609 char *out; 610 size_t len_orig = strlen(string); 611 size_t len; 612 613 /* First, let us count how many characters we need to quote: */ 614 for (i = 0; i < len_orig; i++) { 615 if (is_shell_special(string[i])) { 616 noss++; 617 if (is_dq_special(string[i])) 618 noqb++; 619 } 620 621 } 622 623 /* Do we need to quote at all? */ 624 if (noss == 0) { 625 out = strdup(string); 626 if (out == NULL) { 627 print_error(NULL, gettext("Not enough memory.\n")); 628 return (-1); 629 } 630 *res = out; 631 return (0); 632 } 633 634 /* What is the length of the result? */ 635 if (quote) 636 len = strlen(string) + 2 + noqb + 1; /* 2 for quotation marks */ 637 else 638 len = strlen(string) + noss + 1; 639 640 out = (char *)malloc(len * sizeof (char)); 641 if (out == NULL) { 642 print_error(NULL, gettext("Not enough memory.\n")); 643 return (-1); 644 } 645 646 j = 0; 647 if (quote) 648 out[j++] = '"'; 649 650 for (i = 0; i < len_orig; i++) { 651 /* Quote the dangerous chars by a backslash */ 652 if (quote && is_dq_special(string[i]) || 653 (!quote && is_shell_special(string[i]))) { 654 out[j++] = '\\'; 655 } 656 out[j++] = string[i]; 657 } 658 659 if (quote) 660 out[j++] = '"'; 661 662 out[j] = '\0'; 663 *res = out; 664 return (0); 665 } 666 667 /* Assemble string form sid */ 668 static char * 669 sid_format(char *sidprefix, idmap_rid_t rid) { 670 char *to; 671 size_t len; 672 673 /* 'sid:' + sidprefix + '-' + rid + '\0' */ 674 len = strlen(sidprefix) + 6 + 3 * sizeof (rid); 675 to = (char *)malloc(len * sizeof (char)); 676 if (to == NULL) 677 return (NULL); 678 679 (void) snprintf(to, len, "sid:%s-%u", sidprefix, rid); 680 return (to); 681 } 682 683 /* Assemble string form uid or gid */ 684 static char * 685 pid_format(uid_t from, int is_user) { 686 char *to; 687 size_t len; 688 689 /* ID_UID ":" + uid + '\0' */ 690 len = 5 + 3 * sizeof (uid_t); 691 to = (char *)malloc(len * sizeof (char)); 692 if (to == NULL) 693 return (NULL); 694 695 (void) snprintf(to, 16, "%s:%u", is_user ? ID_UID : ID_GID, from); 696 return (to); 697 } 698 699 /* Assemble winname, e.g. "winuser:bob@foo.sun.com", from name_mapping_t */ 700 static int 701 nm2winqn(name_mapping_t *nm, char **winqn) { 702 char *out; 703 size_t length = 0; 704 int is_domain = 1; 705 char *prefix; 706 707 /* Sometimes there are no text names. Return a sid, then. */ 708 if (nm->winname == NULL) { 709 if (nm->sidprefix == NULL) 710 return (-1); 711 712 *winqn = sid_format(nm->sidprefix, nm->rid); 713 return (0); 714 } 715 716 switch (nm->is_user) { 717 case I_YES: 718 prefix = ID_WINUSER ":"; 719 break; 720 case I_NO: 721 prefix = ID_WINGROUP ":"; 722 break; 723 case I_UNKNOWN: 724 prefix = ID_WINNAME ":"; 725 break; 726 727 } 728 729 length = strlen(prefix) + strlen(nm->winname); 730 731 /* Windomain is not mandatory: */ 732 if (nm->windomain == NULL || 733 *nm->winname == '\0' || 734 strcmp(nm->winname, "\"\"") == 0) 735 is_domain = 0; 736 else 737 length += strlen(nm->windomain) + 1; 738 739 out = (char *)malloc((length + 1) * sizeof (char)); 740 if (out == NULL) { 741 print_error(NULL, 742 gettext("Not enough memory.\n")); 743 return (-1); 744 } 745 746 (void) strcpy(out, prefix); 747 748 if (!is_domain) 749 (void) strcat(out, nm->winname); 750 else if (nm->is_nt4) { 751 (void) strcat(out, nm->windomain); 752 (void) strcat(out, "\\"); 753 (void) strcat(out, nm->winname); 754 } else { 755 (void) strcat(out, nm->winname); 756 (void) strcat(out, "@"); 757 (void) strcat(out, nm->windomain); 758 } 759 760 *winqn = out; 761 return (0); 762 } 763 764 /* Assemble a text unixname, e.g. unixuser:fred */ 765 static int 766 nm2unixname(name_mapping_t *nm, char **unixname) { 767 size_t length = 0; 768 char *out; 769 char *it; 770 char *prefix; 771 772 /* Sometimes there is no name, just pid: */ 773 if (nm->unixname == NULL) { 774 if (nm->pid == UNDEFINED_UID) 775 return (-1); 776 777 *unixname = pid_format(nm->pid, nm->is_user); 778 return (0); 779 } 780 781 if (shell_app(&it, nm->unixname, 0)) 782 return (-1); 783 784 785 switch (nm->is_user) { 786 case I_YES: 787 prefix = ID_UNIXUSER ":"; 788 break; 789 case I_NO: 790 prefix = ID_UNIXGROUP ":"; 791 break; 792 case I_UNKNOWN: 793 prefix = ID_UNIXNAME ":"; 794 break; 795 796 } 797 798 length = strlen(prefix) + strlen(it); 799 800 out = (char *)malloc((length + 1) * sizeof (char)); 801 if (out == NULL) { 802 print_error(NULL, 803 gettext("Not enough memory.\n")); 804 free(it); 805 return (-1); 806 } 807 808 (void) strcpy(out, prefix); 809 (void) strcat(out, it); 810 free(it); 811 812 *unixname = out; 813 return (0); 814 } 815 816 /* Initialize print_mapping variables. Must be called before print_mapping */ 817 static int 818 print_mapping_init(format_t f, FILE *fi) { 819 pnm_format = f; 820 pnm_file = fi; 821 822 switch (pnm_format) { 823 case SMBUSERS: 824 pnm_last_unixname = NULL; 825 break; 826 default: 827 ; 828 } 829 830 return (0); 831 } 832 833 /* Finalize print_mapping. */ 834 static int 835 print_mapping_fini() { 836 switch (pnm_format) { 837 case SMBUSERS: 838 if (pnm_last_unixname != NULL) { 839 (void) fprintf(pnm_file, "\n"); 840 free(pnm_last_unixname); 841 } 842 break; 843 default: 844 ; 845 } 846 847 pnm_file = stderr; 848 pnm_format = UNDEFINED_FORMAT; 849 850 return (0); 851 } 852 853 /* 854 * This prints both name rules and ordinary mappings, based on the pnm_format 855 * set in print_mapping_init(). 856 */ 857 858 static int 859 print_mapping(name_mapping_t *nm) 860 { 861 char *dirstring; 862 char *winname_qm, *windomain_qm, *unixname_qm; 863 char *winname = NULL; 864 char *winname1 = NULL; 865 char *unixname = NULL; 866 FILE *f = pnm_file; 867 868 869 switch (pnm_format) { 870 case MAPPING_NAME: 871 if (nm2winqn(nm, &winname) < 0) 872 return (-1); 873 if (nm2unixname(nm, &unixname) < 0) { 874 free(winname); 875 return (-1); 876 } 877 /* LINTED E_CASE_FALLTHRU */ 878 case MAPPING_ID: 879 if (pnm_format == MAPPING_ID) { 880 if (nm->sidprefix == NULL) { 881 print_error(NULL, 882 gettext("SID not given.\n")); 883 return (-1); 884 } 885 winname = sid_format(nm->sidprefix, nm->rid); 886 if (winname == NULL) 887 return (-1); 888 unixname = pid_format(nm->pid, nm->is_user); 889 if (unixname == NULL) { 890 free(winname); 891 return (-1); 892 } 893 } 894 895 dirstring = direction2string(nm->direction); 896 897 (void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring, 898 unixname); 899 900 free(winname); 901 free(unixname); 902 break; 903 case SMBUSERS: 904 if (!nm->is_user) { 905 print_error(NULL, 906 gettext("Group rule: ")); 907 f = stderr; 908 } else if (nm->direction == IDMAP_DIRECTION_U2W) { 909 print_error(NULL, 910 gettext("Opposite direction of the mapping: ")); 911 f = stderr; 912 } 913 if (shell_app(&winname, nm->winname, 1)) 914 return (-1); 915 916 if (pnm_file != f) { 917 (void) fprintf(f, "%s=%s\n", nm->unixname, winname); 918 } else if (pnm_last_unixname != NULL && 919 strcmp(pnm_last_unixname, nm->unixname) == 0) { 920 (void) fprintf(f, " %s", winname); 921 } else { 922 if (pnm_last_unixname != NULL) { 923 (void) fprintf(f, "\n"); 924 free(pnm_last_unixname); 925 } 926 pnm_last_unixname = strdup(nm->unixname); 927 (void) fprintf(f, "%s=%s", nm->unixname, winname); 928 } 929 930 free(winname); 931 932 break; 933 case USERMAP_CFG: 934 if (!nm->is_user) { 935 print_error(NULL, 936 gettext("Group rule: ")); 937 f = stderr; 938 } 939 940 dirstring = direction2string(nm->direction); 941 942 winname_qm = needs_protection(nm->winname) ? "\"" : ""; 943 windomain_qm = nm->windomain && 944 needs_protection(nm->windomain) ? "\"" : ""; 945 unixname_qm = needs_protection(nm->unixname) ? "\"" : ""; 946 947 if (nm->windomain == NULL) 948 (void) fprintf(f, "%s%s%s\t%s\t%s%s%s\n", 949 winname_qm, 950 nm->winname, winname_qm, dirstring, 951 unixname_qm, nm->unixname, unixname_qm); 952 else 953 (void) fprintf(f, nm->is_nt4 ? 954 "%s%s%1$s\\%3$s%4$s%3$s\t%5$s\t%6$s%7$s%6$s\n" : 955 "%3$s%4$s%3$s@%1$s%2$s%1$s\t%5$s\t%6$s%7$s%6$s\n", 956 windomain_qm, nm->windomain, 957 winname_qm, nm->winname, 958 dirstring, 959 unixname_qm, nm->unixname); 960 break; 961 962 case DEFAULT_FORMAT: 963 if (nm2winqn(nm, &winname1) < 0) 964 return (-1); 965 966 if (shell_app(&winname, winname1, 1)) { 967 free(winname1); 968 return (-1); 969 } 970 971 free(winname1); 972 973 if (nm2unixname(nm, &unixname)) { 974 free(winname); 975 return (-1); 976 } 977 978 if (nm->direction == IDMAP_DIRECTION_U2W) { 979 (void) fprintf(f, 980 "add -d\t%s\t%s\n", 981 unixname, winname); 982 } else { 983 (void) fprintf(f, 984 "add %s\t%s\t%s\n", 985 nm->direction == IDMAP_DIRECTION_BI? "" : "-d", 986 winname, unixname); 987 } 988 free(winname); 989 free(unixname); 990 break; 991 default: 992 /* This can never happen: */ 993 print_error(NULL, 994 gettext("Internal error: invalid print format.\n")); 995 return (-1); 996 } 997 998 return (0); 999 } 1000 1001 /* Allocate a new name_mapping_t and initialize the values. */ 1002 static name_mapping_t * 1003 name_mapping_init() { 1004 name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t)); 1005 if (nm == NULL) { 1006 print_error(NULL, gettext("Not enough memory.\n")); 1007 return (NULL); 1008 } 1009 nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL; 1010 nm->rid = UNDEFINED_RID; 1011 nm->is_nt4 = B_FALSE; 1012 nm->is_user = I_UNKNOWN; 1013 nm->direction = IDMAP_DIRECTION_UNDEF; 1014 nm->pid = UNDEFINED_UID; 1015 return (nm); 1016 } 1017 1018 /* Free name_mapping_t */ 1019 static void 1020 name_mapping_fini(name_mapping_t *nm) { 1021 1022 free(nm->winname); 1023 free(nm->windomain); 1024 free(nm->unixname); 1025 free(nm->sidprefix); 1026 1027 free(nm); 1028 } 1029 1030 1031 /* dump command handler */ 1032 static int 1033 /* LINTED E_FUNC_ARG_UNUSED */ 1034 do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 1035 { 1036 idmap_stat stat; 1037 idmap_iter_t *ihandle; 1038 int is_user; 1039 int rc = 0; 1040 1041 if (init_command()) 1042 return (-1); 1043 1044 (void) print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID, 1045 stdout); 1046 1047 for (is_user = I_YES; is_user >= I_NO; is_user--) { 1048 stat = idmap_iter_mappings(handle, is_user, &ihandle); 1049 if (stat < 0) { 1050 print_error(pos, 1051 gettext("Iteration handle not obtained (%s)\n"), 1052 idmap_stat2string(handle, stat)); 1053 rc = -1; 1054 goto cleanup; 1055 } 1056 1057 do { 1058 name_mapping_t *nm = name_mapping_init(); 1059 if (nm == NULL) { 1060 rc = -1; 1061 goto cleanup; 1062 } 1063 nm->is_user = is_user; 1064 1065 1066 stat = idmap_iter_next_mapping(ihandle, 1067 &nm->sidprefix, &nm->rid, &nm->pid, 1068 &nm->winname, &nm->windomain, 1069 &nm->unixname, &nm->direction); 1070 1071 if (stat >= 0) 1072 (void) print_mapping(nm); 1073 1074 name_mapping_fini(nm); 1075 1076 } while (stat > 0); 1077 1078 /* IDMAP_ERR_NOTFOUND indicates end of the list */ 1079 if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) { 1080 print_error(pos, 1081 gettext("Error during iteration (%s)\n"), 1082 idmap_stat2string(handle, stat)); 1083 rc = -1; 1084 goto cleanup; 1085 } 1086 1087 idmap_iter_destroy(ihandle); 1088 } 1089 cleanup: 1090 (void) print_mapping_fini(); 1091 fini_command(); 1092 return (rc); 1093 } 1094 1095 /* 1096 * The same as strdup, but length chars is duplicated, no matter on 1097 * '\0'. The caller must guarantee "length" chars in "from". 1098 */ 1099 static char * 1100 strndup(char *from, size_t length) { 1101 char *out = (char *)malloc((length + 1) * sizeof (char)); 1102 if (out == NULL) { 1103 print_error(NULL, gettext("Not enough memory\n")); 1104 return (NULL); 1105 } 1106 (void) strncpy(out, from, length); 1107 out[length] = '\0'; 1108 return (out); 1109 } 1110 1111 /* 1112 * Convert pid from string to it's numerical representation. If it is 1113 * a valid string, i.e. number of a proper length, return 1. Otherwise 1114 * print an error message and return 0. 1115 */ 1116 static int 1117 pid_convert(char *string, uid_t *number, int type, cmd_pos_t *pos) { 1118 int i; 1119 long long ll; 1120 char *type_string; 1121 size_t len = strlen(string); 1122 1123 if (type == TYPE_GID) 1124 type_string = ID_GID; 1125 else if (type == TYPE_UID) 1126 type_string = ID_UID; 1127 else 1128 return (0); 1129 1130 for (i = 0; i < len; i++) { 1131 if (!isdigit(string[i])) { 1132 print_error(pos, 1133 gettext("\"%s\" is not a valid %s: the non-digit" 1134 " character '%c' found.\n"), string, 1135 type_string, string[i]); 1136 return (0); 1137 } 1138 } 1139 1140 ll = atoll(string); 1141 1142 /* Isn't it too large? */ 1143 if (type == TYPE_UID && (uid_t)ll != ll || 1144 type == TYPE_GID && (gid_t)ll != ll) { 1145 print_error(pos, 1146 gettext("%llu: too large for a %s.\n"), ll, 1147 type_string); 1148 return (0); 1149 } 1150 1151 *number = (uid_t)ll; 1152 return (1); 1153 } 1154 1155 /* 1156 * Convert SID from string to prefix and rid. If it has a valid 1157 * format, i.e. S(\-\d+)+, return 1. Otherwise print an error 1158 * message and return 0. 1159 */ 1160 static int 1161 sid_convert(char *from, char **prefix, idmap_rid_t *rid, cmd_pos_t *pos) { 1162 int i, j; 1163 char *cp; 1164 char *ecp; 1165 char *prefix_end; 1166 u_longlong_t a; 1167 unsigned long r; 1168 1169 if (strcmp_no0(from, "S-1-") != 0) { 1170 print_error(pos, 1171 gettext("Invalid %s \"%s\": it doesn't start " 1172 "with \"%s\".\n"), ID_SID, from, "S-1-"); 1173 return (0); 1174 } 1175 1176 if (strlen(from) <= strlen("S-1-")) { 1177 print_error(pos, 1178 gettext("Invalid %s \"%s\": the authority and RID parts are" 1179 " missing.\n"), 1180 ID_SID, from); 1181 return (0); 1182 } 1183 1184 /* count '-'s */ 1185 for (j = 0, cp = strchr(from, '-'); 1186 cp != NULL; 1187 j++, cp = strchr(cp + 1, '-')) { 1188 /* can't end on a '-' */ 1189 if (*(cp + 1) == '\0') { 1190 print_error(pos, 1191 gettext("Invalid %s \"%s\": '-' at the end.\n"), 1192 ID_SID, from); 1193 return (0); 1194 } else if (*(cp + 1) == '-') { 1195 print_error(pos, 1196 gettext("Invalid %s \"%s\": double '-'.\n"), 1197 ID_SID, from); 1198 return (0); 1199 } 1200 } 1201 1202 1203 /* check that we only have digits and '-' */ 1204 i = strspn(from + 1, "0123456789-") + 1; 1205 if (i < strlen(from)) { 1206 print_error(pos, 1207 gettext("Invalid %s \"%s\": invalid character '%c'.\n"), 1208 ID_SID, from, from[i]); 1209 return (0); 1210 } 1211 1212 1213 cp = from + strlen("S-1-"); 1214 1215 /* 64-bit safe parsing of unsigned 48-bit authority value */ 1216 errno = 0; 1217 a = strtoull(cp, &ecp, 10); 1218 1219 /* errors parsing the authority or too many bits */ 1220 if (cp == ecp || (a == 0 && errno == EINVAL)) { 1221 print_error(pos, 1222 gettext("Invalid %s \"%s\": unable to parse the " 1223 "authority \"%.*s\".\n"), ID_SID, from, ecp - cp, 1224 cp); 1225 return (0); 1226 } 1227 1228 if ((a == ULLONG_MAX && errno == ERANGE) || 1229 (a & 0x0000ffffffffffffULL) != a) { 1230 print_error(pos, 1231 gettext("Invalid %s \"%s\": the authority " 1232 "\"%.*s\" is too large.\n"), ID_SID, from, 1233 ecp - cp, cp); 1234 return (0); 1235 } 1236 1237 cp = ecp; 1238 1239 if (j < 3) { 1240 print_error(pos, 1241 gettext("Invalid %s \"%s\": must have at least one RID.\n"), 1242 ID_SID, from); 1243 return (0); 1244 } 1245 1246 for (i = 2; i < j; i++) { 1247 if (*cp++ != '-') { 1248 /* Should never happen */ 1249 print_error(pos, 1250 gettext("Invalid %s \"%s\": internal error:" 1251 " '-' missing.\n"), 1252 ID_SID, from); 1253 return (0); 1254 } 1255 /* 32-bit safe parsing of unsigned 32-bit RID */ 1256 errno = 0; 1257 r = strtoul(cp, &ecp, 10); 1258 1259 /* errors parsing the RID */ 1260 if (cp == ecp || (r == 0 && errno == EINVAL)) { 1261 /* should never happen */ 1262 print_error(pos, 1263 gettext("Invalid %s \"%s\": internal error: " 1264 "unable to parse the RID " 1265 "after \"%.*s\".\n"), ID_SID, 1266 from, cp - from, from); 1267 return (0); 1268 } 1269 1270 if (r == ULONG_MAX && errno == ERANGE) { 1271 print_error(pos, 1272 gettext("Invalid %s \"%s\": the RID \"%.*s\"" 1273 " is too large.\n"), ID_SID, 1274 from, ecp - cp, cp); 1275 return (0); 1276 } 1277 prefix_end = cp; 1278 cp = ecp; 1279 } 1280 1281 /* check that all of the string SID has been consumed */ 1282 if (*cp != '\0') { 1283 /* Should never happen */ 1284 print_error(pos, 1285 gettext("Invalid %s \"%s\": internal error: " 1286 "something is still left.\n"), 1287 ID_SID, from); 1288 return (0); 1289 } 1290 1291 *rid = (idmap_rid_t)r; 1292 1293 /* -1 for the '-' at the end: */ 1294 *prefix = strndup(from, prefix_end - from - 1); 1295 if (*prefix == NULL) { 1296 print_error(pos, 1297 gettext("Not enough memory.\n")); 1298 return (0); 1299 } 1300 1301 return (1); 1302 } 1303 1304 /* Does the line start with USERMAP_CFG IP qualifier? */ 1305 static int 1306 ucp_is_IP_qualifier(char *line) { 1307 char *it; 1308 it = line + strcspn(line, " \t\n#:"); 1309 return (*(it + 1) == ':' ? 1 : 0); 1310 } 1311 1312 1313 /* 1314 * returns interior of quotation marks in USERMAP_CFG. In this format, 1315 * there cannot be a protected quotation mark inside. 1316 */ 1317 static char * 1318 ucp_qm_interior(char **line, cmd_pos_t *pos) { 1319 char *out; 1320 char *qm = strchr(*line + 1, '"'); 1321 if (qm == NULL) { 1322 print_error(pos, 1323 gettext("Unclosed quotations\n")); 1324 return (NULL); 1325 } 1326 1327 out = strndup(*line + 1, qm - *line - 1); 1328 *line = qm + 1; 1329 return (out); 1330 } 1331 1332 /* 1333 * Grab next token from the line in USERMAP_CFG format. terminators, 1334 * the 3rd parameter, contains all the characters which can terminate 1335 * the token. line_num is the line number of input used for error 1336 * reporting. 1337 */ 1338 static char * 1339 ucp_grab_token(char **line, cmd_pos_t *pos, const char *terminators) { 1340 char *token; 1341 if (**line == '"') 1342 token = ucp_qm_interior(line, pos); 1343 else { 1344 int length = strcspn(*line, terminators); 1345 token = strndup(*line, length); 1346 *line += length; 1347 } 1348 1349 return (token); 1350 } 1351 1352 1353 /* 1354 * Convert a line in usermap.cfg format to name_mapping. 1355 * 1356 * Return values: -1 for error, 0 for empty line, 1 for a mapping 1357 * found. 1358 */ 1359 static int 1360 ucp_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm) { 1361 char *it; 1362 char *token; 1363 char *token2; 1364 char separator; 1365 int is_direction = 0; 1366 1367 it = line + strspn(line, " \t\n"); 1368 1369 /* empty or comment lines are OK: */ 1370 if (*it == '\0' || *it == '#') 1371 return (0); 1372 1373 /* We do not support network qualifiers */ 1374 if (ucp_is_IP_qualifier(it)) { 1375 print_error(pos, 1376 gettext("Unable to handle network qualifier.\n")); 1377 return (-1); 1378 } 1379 1380 /* The windows name: */ 1381 token = ucp_grab_token(&it, pos, " \t#\\\n@=<"); 1382 if (token == NULL) 1383 return (-1); 1384 1385 separator = *it; 1386 1387 /* Didn't we bump to the end of line? */ 1388 if (separator == '\0' || separator == '#') { 1389 free(token); 1390 print_error(pos, 1391 gettext("UNIX_name not found.\n")); 1392 return (-1); 1393 } 1394 1395 /* Do we have a domainname? */ 1396 if (separator == '\\' || separator == '@') { 1397 it ++; 1398 token2 = ucp_grab_token(&it, pos, " \t\n#"); 1399 if (token2 == NULL) { 1400 free(token); 1401 return (-1); 1402 } else if (*it == '\0' || *it == '#') { 1403 free(token); 1404 free(token2); 1405 print_error(pos, 1406 gettext("UNIX_name not found.\n")); 1407 } 1408 1409 if (separator == '\\') { 1410 nm->windomain = token; 1411 nm->winname = token2; 1412 nm->is_nt4 = 1; 1413 } else { 1414 nm->windomain = token2; 1415 nm->winname = token; 1416 nm->is_nt4 = 0; 1417 1418 } 1419 } else { 1420 nm->windomain = NULL; 1421 nm->winname = token; 1422 nm->is_nt4 = 0; 1423 } 1424 1425 1426 it = it + strspn(it, " \t\n"); 1427 1428 /* Direction string is optional: */ 1429 if (strncmp(it, "==", 2) == 0) { 1430 nm->direction = IDMAP_DIRECTION_BI; 1431 is_direction = 1; 1432 } else if (strncmp(it, "<=", 2) == 0) { 1433 nm->direction = IDMAP_DIRECTION_U2W; 1434 is_direction = 1; 1435 } else if (strncmp(it, "=>", 2) == 0) { 1436 nm->direction = IDMAP_DIRECTION_W2U; 1437 is_direction = 1; 1438 } else { 1439 nm->direction = IDMAP_DIRECTION_BI; 1440 is_direction = 0; 1441 } 1442 1443 if (is_direction) { 1444 it += 2; 1445 it += strspn(it, " \t\n"); 1446 1447 if (*it == '\0' || *it == '#') { 1448 print_error(pos, 1449 gettext("UNIX_name not found.\n")); 1450 return (-1); 1451 } 1452 } 1453 1454 /* Now unixname: */ 1455 it += strspn(it, " \t\n"); 1456 token = ucp_grab_token(&it, pos, " \t\n#"); 1457 1458 if (token == NULL) 1459 /* nm->winname to be freed by name_mapping_fini */ 1460 return (-1); 1461 1462 /* Neither here we support IP qualifiers */ 1463 if (ucp_is_IP_qualifier(token)) { 1464 print_error(pos, 1465 gettext("Unable to handle network qualifier.\n")); 1466 free(token); 1467 return (-1); 1468 } 1469 1470 nm->unixname = token; 1471 1472 it += strspn(it, " \t\n"); 1473 1474 /* Does something remain on the line */ 1475 if (*it != '\0' && *it != '#') { 1476 print_error(pos, 1477 gettext("Unrecognized parameters \"%s\".\n"), it); 1478 return (-1); 1479 } 1480 1481 return (1); 1482 } 1483 1484 /* 1485 * Parse SMBUSERS line to name_mapping_t. if line is NULL, then 1486 * pasrsing of the previous line is continued. line_num is input line 1487 * number used for error reporting. 1488 * Return values: 1489 * rc -1: error 1490 * rc = 0: mapping found and the line is finished, 1491 * rc = 1: mapping found and there remains other on the line 1492 */ 1493 static int 1494 sup_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm) { 1495 static char *ll = NULL; 1496 static char *unixname = NULL; 1497 static size_t unixname_l = 0; 1498 char *token; 1499 1500 if (line != NULL) { 1501 ll = line; 1502 1503 unixname = ll += strspn(ll, " \t"); 1504 if (*ll == '\0' || *ll == '#') 1505 return (0); 1506 1507 unixname_l = strcspn(ll, " \t:=#\n"); 1508 ll += unixname_l; 1509 1510 if (*ll == '\0'|| *ll == '#') 1511 return (0); 1512 1513 ll += strspn(ll, " \t:=#\n"); 1514 1515 } 1516 1517 if (*ll == '\0'|| *ll == '#') 1518 return (0); 1519 1520 token = ucp_grab_token(&ll, pos, " \t\n"); 1521 if (token == NULL) 1522 return (-1); 1523 1524 nm->is_nt4 = 0; 1525 nm->direction = IDMAP_DIRECTION_W2U; 1526 1527 nm->windomain = NULL; 1528 nm->winname = token; 1529 nm->unixname = strndup(unixname, unixname_l); 1530 if (nm->unixname == NULL) 1531 return (-1); 1532 1533 ll += strspn(ll, " \t\n"); 1534 return (1); 1535 } 1536 1537 /* Parse line to name_mapping_t. Basicaly just a format switch. */ 1538 static int 1539 line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm, format_t f) { 1540 switch (f) { 1541 case USERMAP_CFG: 1542 if (line == NULL) 1543 return (0); 1544 else 1545 return (ucp_line2nm(line, pos, nm)); 1546 case SMBUSERS: 1547 return (sup_line2nm(line, pos, nm)); 1548 default: 1549 /* This can never happen */ 1550 print_error(pos, 1551 gettext("Internal error: invalid line format.\n")); 1552 } 1553 1554 return (-1); 1555 } 1556 1557 1558 /* Examine -f flag and return the appropriate format_t */ 1559 static format_t 1560 ff2format(char *ff, int is_mandatory) { 1561 1562 if (ff == NULL && is_mandatory) { 1563 print_error(NULL, gettext("Format not given.\n")); 1564 return (UNDEFINED_FORMAT); 1565 } 1566 1567 if (ff == NULL) 1568 return (DEFAULT_FORMAT); 1569 1570 if (strcasecmp(ff, "usermap.cfg") == 0) 1571 return (USERMAP_CFG); 1572 1573 if (strcasecmp(ff, "smbusers") == 0) 1574 return (SMBUSERS); 1575 1576 print_error(NULL, 1577 gettext("The only known formats are: \"usermap.cfg\" and " 1578 "\"smbusers\".\n")); 1579 return (UNDEFINED_FORMAT); 1580 } 1581 1582 /* Delete all namerules of the given type */ 1583 static int 1584 flush_nm(boolean_t is_user, cmd_pos_t *pos) 1585 { 1586 idmap_stat stat; 1587 1588 stat = idmap_udt_flush_namerules(udt, is_user); 1589 if (stat < 0) { 1590 print_error(pos, 1591 is_user ? gettext("Unable to flush users (%s).\n") 1592 : gettext("Unable to flush groups (%s).\n"), 1593 idmap_stat2string(handle, stat)); 1594 return (-1); 1595 } 1596 1597 if (positions_add(pos) < 0) 1598 return (-1); 1599 1600 return (0); 1601 } 1602 1603 /* import command handler */ 1604 static int 1605 /* LINTED E_FUNC_ARG_UNUSED */ 1606 do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 1607 { 1608 name_mapping_t *nm; 1609 cmd_pos_t pos2; 1610 char line[MAX_INPUT_LINE_SZ]; 1611 format_t format; 1612 int rc = 0; 1613 idmap_stat stat; 1614 FILE *file = NULL; 1615 1616 if (batch_mode) { 1617 print_error(pos, 1618 gettext("Import is not allowed in the batch mode.\n")); 1619 return (-1); 1620 } 1621 1622 format = ff2format(argv[0], 1); 1623 if (format == UNDEFINED_FORMAT) 1624 return (-1); 1625 1626 if (init_udt_command()) 1627 return (-1); 1628 1629 /* We don't flush groups in the usermap.cfg nor smbusers format */ 1630 if (f[F_FLAG] != NULL && 1631 flush_nm(B_TRUE, pos) < 0 && 1632 (format == USERMAP_CFG || format == SMBUSERS || 1633 flush_nm(B_FALSE, pos) < 0)) { 1634 rc = -1; 1635 goto cleanup; 1636 } 1637 1638 /* Where we import from? */ 1639 if (f[f_FLAG] == NULL) 1640 file = stdin; 1641 else { 1642 file = fopen(f[f_FLAG], "r"); 1643 if (file == NULL) { 1644 perror(f[f_FLAG]); 1645 goto cleanup; 1646 } 1647 } 1648 1649 pos2.linenum = 0; 1650 pos2.line = line; 1651 1652 while (fgets(line, MAX_INPUT_LINE_SZ, file)) { 1653 char *line2 = line; 1654 pos2.linenum++; 1655 1656 /* 1657 * In SMBUSERS format there can be more mappings on 1658 * each line. So we need the internal cycle for each line. 1659 */ 1660 do { 1661 nm = name_mapping_init(); 1662 if (nm == NULL) { 1663 rc = -1; 1664 goto cleanup; 1665 } 1666 1667 rc = line2nm(line2, &pos2, nm, format); 1668 line2 = NULL; 1669 1670 if (rc < 1) { 1671 name_mapping_fini(nm); 1672 break; 1673 } 1674 1675 stat = idmap_udt_add_namerule(udt, nm->windomain, 1676 nm->is_user ? B_TRUE : B_FALSE, nm->winname, 1677 nm->unixname, nm->is_nt4, nm->direction); 1678 if (stat < 0) { 1679 print_error(&pos2, 1680 gettext("Transaction error (%s)\n"), 1681 idmap_stat2string(handle, stat)); 1682 rc = -1; 1683 } 1684 1685 if (rc >= 0) 1686 rc = positions_add(&pos2); 1687 1688 name_mapping_fini(nm); 1689 1690 } while (rc >= 0); 1691 1692 if (rc < 0) { 1693 print_error(NULL, 1694 gettext("Import canceled.\n")); 1695 break; 1696 } 1697 } 1698 1699 cleanup: 1700 if (fini_udt_command((rc < 0 ? 0 : 1), pos)) 1701 rc = -1; 1702 if (file != NULL && file != stdin) 1703 (void) fclose(file); 1704 return (rc); 1705 } 1706 1707 1708 /* 1709 * List name mappings in the format specified. list_users / 1710 * list_groups determine which type to list. The output goes to the 1711 * file fi. 1712 */ 1713 static int 1714 list_name_mappings(format_t format, FILE *fi) 1715 { 1716 idmap_stat stat; 1717 idmap_iter_t *ihandle; 1718 name_mapping_t *nm; 1719 int is_user; 1720 1721 for (is_user = I_YES; is_user >= I_NO; is_user--) { 1722 /* Only users can be in USERMAP_CFG format, not a group */ 1723 if (!is_user && format == USERMAP_CFG) 1724 continue; 1725 1726 stat = idmap_iter_namerules(handle, NULL, is_user, NULL, 1727 NULL, &ihandle); 1728 if (stat < 0) { 1729 print_error(NULL, 1730 gettext("Iteration handle not obtained (%s)\n"), 1731 idmap_stat2string(handle, stat)); 1732 idmap_iter_destroy(ihandle); 1733 return (-1); 1734 } 1735 1736 (void) print_mapping_init(format, fi); 1737 1738 do { 1739 nm = name_mapping_init(); 1740 if (nm == NULL) { 1741 idmap_iter_destroy(ihandle); 1742 return (-1); 1743 } 1744 1745 stat = idmap_iter_next_namerule(ihandle, &nm->windomain, 1746 &nm->winname, &nm->unixname, &nm->is_nt4, 1747 &nm->direction); 1748 if (stat >= 0) { 1749 nm->is_user = is_user; 1750 (void) print_mapping(nm); 1751 } 1752 1753 name_mapping_fini(nm); 1754 1755 } while (stat > 0); 1756 1757 (void) print_mapping_fini(); 1758 1759 if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) { 1760 print_error(NULL, 1761 gettext("Error during iteration (%s)\n"), 1762 idmap_stat2string(handle, stat)); 1763 idmap_iter_destroy(ihandle); 1764 return (-1); 1765 } 1766 1767 idmap_iter_destroy(ihandle); 1768 } 1769 return (0); 1770 } 1771 1772 /* Export command handler */ 1773 static int 1774 /* LINTED E_FUNC_ARG_UNUSED */ 1775 do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos) { 1776 int rc; 1777 format_t format; 1778 FILE *fi; 1779 1780 format = ff2format(argv[0], 1); 1781 if (format == UNDEFINED_FORMAT) 1782 return (-1); 1783 1784 /* Where do we output to? */ 1785 if (f[f_FLAG] == NULL) 1786 fi = stdout; 1787 else { 1788 fi = fopen(f[f_FLAG], "w"); 1789 if (fi == NULL) { 1790 perror(f[f_FLAG]); 1791 return (-1); 1792 } 1793 } 1794 1795 if (init_command() < 0) { 1796 rc = -1; 1797 goto cleanup; 1798 } 1799 1800 /* List the requested types: */ 1801 rc = list_name_mappings(format, fi); 1802 1803 fini_command(); 1804 1805 cleanup: 1806 if (fi != NULL && fi != stdout) 1807 (void) fclose(fi); 1808 return (rc); 1809 } 1810 1811 /* List command handler */ 1812 static int 1813 /* LINTED E_FUNC_ARG_UNUSED */ 1814 do_list_name_mappings(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 1815 { 1816 int rc; 1817 1818 if (init_command()) { 1819 return (-1); 1820 } 1821 1822 /* List the requested types: */ 1823 rc = list_name_mappings(DEFAULT_FORMAT, stdout); 1824 1825 fini_command(); 1826 return (rc); 1827 } 1828 1829 /* This is just a debug function for dumping flags */ 1830 static void 1831 print_flags(flag_t *f) 1832 { 1833 int c; 1834 for (c = 0; c < FLAG_ALPHABET_SIZE; c++) { 1835 if (f[c] == FLAG_SET) 1836 (void) printf("FLAG: -%c, VALUE: %p\n", c, 1837 (void *) f[c]); 1838 else if (f[c]) 1839 (void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]); 1840 } 1841 } 1842 1843 /* 1844 * This function splits name to the relevant pieces: is_user, winname, 1845 * windomain unixname. Sometimes it is not possible to determine OS 1846 * side, because it could be determined by the opposite name in idmap 1847 * show. So this function must be called several times. 1848 * 1849 * Return values: -1 ... clear syntax error 1850 * 0 ... it wasnt possible to determine 1851 * 1 ... determined 1852 */ 1853 static int 1854 name2parts(char *name, name_mapping_t *nm, cmd_pos_t *pos) { 1855 char *it; 1856 int is_win = I_NO; 1857 int is_unix = I_NO; 1858 1859 if (nm->winname != NULL && nm->unixname != NULL) 1860 return (0); 1861 1862 /* If it starts with type string, that is easy: */ 1863 if (it = strchr(name, ':')) { 1864 if (strcmp_no0(name, ID_UNIXUSER ":") == 0) { 1865 if (nm->is_user == I_NO) { 1866 print_error(pos, 1867 gettext("%s - invalid combination of user" 1868 " and group identity types.\n"), 1869 ID_UNIXUSER); 1870 return (-1); 1871 } 1872 if (nm->unixname != NULL) 1873 return (0); 1874 1875 nm->is_user = I_YES; 1876 is_unix = I_YES; 1877 1878 } else if (strcmp_no0(name, ID_UNIXGROUP ":") == 0) { 1879 if (nm->is_user == I_YES) { 1880 print_error(pos, 1881 gettext("%s - invalid combination of user" 1882 " and group identity types.\n"), 1883 ID_UNIXGROUP); 1884 return (-1); 1885 } 1886 if (nm->unixname != NULL) 1887 return (0); 1888 1889 nm->is_user = I_NO; 1890 is_unix = I_YES; 1891 1892 } else if (strcmp_no0(name, ID_WINUSER ":") == 0) { 1893 if (nm->is_user == I_NO) { 1894 print_error(pos, 1895 gettext("%s - invalid combination of user" 1896 " and group identity types.\n"), 1897 ID_WINUSER); 1898 return (-1); 1899 } 1900 if (nm->winname != NULL) 1901 return (0); 1902 1903 nm->is_user = I_YES; 1904 is_win = I_YES; 1905 1906 } else if (strcmp_no0(name, ID_WINGROUP ":") == 0) { 1907 if (nm->is_user == I_YES) { 1908 print_error(pos, 1909 gettext("%s - invalid combination of user" 1910 " and group identity types.\n"), 1911 ID_WINGROUP); 1912 return (-1); 1913 } 1914 if (nm->winname != NULL) 1915 return (0); 1916 1917 nm->is_user = I_NO; 1918 is_win = I_YES; 1919 1920 } else if (strcmp_no0(name, ID_WINNAME ":") == 0) { 1921 if (nm->winname != NULL) 1922 return (0); 1923 is_win = I_YES; 1924 } else { 1925 print_error(pos, 1926 gettext("Error: invalid identity type \"%.*s\"\n"), 1927 it - name, name); 1928 return (-1); 1929 } 1930 name = it + 1; 1931 } 1932 1933 /* If it contains '@' or '\\', then it is a winname with domain */ 1934 if (!is_unix && nm->winname == NULL) { 1935 if ((it = strchr(name, '@')) != NULL) { 1936 int length = it - name + 1; 1937 nm->winname = (char *)malloc(length * sizeof (char)); 1938 (void) strncpy(nm->winname, name, length - 1); 1939 nm->winname[length - 1] = '\0'; 1940 nm->windomain = strdup(it + 1); 1941 return (1); 1942 } else if ((it = strrchr(name, '\\')) != NULL) { 1943 int length = it - name + 1; 1944 nm->windomain = (char *)malloc(length * sizeof (char)); 1945 (void) strncpy(nm->windomain, name, length - 1); 1946 nm->windomain[length - 1] = '\0'; 1947 nm->winname = strdup(it + 1); 1948 nm->is_nt4 = B_TRUE; 1949 return (1); 1950 } 1951 } 1952 1953 /* 1954 * if is_unix/is_win is not yet determined, then the last 1955 * hope is that the opposite side is known already. In that 1956 * case, it is the only remaining side. 1957 */ 1958 if (is_unix || nm->unixname == NULL && nm->winname != NULL) { 1959 if (strlen(name) == 0) 1960 nm->unixname = strdup("\"\""); 1961 else 1962 nm->unixname = strdup(name); 1963 return (1); 1964 } else if (is_win || nm->unixname != NULL && nm->winname == NULL) { 1965 if (strlen(name) == 0) 1966 nm->winname = strdup("\"\""); 1967 else 1968 nm->winname = strdup(name); 1969 nm->windomain = NULL; 1970 return (1); 1971 } 1972 1973 return (0); 1974 } 1975 1976 /* add command handler. */ 1977 static int 1978 do_add_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 1979 { 1980 name_mapping_t *nm; 1981 int rc = 0; 1982 int i; 1983 int is_argv0_unix = -1; 1984 idmap_stat stat; 1985 1986 1987 /* Exactly two arguments must be specified */ 1988 if (argc < 2) { 1989 print_error(pos, gettext("Not enough arguments.\n")); 1990 return (-1); 1991 } else if (argc > 2) { 1992 print_error(pos, gettext("Too many arguments.\n")); 1993 return (-1); 1994 } 1995 1996 /* 1997 * Direction can be determined by the opposite name, so we 1998 * need to run name2parts twice for the first name, i.e. 3x in 1999 * total. 2000 */ 2001 nm = name_mapping_init(); 2002 if (nm == NULL) 2003 return (-1); 2004 2005 for (i = 0; i < 3; i++) { 2006 switch (name2parts(argv[i % 2], nm, pos)) { 2007 case -1: 2008 name_mapping_fini(nm); 2009 return (-1); 2010 case 1: 2011 if (is_argv0_unix < 0) 2012 is_argv0_unix = 2013 i % 2 ^ (nm->unixname != NULL ? 1 : 0); 2014 break; 2015 } 2016 } 2017 2018 if (nm->winname == NULL || nm->unixname == NULL) { 2019 print_error(pos, gettext("Name types not determined.\n")); 2020 name_mapping_fini(nm); 2021 return (-1); 2022 } 2023 2024 if (f[d_FLAG] != NULL) 2025 nm->direction = is_argv0_unix 2026 ? IDMAP_DIRECTION_U2W 2027 : IDMAP_DIRECTION_W2U; 2028 else 2029 nm->direction = IDMAP_DIRECTION_BI; 2030 2031 /* Now let us write it: */ 2032 2033 if (init_udt_command()) { 2034 name_mapping_fini(nm); 2035 return (-1); 2036 } 2037 2038 stat = idmap_udt_add_namerule(udt, nm->windomain, 2039 nm->is_user ? B_TRUE : B_FALSE, nm->winname, nm->unixname, 2040 nm->is_nt4, nm->direction); 2041 2042 /* We echo the mapping */ 2043 (void) print_mapping_init(DEFAULT_FORMAT, stdout); 2044 (void) print_mapping(nm); 2045 (void) print_mapping_fini(); 2046 2047 if (stat < 0) { 2048 print_error(pos, 2049 gettext("Mapping not created (%s)\n"), 2050 idmap_stat2string(handle, stat)); 2051 rc = -1; 2052 } 2053 2054 if (rc == 0) 2055 rc = positions_add(pos); 2056 2057 cleanup: 2058 name_mapping_fini(nm); 2059 if (fini_udt_command(1, pos)) 2060 rc = -1; 2061 return (rc); 2062 } 2063 2064 /* remove command handler */ 2065 static int 2066 do_remove_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 2067 { 2068 name_mapping_t *nm; 2069 int rc = 0; 2070 int i; 2071 int is_argv0_unix = -1; 2072 idmap_stat stat; 2073 int is_user; 2074 2075 /* "-a" means we flush all of them */ 2076 if (f[a_FLAG] != NULL) { 2077 if (argc) { 2078 print_error(pos, 2079 gettext("Too many arguments.\n")); 2080 return (-1); 2081 } 2082 2083 if (init_udt_command()) 2084 return (-1); 2085 rc = flush_nm(B_TRUE, pos); 2086 2087 if (rc >= 0) 2088 rc = flush_nm(B_FALSE, pos); 2089 2090 if (fini_udt_command(rc ? 0 : 1, pos)) 2091 rc = -1; 2092 return (rc); 2093 } 2094 2095 /* Contrary to add_name_mapping, we can have only one argument */ 2096 if (argc < 1) { 2097 print_error(pos, gettext("Not enough arguments.\n")); 2098 return (-1); 2099 } else if (argc > 2) { 2100 print_error(pos, gettext("Too many arguments.\n")); 2101 return (-1); 2102 } else if ( 2103 /* both -f and -t: */ 2104 f[f_FLAG] != NULL && f[t_FLAG] != NULL || 2105 /* -d with a single argument: */ 2106 argc == 1 && f[d_FLAG] != NULL || 2107 /* -f or -t with two arguments: */ 2108 argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) { 2109 print_error(pos, 2110 gettext("Direction ambiguous.\n")); 2111 return (-1); 2112 } 2113 2114 2115 /* 2116 * Similar to do_add_name_mapping - see the comments 2117 * there. Except we may have only one argument here. 2118 */ 2119 nm = name_mapping_init(); 2120 if (nm == NULL) 2121 return (-1); 2122 2123 for (i = 0; i < 2 * argc - 1; i++) { 2124 switch (name2parts(argv[i % 2], nm, pos)) { 2125 case -1: 2126 name_mapping_fini(nm); 2127 return (-1); 2128 case 1: 2129 if (is_argv0_unix < 0) 2130 is_argv0_unix = i % 2 ^ (nm->unixname ? 1 : 0); 2131 break; 2132 } 2133 } 2134 2135 2136 if (nm->winname == NULL && nm->unixname == NULL) { 2137 print_error(pos, gettext("Name types not determined.\n")); 2138 name_mapping_fini(nm); 2139 return (-1); 2140 } 2141 2142 /* 2143 * If the direction is not specified by a -d/-f/-t flag, then it 2144 * is IDMAP_DIRECTION_UNDEF, because in that case we want to 2145 * remove any mapping. If it was IDMAP_DIRECTION_BI, idmap_api would 2146 * delete a bidirectional one only. 2147 */ 2148 if (f[d_FLAG] != NULL || f[f_FLAG] != NULL) 2149 nm->direction = is_argv0_unix 2150 ? IDMAP_DIRECTION_U2W 2151 : IDMAP_DIRECTION_W2U; 2152 else if (f[t_FLAG] != NULL) 2153 nm->direction = is_argv0_unix 2154 ? IDMAP_DIRECTION_W2U 2155 : IDMAP_DIRECTION_U2W; 2156 else 2157 nm->direction = IDMAP_DIRECTION_UNDEF; 2158 2159 if (init_udt_command()) { 2160 name_mapping_fini(nm); 2161 return (-1); 2162 } 2163 2164 for (is_user = I_YES; is_user >= I_NO; is_user--) { 2165 if ((is_user == I_YES && nm->is_user == I_NO) || 2166 (is_user == I_NO && nm->is_user == I_YES)) 2167 continue; 2168 2169 stat = idmap_udt_rm_namerule(udt, is_user ? B_TRUE : B_FALSE, 2170 nm->windomain, nm->winname, nm->unixname, nm->direction); 2171 2172 if (stat < 0) { 2173 print_error(pos, 2174 gettext("Mapping not deleted (%s)\n"), 2175 idmap_stat2string(handle, stat)); 2176 rc = -1; 2177 break; 2178 } 2179 } 2180 2181 if (rc == 0) 2182 rc = positions_add(pos); 2183 2184 cleanup: 2185 name_mapping_fini(nm); 2186 if (fini_udt_command(1, pos)) 2187 rc = -1; 2188 return (rc); 2189 } 2190 2191 2192 /* exit command handler */ 2193 static int 2194 /* LINTED E_FUNC_ARG_UNUSED */ 2195 do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos) { 2196 return (0); 2197 } 2198 2199 2200 /* debug command handler: just print the parameters */ 2201 static int 2202 /* LINTED E_STATIC_UNUSED */ 2203 debug_print_params(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 2204 { 2205 int i; 2206 #if 0 2207 char *leaktest = (char *)malloc(100); 2208 #endif 2209 2210 print_flags(f); 2211 2212 for (i = 0; i < argc; i++) { 2213 (void) printf("Argument %d: %s\n", i, argv[i]); 2214 } 2215 2216 (void) fflush(stdout); 2217 return (0); 2218 } 2219 2220 /* 2221 * Return a pointer after a given prefix. If there is no such prefix, 2222 * return NULL 2223 */ 2224 static char * 2225 get_root(char *string, char *typestring) { 2226 if (strcasecmp_no0(string, typestring) != 0) 2227 return (NULL); 2228 return (string + strlen(typestring)); 2229 } 2230 2231 /* 2232 * From name_mapping_t, asseble a string containing identity of the 2233 * given type. 2234 */ 2235 static int 2236 nm2type(name_mapping_t *nm, int type, char **to) { 2237 switch (type) { 2238 case TYPE_SID: 2239 if (nm->sidprefix == NULL) 2240 return (-1); 2241 *to = sid_format(nm->sidprefix, nm->rid); 2242 return (0); 2243 case TYPE_WN: 2244 case TYPE_WU: 2245 case TYPE_WG: 2246 return (nm2winqn(nm, to)); 2247 case TYPE_UID: 2248 case TYPE_GID: 2249 case TYPE_PID: 2250 *to = pid_format(nm->pid, nm->is_user); 2251 if (*to == NULL) 2252 return (-1); 2253 else 2254 return (0); 2255 case TYPE_UN: 2256 case TYPE_UU: 2257 case TYPE_UG: 2258 return (nm2unixname(nm, to)); 2259 default: 2260 /* This can never happen: */ 2261 print_error(NULL, 2262 gettext("Internal error: invalid name type.\n")); 2263 return (-1); 2264 } 2265 /* never reached */ 2266 } 2267 2268 /* show command handler */ 2269 static int 2270 do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos) 2271 { 2272 idmap_stat stat = 0; 2273 int flag; 2274 idmap_stat map_stat = 0; 2275 int type_from; 2276 int type_to; 2277 char *root; 2278 name_mapping_t *nm = NULL; 2279 char *fromname; 2280 char *toname; 2281 2282 if (argc == 0) { 2283 print_error(pos, 2284 gettext("No identity given\n")); 2285 return (-1); 2286 } else if (argc > 2) { 2287 print_error(pos, 2288 gettext("Too many arguments.\n")); 2289 return (-1); 2290 } 2291 2292 flag = f[c_FLAG] != NULL ? 0 : IDMAP_REQ_FLG_NO_NEW_ID_ALLOC; 2293 2294 if (init_command()) 2295 return (-1); 2296 2297 nm = name_mapping_init(); 2298 if (nm == NULL) 2299 goto cleanup; 2300 2301 /* First, determine type_from: */ 2302 if ((root = get_root(argv[0], ID_UID ":")) != NULL) 2303 type_from = TYPE_UID; 2304 else if ((root = get_root(argv[0], ID_GID ":")) != NULL) 2305 type_from = TYPE_GID; 2306 else if ((root = get_root(argv[0], ID_SID ":")) != NULL) 2307 type_from = TYPE_SID; 2308 else 2309 switch (name2parts(argv[0], nm, pos)) { 2310 case 1: /* name2parts determined the type */ 2311 if (nm->unixname != NULL) 2312 switch (nm->is_user) { 2313 case I_YES: 2314 type_from = TYPE_UU; 2315 break; 2316 case I_NO: 2317 type_from = TYPE_UG; 2318 break; 2319 default: 2320 type_from = TYPE_UN; 2321 } 2322 else 2323 switch (nm->is_user) { 2324 case I_YES: 2325 type_from = TYPE_WU; 2326 break; 2327 case I_NO: 2328 type_from = TYPE_WG; 2329 break; 2330 default: 2331 type_from = TYPE_WN; 2332 } 2333 break; 2334 case 0: /* name2parts didn't determine the type: */ 2335 print_error(pos, gettext("Invalid type.\n")); 2336 /* LINTED E_CASE_FALLTHRU */ 2337 case -1: 2338 stat = IDMAP_ERR_ARG; 2339 goto cleanup; 2340 } 2341 2342 /* Second, determine type_to: */ 2343 if (argc < 2) { 2344 type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID; 2345 if (type_from & IS_NAME) 2346 type_to |= IS_NAME; 2347 } else if (strcasecmp(argv[1], ID_UID) == 0) 2348 type_to = TYPE_UID; 2349 else if (strcasecmp(argv[1], ID_GID) == 0) 2350 type_to = TYPE_GID; 2351 else if (strcasecmp(argv[1], ID_SID) == 0) 2352 type_to = TYPE_SID; 2353 else if (strcmp(argv[1], ID_UNIXUSER) == 0) 2354 type_to = TYPE_UU; 2355 else if (strcmp(argv[1], ID_UNIXGROUP) == 0) 2356 type_to = TYPE_UG; 2357 else if (strcmp(argv[1], ID_WINNAME) == 0) 2358 type_to = TYPE_WN; 2359 else if (strcmp(argv[1], ID_WINUSER) == 0) 2360 type_to = TYPE_WU; 2361 else if (strcmp(argv[1], ID_WINGROUP) == 0) 2362 type_to = TYPE_WG; 2363 else { 2364 print_error(pos, 2365 gettext("Invalid target type.\n")); 2366 stat = IDMAP_ERR_ARG; 2367 goto cleanup; 2368 } 2369 2370 /* Are both arguments the same OS side? */ 2371 if (!(type_from & IS_WIN ^ type_to & IS_WIN)) { 2372 print_error(pos, 2373 gettext("Direction ambiguous.\n")); 2374 stat = IDMAP_ERR_ARG; 2375 goto cleanup; 2376 } 2377 2378 /* Are both arguments the same userness/groupness? */ 2379 if ((type_from & IS_USER) && (type_to & IS_GROUP)) { 2380 print_error(pos, 2381 gettext("Mapping of user to group impossible.\n")); 2382 stat = IDMAP_ERR_ARG; 2383 goto cleanup; 2384 } else if ((type_from & IS_GROUP) && (type_to & IS_USER)) { 2385 print_error(pos, 2386 gettext("Mapping of group to user impossible.\n")); 2387 stat = IDMAP_ERR_ARG; 2388 goto cleanup; 2389 } 2390 2391 if (type_from == TYPE_SID) { 2392 if (!sid_convert(root, &nm->sidprefix, &nm->rid, pos)) { 2393 stat = IDMAP_ERR_ARG; 2394 goto cleanup; 2395 } 2396 } else if (type_from == TYPE_UID || type_from == TYPE_GID) { 2397 if (!pid_convert(root, &nm->pid, type_from, pos)) { 2398 stat = IDMAP_ERR_ARG; 2399 goto cleanup; 2400 } 2401 } 2402 2403 /* 2404 * We have two interfaces for retrieving the mappings: 2405 * idmap_get_sidbyuid & comp (the batch interface) and 2406 * idmap_get_w2u_mapping & comp. We want to use both of them, because 2407 * the former mimicks kernel interface better and the later offers the 2408 * string names. In the batch case, our batch has always size 1. 2409 */ 2410 if (type_from & IS_NAME || type_to & IS_NAME) { 2411 if (type_from & IS_WIN) { 2412 if (type_to == TYPE_UID) 2413 nm->is_user = I_YES; 2414 else if (type_to == TYPE_GID) 2415 nm->is_user = I_NO; 2416 2417 map_stat = idmap_get_w2u_mapping(handle, 2418 nm->sidprefix, 2419 &nm->rid, 2420 nm->winname, 2421 nm->windomain, 2422 flag, 2423 &nm->is_user, 2424 &nm->pid, 2425 &nm->unixname, 2426 &nm->direction); 2427 } else { 2428 if (type_from == TYPE_UID) 2429 nm->is_user = I_YES; 2430 else if (type_from == TYPE_GID) 2431 nm->is_user = I_NO; 2432 2433 map_stat = idmap_get_u2w_mapping(handle, 2434 &nm->pid, 2435 nm->unixname, 2436 flag, 2437 nm->is_user, 2438 &nm->sidprefix, 2439 &nm->rid, 2440 &nm->winname, 2441 &nm->windomain, 2442 &nm->direction); 2443 } 2444 2445 } else { 2446 /* batch handle */ 2447 idmap_get_handle_t *ghandle = NULL; 2448 /* To be passed to idmap_get_uidbysid */ 2449 gid_t gid = UNDEFINED_GID; 2450 /* To be passed to idmap_get_gidbysid */ 2451 uid_t uid = UNDEFINED_UID; 2452 2453 2454 /* Create an in-memory structure for all the batch: */ 2455 stat = idmap_get_create(handle, &ghandle); 2456 if (stat < 0) { 2457 print_error(pos, 2458 gettext("Unable to create handle for communicating" 2459 " with idmapd(1M) (%s)\n"), 2460 idmap_stat2string(handle, stat)); 2461 idmap_get_destroy(ghandle); 2462 goto cleanup; 2463 } 2464 2465 /* Schedule the request: */ 2466 if (type_from == TYPE_SID && type_to == TYPE_UID) { 2467 stat = idmap_get_uidbysid(ghandle, 2468 nm->sidprefix, 2469 nm->rid, 2470 flag, 2471 &uid, 2472 &map_stat); 2473 nm->is_user = I_YES; 2474 } else if (type_from == TYPE_SID && type_to == TYPE_GID) { 2475 stat = idmap_get_gidbysid(ghandle, 2476 nm->sidprefix, 2477 nm->rid, 2478 flag, 2479 &gid, 2480 &map_stat); 2481 nm->is_user = I_NO; 2482 } else if (type_from == TYPE_SID && type_to == TYPE_PID) 2483 stat = idmap_get_pidbysid(ghandle, 2484 nm->sidprefix, 2485 nm->rid, 2486 flag, 2487 &nm->pid, 2488 &nm->is_user, 2489 &map_stat); 2490 else if (type_from == TYPE_UID && type_to == TYPE_SID) { 2491 stat = idmap_get_sidbyuid(ghandle, 2492 nm->pid, 2493 flag, 2494 &nm->sidprefix, 2495 &nm->rid, 2496 &map_stat); 2497 nm->is_user = I_YES; 2498 } else if (type_from == TYPE_GID && type_to == TYPE_SID) { 2499 stat = idmap_get_sidbygid(ghandle, 2500 (gid_t)nm->pid, 2501 flag, 2502 &nm->sidprefix, 2503 &nm->rid, 2504 &map_stat); 2505 nm->is_user = I_NO; 2506 } else { 2507 /* This can never happen: */ 2508 print_error(pos, 2509 gettext("Internal error in show.\n")); 2510 exit(1); 2511 } 2512 2513 if (stat < 0) { 2514 print_error(pos, 2515 gettext("Request for %.3s not sent (%s)\n"), 2516 argv[0], idmap_stat2string(handle, stat)); 2517 idmap_get_destroy(ghandle); 2518 goto cleanup; 2519 } 2520 2521 /* Send the batch to idmapd and obtain results: */ 2522 stat = idmap_get_mappings(ghandle); 2523 if (stat < 0) { 2524 print_error(pos, 2525 gettext("Mappings not obtained because of" 2526 " RPC problem (%s)\n"), 2527 idmap_stat2string(handle, stat)); 2528 idmap_get_destroy(ghandle); 2529 goto cleanup; 2530 } 2531 2532 /* Destroy the batch handle: */ 2533 idmap_get_destroy(ghandle); 2534 2535 if (type_to == TYPE_UID) 2536 nm->pid = uid; 2537 else if (type_to == TYPE_GID) 2538 nm->pid = (uid_t)gid; 2539 2540 } 2541 2542 /* 2543 * If there was -c flag, we do output whatever we can even in 2544 * the case of error: 2545 */ 2546 if (map_stat < 0) { 2547 print_error(pos, 2548 gettext("%s\n"), 2549 idmap_stat2string(handle, map_stat)); 2550 if (flag == IDMAP_REQ_FLG_NO_NEW_ID_ALLOC) 2551 goto cleanup; 2552 } 2553 2554 2555 /* 2556 * idmapd returns fallback uid/gid in case of errors. However 2557 * it uses special sentinel value i.e 4294967295 (or -1) to 2558 * indicate that falbback pid is not available either. In such 2559 * case idmap(1M) should not display the mapping because there 2560 * is no fallback mapping. 2561 */ 2562 2563 if (type_to == TYPE_UID && nm->pid == UNDEFINED_UID || 2564 type_to == TYPE_GID && nm->pid == (uid_t)UNDEFINED_GID) { 2565 goto cleanup; 2566 } 2567 2568 if (nm2type(nm, type_from, &fromname) < 0) 2569 goto cleanup; 2570 2571 if (nm2type(nm, type_to, &toname) < 0) { 2572 if (flag == 0) 2573 (void) printf("%s -> %s:%u\n", 2574 fromname, 2575 (type_from | type_to) & IS_GROUP ? ID_GID : ID_UID, 2576 UID_NOBODY); 2577 free(fromname); 2578 goto cleanup; 2579 } 2580 2581 (void) printf("%s -> %s\n", fromname, toname); 2582 free(fromname); 2583 free(toname); 2584 2585 cleanup: 2586 if (nm != NULL) 2587 name_mapping_fini(nm); 2588 fini_command(); 2589 return (stat < 0 || map_stat < 0 ? -1 : 0); 2590 } 2591 2592 /* main function. Returns 1 for error, 0 otherwise */ 2593 int 2594 main(int argc, char *argv[]) { 2595 int rc; 2596 2597 /* set locale and domain for internationalization */ 2598 (void) setlocale(LC_ALL, ""); 2599 (void) textdomain(TEXT_DOMAIN); 2600 2601 /* idmap_engine determines the batch_mode: */ 2602 rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t), 2603 commands, 2604 argc - 1, 2605 argv + 1, 2606 &batch_mode); 2607 2608 if (rc < 0) { 2609 (void) engine_fini(); 2610 if (rc == IDMAP_ENG_ERROR_SILENT) 2611 help(); 2612 return (1); 2613 } 2614 2615 udt_used = 0; 2616 if (batch_mode) { 2617 if (init_udt_batch() < 0) 2618 return (1); 2619 } 2620 2621 rc = run_engine(argc - 1, argv + 1); 2622 2623 if (batch_mode) { 2624 batch_mode = 0; 2625 if (fini_udt_command(rc == 0 ? 1 : 0, NULL)) 2626 rc = -1; 2627 } 2628 2629 (void) engine_fini(); 2630 return (rc == 0 ? 0 : 1); 2631 } 2632