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