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