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