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