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