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