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