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