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