xref: /illumos-gate/usr/src/cmd/idmap/idmap/idmap.c (revision 7800901e60d340b6af88e94a2149805dcfcaaf56)
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 2007 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 
38 /* Initialization values for pids/rids: */
39 
40 #define	UNDEFINED_UID (uid_t)-1
41 #define	UNDEFINED_GID (gid_t)-1
42 #define	UNDEFINED_RID (idmap_rid_t)-1;
43 
44 /* is_user values */
45 
46 #define	I_YES 1
47 #define	I_NO 0
48 #define	I_UNKNOWN -1
49 
50 /*
51  * used in do_show for the type of argument, which can be winname,
52  * unixname, uid, gid, sid or not given at all:
53  */
54 
55 #define	TYPE_SID	0x010	/* sid */
56 #define	TYPE_WN		0x110	/* winname */
57 #define	TYPE_WU		0x111	/* winuser */
58 #define	TYPE_WG		0x112	/* wingroup */
59 #define	TYPE_UID	0x001	/* uid */
60 #define	TYPE_GID	0x002	/* gid */
61 #define	TYPE_PID	0x000	/* pid */
62 #define	TYPE_UN		0x100	/* unixname */
63 #define	TYPE_UU		0x101	/* unixuser */
64 #define	TYPE_UG		0x102	/* unixgroup */
65 
66 #define	IS_WIN		0x010	/* mask for the windows types */
67 #define	IS_NAME		0x100	/* mask for string name types */
68 #define	IS_USER		0x001	/* mask for user types */
69 #define	IS_GROUP	0x002	/* mask for group types */
70 
71 
72 /* Identity type strings */
73 
74 #define	ID_WINNAME	"winname"
75 #define	ID_UNIXNAME	"unixname"
76 #define	ID_UNIXUSER	"unixuser"
77 #define	ID_UNIXGROUP	"unixgroup"
78 #define	ID_WINUSER	"winuser"
79 #define	ID_WINGROUP	"wingroup"
80 #define	ID_SID	"sid"
81 #define	ID_UID	"uid"
82 #define	ID_GID	"gid"
83 
84 /* Flags */
85 
86 #define	f_FLAG	'f'
87 #define	t_FLAG	't'
88 #define	d_FLAG	'd'
89 #define	F_FLAG	'F'
90 #define	a_FLAG	'a'
91 #define	n_FLAG	'n'
92 #define	c_FLAG	'c'
93 
94 
95 /* used in the function do_import */
96 #define	MAX_INPUT_LINE_SZ 2047
97 
98 
99 typedef struct {
100 	int is_user;
101 	int direction;
102 	boolean_t is_nt4;
103 	char *unixname;
104 	char *winname;
105 	char *windomain;
106 	char *sidprefix;
107 	idmap_rid_t rid;
108 	uid_t pid;
109 } name_mapping_t;
110 
111 /*
112  * Formats of the output:
113  *
114  * Idmap reads/prints mappings in several formats: ordinary mappings,
115  * name mappings in Samba username map format (smbusers), Netapp
116  * usermap.cfg.
117  *
118  * DEFAULT_FORMAT are in fact the idmap subcommands suitable for
119  * piping to idmap standart input. For example
120  * add -d winuser:bob@foo.com unixuser:fred
121  * add -d winuser:bob2bar.com unixuser:fred
122  *
123  * SMBUSERS is the format of Samba username map (smbusers). For full
124  * documentation, search for "username map" in smb.conf manpage.
125  * The format is for example
126  *    fred = bob@foo.com bob2@bar.com
127  *
128  * USERMAP_CFG is the format of Netapp usermap.cfg file. Search
129  * http://www.netapp.com/ for more documentation. IP qualifiers are not
130  * supported.
131  * The format is for example
132  *    bob@foo.com => fred
133  *    "Bob With Spaces"@bar.com => fred  #comment
134  *
135  * The previous formats were for name rules. MAPPING_NAME and
136  * MAPPING_ID are for the actual mappings, as seen in show/dump
137  * commands. MAPPING_NAME prefers the string names of the user over
138  * their numerical identificators. MAPPING_ID prints just the
139  * identificators.
140  * Example of the MAPPING_NAME:
141  *   winname:bob@foo.com -> unixname:fred
142  *
143  * Example of the MAPPING_ID:
144  *   sid:S-1-2-3-4 -> uid:5678
145  */
146 
147 typedef enum {
148 	UNDEFINED_FORMAT = -1,
149 	DEFAULT_FORMAT = 0,
150 	MAPPING_ID,
151 	MAPPING_NAME,
152 	USERMAP_CFG,
153 	SMBUSERS
154 } format_t;
155 
156 /* Gives the format to use. Set in print_mapping_init  */
157 static format_t pnm_format;
158 
159 /* The file for print_mapping_init output. Mostly just stdout. */
160 static FILE *pnm_file;
161 
162 /* In smbusers format, more unixnames can be aggregated to one line. */
163 static char *pnm_last_unixname;
164 
165 /*
166  * idmap_api batch related variables:
167  *
168  * idmap can operate in two modes. It the batch mode, the idmap_api
169  * batch is committed at the end of a batch of several
170  * commands. At the end of input file, typically. This mode is used
171  * for processing input from a file.
172  *  In the non-batch mode, each command is committed immediately. This
173  * mode is used for tty input.
174  */
175 
176 /* Are we in the batch mode? */
177 static int batch_mode = 0;
178 
179 /* Self describing stricture for positions */
180 struct pos_sds {
181 	int size;
182 	int last;
183 	cmd_pos_t *pos[1];
184 };
185 
186 static struct pos_sds *positions;
187 
188 /* Handles for idmap_api batch */
189 static idmap_handle_t *handle = NULL;
190 static idmap_udt_handle_t *udt = NULL;
191 
192 /* Do we need to commit the udt batch at the end? */
193 static int udt_used;
194 
195 /* Command handlers */
196 
197 static int do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
198 static int do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
199 static int do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
200 static int do_list_name_mappings(flag_t *f, int argc, char **argv,
201     cmd_pos_t *pos);
202 static int do_add_name_mapping(flag_t *f, int argc, char **argv,
203     cmd_pos_t *pos);
204 static int do_remove_name_mapping(flag_t *f, int argc, char **argv,
205     cmd_pos_t *pos);
206 static int do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
207 static int do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
208 static int do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
209 
210 /* Command names and their hanlers to be passed to idmap_engine */
211 
212 static cmd_ops_t commands[] = {
213 	{
214 		"show",
215 		"c(create)",
216 		do_show_mapping
217 	},
218 	{
219 		"dump",
220 		"n(names)",
221 		do_dump
222 	},
223 	{
224 		"import",
225 		"F(flush)f:(file)",
226 		do_import
227 	},
228 	{
229 		"export",
230 		"f:(file)",
231 		do_export
232 	},
233 	{
234 		"list",
235 		"",
236 		do_list_name_mappings
237 	},
238 	{
239 		"add",
240 		"d(directional)",
241 		do_add_name_mapping
242 	},
243 	{
244 		"remove",
245 		"a(all)t(to)f(from)d(directional)",
246 		do_remove_name_mapping
247 	},
248 	{
249 		"exit",
250 		"",
251 		do_exit
252 	},
253 	{
254 		"help",
255 		"",
256 		do_help
257 	}
258 };
259 
260 /* Print error message, possibly with a position */
261 /* printflike */
262 static void
263 print_error(cmd_pos_t *pos, const char *format, ...) {
264 	size_t length;
265 
266 	va_list ap;
267 
268 	va_start(ap, format);
269 
270 	if (pos != NULL) {
271 		length = strlen(pos->line);
272 
273 		/* Skip newlines etc at the end: */
274 		while (length > 0 && isspace(pos->line[length - 1]))
275 			length--;
276 
277 		(void) fprintf(stderr,
278 		    gettext("Error at line %d: %.*s\n"),
279 		    pos->linenum,
280 		    length,
281 		    pos->line);
282 	}
283 	(void) vfprintf(stderr, format, ap);
284 
285 	va_end(ap);
286 }
287 
288 /* Inits positions sds. 0 means everything went OK, -1 for errors */
289 static int
290 init_positions() {
291 	int init_size = 32; /* Initial size of the positions array */
292 
293 	positions = (struct pos_sds *) malloc(sizeof (struct pos_sds) +
294 	    (init_size - 1) * sizeof (cmd_pos_t *));
295 
296 	if (positions == NULL) {
297 		print_error(NULL, gettext("Not enough memory.\n"));
298 		return (-1);
299 	}
300 
301 	positions->size = init_size;
302 	positions->last = 0;
303 	return (0);
304 }
305 
306 /* Free the positions array */
307 static void
308 fini_positions() {
309 	int i;
310 	for (i = 0; i < positions->last; i++) {
311 		if (positions->pos[i] == NULL)
312 			continue;
313 		free(positions->pos[i]->line);
314 		free(positions->pos[i]);
315 	}
316 	free(positions);
317 
318 	positions = NULL;
319 }
320 
321 /*
322  * Add another position to the positions array. 0 means everything
323  * went OK, -1 for errors
324  */
325 static int
326 positions_add(cmd_pos_t *pos) {
327 	if (positions->last >= positions->size) {
328 		positions->size *= 2;
329 		positions = (struct pos_sds *)realloc(positions,
330 		    sizeof (struct pos_sds) +
331 		    (positions->size - 1) * sizeof (cmd_pos_t *));
332 		if (positions == NULL)
333 			goto nomemory;
334 	}
335 
336 	if (pos == NULL)
337 		positions->pos[positions->last] = NULL;
338 	else {
339 		positions->pos[positions->last] = (cmd_pos_t *)calloc(1,
340 		    sizeof (cmd_pos_t));
341 		if (positions->pos[positions->last] == NULL)
342 			goto nomemory;
343 
344 		*positions->pos[positions->last] = *pos;
345 		positions->pos[positions->last]->line = strdup(pos->line);
346 		if (positions->pos[positions->last]->line == NULL)
347 			goto nomemory;
348 	}
349 
350 	positions->last++;
351 	return (0);
352 
353 nomemory:
354 	print_error(NULL, gettext("Not enough memory.\n"));
355 	return (-1);
356 }
357 
358 
359 
360 
361 /*
362  * Compare two strings just like strcmp, but stop before the end of
363  * the s2
364  */
365 static int
366 strcmp_no0(const char *s1, const char *s2) {
367 	return (strncmp(s1, s2, strlen(s2)));
368 }
369 
370 /* The same as strcmp_no0, but case insensitive. */
371 static int
372 strcasecmp_no0(const char *s1, const char *s2) {
373 	return (strncasecmp(s1, s2, strlen(s2)));
374 }
375 
376 
377 /* Print help message */
378 static void
379 help() {
380 	(void) fprintf(stderr,
381 	    "idmap\n"
382 	    "idmap -f command-file\n"
383 	    "idmap show [-c] identity [targettype]\n"
384 	    "idmap dump [-n]\n"
385 	    "idmap add [-d] name1 name2\n"
386 	    "idmap remove -a\n"
387 	    "idmap remove name\n"
388 	    "idmap remove [-d] name1 name2\n"
389 	    "idmap list\n"
390 	    "idmap import [-F] [-f file] format\n"
391 	    "idmap export [-f file] format\n"
392 	    "idmap help\n");
393 }
394 
395 /* The handler for the "help" command. */
396 static int
397 /* LINTED E_FUNC_ARG_UNUSED */
398 do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
399 {
400 	help();
401 	return (0);
402 }
403 
404 /* Initialization of the idmap api batch */
405 static int
406 init_batch() {
407 	idmap_stat stat;
408 
409 	stat = idmap_init(&handle);
410 	if (stat < 0) {
411 		print_error(NULL,
412 		    gettext("Connection not established (%s)\n"),
413 		    idmap_stat2string(NULL, stat));
414 		return (-1);
415 	}
416 
417 	return (0);
418 }
419 
420 /* Initialization common to all commands */
421 static int
422 init_command() {
423 	if (batch_mode)
424 		return (0);
425 
426 	return (init_batch());
427 }
428 
429 /* Finalization common to all commands */
430 static void
431 fini_command() {
432 	if (batch_mode)
433 		return;
434 	(void) idmap_fini(handle);
435 	handle = NULL;
436 }
437 
438 /* Initialization of the commands which perform write operations  */
439 static int
440 init_udt_batch() {
441 	idmap_stat stat;
442 
443 	if (init_batch())
444 		return (-1);
445 
446 	stat = idmap_udt_create(handle, &udt);
447 	if (stat < 0) {
448 		print_error(NULL,
449 		    gettext("Error initiating transaction (%s)"),
450 		    idmap_stat2string(handle, stat));
451 		return (-1);
452 	}
453 
454 	if (init_positions() < 0)
455 		return (-1);
456 
457 	return (0);
458 }
459 
460 
461 /* Finalization of the write commands  */
462 static int
463 init_udt_command() {
464 	udt_used = 1;
465 	if (batch_mode)
466 		return (0);
467 
468 	return (init_udt_batch());
469 }
470 
471 
472 /* If everythings is OK, send the udt batch to idmapd  */
473 static int
474 fini_udt_command(int ok, cmd_pos_t *pos) {
475 	int rc = 0;
476 	int64_t failpos;
477 	idmap_stat stat, stat1;
478 	cmd_pos_t *reported_pos;
479 
480 	if (batch_mode)
481 		return (0);
482 	if (udt == NULL) {
483 		print_error(pos,
484 		    gettext("Internal error: uninitiated batch.\n"));
485 		return (-1);
486 	}
487 
488 	if (ok && udt_used) {
489 		stat = idmap_udt_commit(udt);
490 		if (stat == IDMAP_SUCCESS)
491 			goto out;
492 
493 		rc = -1;
494 
495 		stat1 = idmap_udt_get_error_index(udt, &failpos);
496 		if (stat1 != IDMAP_SUCCESS) {
497 			print_error(NULL,
498 			    gettext("Error diagnosing transaction (%s)\n"),
499 			    idmap_stat2string(handle, stat1));
500 			goto out;
501 		}
502 
503 
504 		if (failpos < 0)
505 			reported_pos = pos;
506 		else
507 			reported_pos = positions->pos[failpos];
508 
509 		print_error(reported_pos,
510 		    gettext("Error commiting transaction (%s)\n"),
511 		    idmap_stat2string(handle, stat));
512 	}
513 
514 out:
515 	idmap_udt_destroy(udt);
516 	udt = NULL;
517 	udt_used = 0;
518 	fini_command();
519 	fini_positions();
520 	return (rc);
521 }
522 
523 /* Convert numeric expression of the direction to it's string form */
524 static char *
525 direction2string(int direction) {
526 	switch (direction) {
527 	case IDMAP_DIRECTION_BI:
528 		return ("==");
529 	case IDMAP_DIRECTION_W2U:
530 		return ("=>");
531 	case IDMAP_DIRECTION_U2W:
532 		return ("<=");
533 	default:
534 		/* This can never happen: */
535 		print_error(NULL,
536 		    gettext("Internal error: invalid direction.\n"));
537 		return ("");
538 	}
539 	/* never reached */
540 }
541 
542 /* Do we need quotation marks around winname in the USERMAP_CFG format? */
543 static int
544 needs_protection(char *what) {
545 	if (strchr(what, ' ') != NULL)
546 		return (1);
547 
548 	if (strchr(what, '\t') != NULL)
549 		return (1);
550 
551 	if (strchr(what, '#') != NULL)
552 		return (1);
553 
554 	return (0);
555 }
556 
557 
558 /*
559  * Returns 1 if c is a shell-meta-character requiring quoting, 0
560  * otherwise.
561  *
562  * We don't quote '*' and ':' because they cannot do any harm
563  * a) they have no meaning to idmap_engine b) even ifsomebody copy &
564  * paste idmap output to a shell commandline, there is the identity
565  * type string in front of them. On the other hand, '*' and ':' are
566  * everywhere.
567  */
568 static int
569 is_shell_special(char c) {
570 	if (isspace(c))
571 		return (1);
572 
573 	if (strchr("&^{}#;'\"\\`!$()[]><|~", c) != NULL)
574 		return (1);
575 
576 	return (0);
577 }
578 
579 /*
580  * Returns 1 if c is a shell-meta-character requiring quoting even
581  * inside double quotes, 0 otherwise. It means \, " and $ .
582  *
583  * This set of characters is a subset of those in is_shell_special().
584  */
585 static int
586 is_dq_special(char c) {
587 	if (strchr("\\\"$", c) != NULL)
588 		return (1);
589 	return (0);
590 }
591 
592 
593 
594 
595 /*
596  * Quote any shell meta-characters in the given string.  If 'quote' is
597  * true then use double-quotes to quote the whole string, else use
598  * back-slash to quote each individual meta-character.
599  *
600  * The resulting string is placed in *res.  Callers must free *res if the
601  * return value isn't 0 (even if the given string had no meta-chars).
602  * If there are any errors this returns -1, else 0.
603  */
604 static int
605 shell_app(char **res, char *string, int quote) {
606 	int i, j;
607 	uint_t noss = 0; /* Number Of Shell Special chars in the input */
608 	uint_t noqb = 0; /* Number Of Quotes and Backslahes in the input */
609 	char *out;
610 	size_t len_orig = strlen(string);
611 	size_t len;
612 
613 	/* First, let us count how many characters we need to quote: */
614 	for (i = 0; i < len_orig; i++) {
615 		if (is_shell_special(string[i])) {
616 			noss++;
617 			if (is_dq_special(string[i]))
618 				noqb++;
619 		}
620 
621 	}
622 
623 	/* Do we need to quote at all? */
624 	if (noss == 0) {
625 		out = strdup(string);
626 		if (out == NULL) {
627 			print_error(NULL, gettext("Not enough memory.\n"));
628 			return (-1);
629 		}
630 		*res = out;
631 		return (0);
632 	}
633 
634 	/* What is the length of the result? */
635 	if (quote)
636 		len = strlen(string) + 2 + noqb + 1; /* 2 for quotation marks */
637 	else
638 		len = strlen(string) + noss + 1;
639 
640 	out = (char *)malloc(len * sizeof (char));
641 	if (out == NULL) {
642 		print_error(NULL, gettext("Not enough memory.\n"));
643 		return (-1);
644 	}
645 
646 	j = 0;
647 	if (quote)
648 		out[j++] = '"';
649 
650 	for (i = 0; i < len_orig; i++) {
651 		/* Quote the dangerous chars by a backslash */
652 		if (quote && is_dq_special(string[i]) ||
653 			(!quote && is_shell_special(string[i]))) {
654 			out[j++] = '\\';
655 		}
656 		out[j++] = string[i];
657 	}
658 
659 	if (quote)
660 		out[j++] = '"';
661 
662 	out[j] = '\0';
663 	*res = out;
664 	return (0);
665 }
666 
667 /* Assemble string form sid */
668 static char *
669 sid_format(char *sidprefix, idmap_rid_t rid) {
670 	char *to;
671 	size_t len;
672 
673 	/* 'sid:' + sidprefix + '-' + rid + '\0' */
674 	len = strlen(sidprefix) + 6 + 3 * sizeof (rid);
675 	to = (char *)malloc(len * sizeof (char));
676 	if (to == NULL)
677 		return (NULL);
678 
679 	(void) snprintf(to, len, "sid:%s-%u", sidprefix, rid);
680 	return (to);
681 }
682 
683 /* Assemble string form uid or gid */
684 static char *
685 pid_format(uid_t from, int is_user) {
686 	char *to;
687 	size_t len;
688 
689 	/* ID_UID ":" + uid + '\0' */
690 	len = 5 + 3 * sizeof (uid_t);
691 	to = (char *)malloc(len * sizeof (char));
692 	if (to == NULL)
693 		return (NULL);
694 
695 	(void) snprintf(to, 16, "%s:%u", is_user ? ID_UID : ID_GID, from);
696 	return (to);
697 }
698 
699 /* Assemble winname, e.g. "winuser:bob@foo.sun.com", from name_mapping_t */
700 static int
701 nm2winqn(name_mapping_t *nm, char **winqn) {
702 	char *out;
703 	size_t length = 0;
704 	int is_domain = 1;
705 	char *prefix;
706 
707 	/* Sometimes there are no text names. Return a sid, then. */
708 	if (nm->winname == NULL) {
709 		if (nm->sidprefix == NULL)
710 			return (-1);
711 
712 		*winqn = sid_format(nm->sidprefix, nm->rid);
713 		return (0);
714 	}
715 
716 	switch (nm->is_user) {
717 	case I_YES:
718 		prefix = ID_WINUSER ":";
719 		break;
720 	case I_NO:
721 		prefix = ID_WINGROUP ":";
722 		break;
723 	case I_UNKNOWN:
724 		prefix = ID_WINNAME ":";
725 		break;
726 
727 	}
728 
729 	length = strlen(prefix) + strlen(nm->winname);
730 
731 	/* Windomain is not mandatory: */
732 	if (nm->windomain == NULL ||
733 	    *nm->winname == '\0' ||
734 	    strcmp(nm->winname, "\"\"") == 0)
735 		is_domain = 0;
736 	else
737 		length += strlen(nm->windomain) + 1;
738 
739 	out = (char *)malloc((length + 1) * sizeof (char));
740 	if (out == NULL) {
741 		print_error(NULL,
742 		    gettext("Not enough memory.\n"));
743 		return (-1);
744 	}
745 
746 	(void) strcpy(out, prefix);
747 
748 	if (!is_domain)
749 		(void) strcat(out, nm->winname);
750 	else if (nm->is_nt4) {
751 		(void) strcat(out, nm->windomain);
752 		(void) strcat(out, "\\");
753 		(void) strcat(out, nm->winname);
754 	} else {
755 		(void) strcat(out, nm->winname);
756 		(void) strcat(out, "@");
757 		(void) strcat(out, nm->windomain);
758 	}
759 
760 	*winqn = out;
761 	return (0);
762 }
763 
764 /* Assemble a text unixname, e.g. unixuser:fred */
765 static int
766 nm2unixname(name_mapping_t *nm, char **unixname) {
767 	size_t length = 0;
768 	char *out;
769 	char *it;
770 	char *prefix;
771 
772 	/* Sometimes there is no name, just pid: */
773 	if (nm->unixname == NULL) {
774 		if (nm->pid == UNDEFINED_UID)
775 			return (-1);
776 
777 		*unixname = pid_format(nm->pid, nm->is_user);
778 		return (0);
779 	}
780 
781 	if (shell_app(&it, nm->unixname, 0))
782 		return (-1);
783 
784 
785 	switch (nm->is_user) {
786 	case I_YES:
787 		prefix = ID_UNIXUSER ":";
788 		break;
789 	case I_NO:
790 		prefix = ID_UNIXGROUP ":";
791 		break;
792 	case I_UNKNOWN:
793 		prefix = ID_UNIXNAME ":";
794 		break;
795 
796 	}
797 
798 	length = strlen(prefix) + strlen(it);
799 
800 	out = (char *)malloc((length + 1) * sizeof (char));
801 	if (out == NULL) {
802 		print_error(NULL,
803 		    gettext("Not enough memory.\n"));
804 		free(it);
805 		return (-1);
806 	}
807 
808 	(void) strcpy(out, prefix);
809 	(void) strcat(out, it);
810 	free(it);
811 
812 	*unixname = out;
813 	return (0);
814 }
815 
816 /* Initialize print_mapping variables. Must be called before print_mapping */
817 static int
818 print_mapping_init(format_t f, FILE *fi) {
819 	pnm_format = f;
820 	pnm_file = fi;
821 
822 	switch (pnm_format) {
823 	case SMBUSERS:
824 		pnm_last_unixname = NULL;
825 		break;
826 	default:
827 		;
828 	}
829 
830 	return (0);
831 }
832 
833 /* Finalize print_mapping. */
834 static int
835 print_mapping_fini() {
836 	switch (pnm_format) {
837 	case SMBUSERS:
838 		if (pnm_last_unixname != NULL) {
839 			(void) fprintf(pnm_file, "\n");
840 			free(pnm_last_unixname);
841 		}
842 		break;
843 	default:
844 		;
845 	}
846 
847 	pnm_file = stderr;
848 	pnm_format = UNDEFINED_FORMAT;
849 
850 	return (0);
851 }
852 
853 /*
854  * This prints both name rules and ordinary mappings, based on the pnm_format
855  * set in print_mapping_init().
856  */
857 
858 static int
859 print_mapping(name_mapping_t *nm)
860 {
861 	char *dirstring;
862 	char *winname_qm, *windomain_qm, *unixname_qm;
863 	char *winname = NULL;
864 	char *winname1 = NULL;
865 	char *unixname = NULL;
866 	FILE *f = pnm_file;
867 
868 
869 	switch (pnm_format) {
870 	case MAPPING_NAME:
871 		if (nm2winqn(nm, &winname) < 0)
872 			return (-1);
873 		if (nm2unixname(nm, &unixname) < 0) {
874 			free(winname);
875 			return (-1);
876 		}
877 	/* LINTED E_CASE_FALLTHRU */
878 	case MAPPING_ID:
879 		if (pnm_format == MAPPING_ID) {
880 			if (nm->sidprefix == NULL) {
881 				print_error(NULL,
882 				    gettext("SID not given.\n"));
883 				return (-1);
884 			}
885 			winname = sid_format(nm->sidprefix, nm->rid);
886 			if (winname == NULL)
887 				return (-1);
888 			unixname = pid_format(nm->pid, nm->is_user);
889 			if (unixname == NULL) {
890 				free(winname);
891 				return (-1);
892 			}
893 		}
894 
895 		dirstring = direction2string(nm->direction);
896 
897 		(void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring,
898 		    unixname);
899 
900 		free(winname);
901 		free(unixname);
902 		break;
903 	case SMBUSERS:
904 		if (!nm->is_user) {
905 			print_error(NULL,
906 			    gettext("Group rule: "));
907 			f = stderr;
908 		} else 	if (nm->direction == IDMAP_DIRECTION_U2W) {
909 			print_error(NULL,
910 			    gettext("Opposite direction of the mapping: "));
911 			f = stderr;
912 		}
913 		if (shell_app(&winname, nm->winname, 1))
914 			return (-1);
915 
916 		if (pnm_file != f) {
917 			(void) fprintf(f, "%s=%s\n", nm->unixname, winname);
918 		} else if (pnm_last_unixname != NULL &&
919 		    strcmp(pnm_last_unixname, nm->unixname) == 0) {
920 			(void) fprintf(f, " %s", winname);
921 		} else {
922 			if (pnm_last_unixname != NULL) {
923 				(void) fprintf(f, "\n");
924 				free(pnm_last_unixname);
925 			}
926 			pnm_last_unixname = strdup(nm->unixname);
927 			(void) fprintf(f, "%s=%s", nm->unixname, winname);
928 		}
929 
930 		free(winname);
931 
932 		break;
933 	case USERMAP_CFG:
934 		if (!nm->is_user) {
935 			print_error(NULL,
936 			    gettext("Group rule: "));
937 			f = stderr;
938 		}
939 
940 		dirstring = direction2string(nm->direction);
941 
942 		winname_qm = needs_protection(nm->winname) ? "\"" : "";
943 		windomain_qm =  nm->windomain &&
944 		    needs_protection(nm->windomain) ? "\"" : "";
945 		unixname_qm = needs_protection(nm->unixname) ? "\"" : "";
946 
947 		if (nm->windomain == NULL)
948 			(void) fprintf(f, "%s%s%s\t%s\t%s%s%s\n",
949 			    winname_qm,
950 			    nm->winname, winname_qm, dirstring,
951 			    unixname_qm, nm->unixname, unixname_qm);
952 		else
953 			(void) fprintf(f, nm->is_nt4 ?
954 			    "%s%s%1$s\\%3$s%4$s%3$s\t%5$s\t%6$s%7$s%6$s\n" :
955 			    "%3$s%4$s%3$s@%1$s%2$s%1$s\t%5$s\t%6$s%7$s%6$s\n",
956 			    windomain_qm, nm->windomain,
957 			    winname_qm, nm->winname,
958 			    dirstring,
959 			    unixname_qm, nm->unixname);
960 		break;
961 
962 	case DEFAULT_FORMAT:
963 		if (nm2winqn(nm, &winname1) < 0)
964 			return (-1);
965 
966 		if (shell_app(&winname, winname1, 1)) {
967 			free(winname1);
968 			return (-1);
969 		}
970 
971 		free(winname1);
972 
973 		if (nm2unixname(nm, &unixname)) {
974 			free(winname);
975 			return (-1);
976 		}
977 
978 		if (nm->direction == IDMAP_DIRECTION_U2W) {
979 			(void) fprintf(f,
980 			    "add -d\t%s\t%s\n",
981 			    unixname, winname);
982 		} else {
983 			(void) fprintf(f,
984 			    "add %s\t%s\t%s\n",
985 			    nm->direction == IDMAP_DIRECTION_BI? "" : "-d",
986 			    winname, unixname);
987 		}
988 		free(winname);
989 		free(unixname);
990 		break;
991 	default:
992 		/* This can never happen: */
993 		print_error(NULL,
994 		    gettext("Internal error: invalid print format.\n"));
995 		return (-1);
996 	}
997 
998 	return (0);
999 }
1000 
1001 /* Allocate a new name_mapping_t and initialize the values. */
1002 static name_mapping_t *
1003 name_mapping_init() {
1004 	name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t));
1005 	if (nm == NULL) {
1006 		print_error(NULL, gettext("Not enough memory.\n"));
1007 		return (NULL);
1008 	}
1009 	nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL;
1010 	nm->rid = UNDEFINED_RID;
1011 	nm->is_nt4 = B_FALSE;
1012 	nm->is_user = I_UNKNOWN;
1013 	nm->direction = IDMAP_DIRECTION_UNDEF;
1014 	nm->pid = UNDEFINED_UID;
1015 	return (nm);
1016 }
1017 
1018 /* Free name_mapping_t */
1019 static void
1020 name_mapping_fini(name_mapping_t *nm) {
1021 
1022 	free(nm->winname);
1023 	free(nm->windomain);
1024 	free(nm->unixname);
1025 	free(nm->sidprefix);
1026 
1027 	free(nm);
1028 }
1029 
1030 
1031 /* dump command handler */
1032 static int
1033 /* LINTED E_FUNC_ARG_UNUSED */
1034 do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
1035 {
1036 	idmap_stat stat;
1037 	idmap_iter_t *ihandle;
1038 	int is_user;
1039 	int rc = 0;
1040 
1041 	if (init_command())
1042 		return (-1);
1043 
1044 	(void) print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID,
1045 	    stdout);
1046 
1047 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
1048 		stat = idmap_iter_mappings(handle, is_user, &ihandle);
1049 		if (stat < 0) {
1050 			print_error(pos,
1051 			    gettext("Iteration handle not obtained (%s)\n"),
1052 			    idmap_stat2string(handle, stat));
1053 			rc = -1;
1054 			goto cleanup;
1055 		}
1056 
1057 		do {
1058 			name_mapping_t *nm = name_mapping_init();
1059 			if (nm == NULL) {
1060 				rc = -1;
1061 				goto cleanup;
1062 			}
1063 			nm->is_user = is_user;
1064 
1065 
1066 			stat = idmap_iter_next_mapping(ihandle,
1067 			    &nm->sidprefix, &nm->rid, &nm->pid,
1068 			    &nm->winname, &nm->windomain,
1069 			    &nm->unixname, &nm->direction);
1070 
1071 			if (stat >= 0)
1072 				(void) print_mapping(nm);
1073 
1074 			name_mapping_fini(nm);
1075 
1076 		} while (stat > 0);
1077 
1078 		/* IDMAP_ERR_NOTFOUND indicates end of the list */
1079 		if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) {
1080 			print_error(pos,
1081 			    gettext("Error during iteration (%s)\n"),
1082 			    idmap_stat2string(handle, stat));
1083 			rc = -1;
1084 			goto cleanup;
1085 		}
1086 
1087 		idmap_iter_destroy(ihandle);
1088 	}
1089 cleanup:
1090 	(void) print_mapping_fini();
1091 	fini_command();
1092 	return (rc);
1093 }
1094 
1095 /*
1096  * The same as strdup, but length chars is duplicated, no matter on
1097  * '\0'. The caller must guarantee "length" chars in "from".
1098  */
1099 static char *
1100 strndup(char *from, size_t length) {
1101 	char *out = (char *)malloc((length + 1) * sizeof (char));
1102 	if (out == NULL) {
1103 		print_error(NULL, gettext("Not enough memory\n"));
1104 		return (NULL);
1105 	}
1106 	(void) strncpy(out, from, length);
1107 	out[length] = '\0';
1108 	return (out);
1109 }
1110 
1111 /*
1112  * Convert pid from string to it's numerical representation. If it is
1113  * a valid string, i.e. number of a proper length, return 1. Otherwise
1114  * print an error message and return 0.
1115  */
1116 static int
1117 pid_convert(char *string, uid_t *number, int type, cmd_pos_t *pos) {
1118 	int i;
1119 	long long ll;
1120 	char *type_string;
1121 	size_t len = strlen(string);
1122 
1123 	if (type == TYPE_GID)
1124 		type_string = ID_GID;
1125 	else if (type == TYPE_UID)
1126 		type_string = ID_UID;
1127 	else
1128 		return (0);
1129 
1130 	for (i = 0; i < len; i++) {
1131 		if (!isdigit(string[i])) {
1132 			print_error(pos,
1133 			    gettext("\"%s\" is not a valid %s: the non-digit"
1134 				" character '%c' found.\n"), string,
1135 			    type_string, string[i]);
1136 			return (0);
1137 		}
1138 	}
1139 
1140 	ll = atoll(string);
1141 
1142 	/* Isn't it too large? */
1143 	if (type == TYPE_UID && (uid_t)ll != ll ||
1144 	    type == TYPE_GID && (gid_t)ll != ll) {
1145 		print_error(pos,
1146 		    gettext("%llu: too large for a %s.\n"), ll,
1147 		    type_string);
1148 		return (0);
1149 	}
1150 
1151 	*number = (uid_t)ll;
1152 	return (1);
1153 }
1154 
1155 /*
1156  * Convert SID from string to prefix and rid. If it has a valid
1157  * format, i.e. S(\-\d+)+, return 1. Otherwise print an error
1158  * message and return 0.
1159  */
1160 static int
1161 sid_convert(char *from, char **prefix, idmap_rid_t *rid, cmd_pos_t *pos) {
1162 	int i, j;
1163 	char *cp;
1164 	char *ecp;
1165 	char *prefix_end;
1166 	u_longlong_t	a;
1167 	unsigned long	r;
1168 
1169 	if (strcmp_no0(from, "S-1-") != 0) {
1170 		print_error(pos,
1171 		    gettext("Invalid %s \"%s\": it doesn't start "
1172 			"with \"%s\".\n"), ID_SID, from, "S-1-");
1173 		return (0);
1174 	}
1175 
1176 	if (strlen(from) <= strlen("S-1-")) {
1177 		print_error(pos,
1178 		    gettext("Invalid %s \"%s\": the authority and RID parts are"
1179 			" missing.\n"),
1180 		    ID_SID, from);
1181 		return (0);
1182 	}
1183 
1184 	/* count '-'s */
1185 	for (j = 0, cp = strchr(from, '-');
1186 		cp != NULL;
1187 		j++, cp = strchr(cp + 1, '-')) {
1188 		/* can't end on a '-' */
1189 		if (*(cp + 1) == '\0') {
1190 			print_error(pos,
1191 			    gettext("Invalid %s \"%s\": '-' at the end.\n"),
1192 			    ID_SID, from);
1193 			return (0);
1194 		} else 	if (*(cp + 1) == '-') {
1195 			print_error(pos,
1196 			    gettext("Invalid %s \"%s\": double '-'.\n"),
1197 			    ID_SID, from);
1198 			return (0);
1199 		}
1200 	}
1201 
1202 
1203 	/* check that we only have digits and '-' */
1204 	i = strspn(from + 1, "0123456789-") + 1;
1205 	if (i < strlen(from)) {
1206 		print_error(pos,
1207 		    gettext("Invalid %s \"%s\": invalid character '%c'.\n"),
1208 		    ID_SID, from, from[i]);
1209 		return (0);
1210 	}
1211 
1212 
1213 	cp = from + strlen("S-1-");
1214 
1215 	/* 64-bit safe parsing of unsigned 48-bit authority value */
1216 	errno = 0;
1217 	a = strtoull(cp, &ecp, 10);
1218 
1219 	/* errors parsing the authority or too many bits */
1220 	if (cp == ecp || (a == 0 && errno == EINVAL)) {
1221 			print_error(pos,
1222 			gettext("Invalid %s \"%s\": unable to parse the "
1223 			    "authority \"%.*s\".\n"), ID_SID, from, ecp - cp,
1224 			cp);
1225 		    return (0);
1226 	}
1227 
1228 	if ((a == ULLONG_MAX && errno == ERANGE) ||
1229 	    (a & 0x0000ffffffffffffULL) != a) {
1230 		print_error(pos,
1231 		    gettext("Invalid %s \"%s\": the authority "
1232 			"\"%.*s\" is too large.\n"), ID_SID, from,
1233 		    ecp - cp, cp);
1234 		return (0);
1235 	}
1236 
1237 	cp = ecp;
1238 
1239 	if (j < 3) {
1240 		print_error(pos,
1241 		    gettext("Invalid %s \"%s\": must have at least one RID.\n"),
1242 		    ID_SID, from);
1243 		return (0);
1244 	}
1245 
1246 	for (i = 2; i < j; i++) {
1247 		if (*cp++ != '-') {
1248 			/* Should never happen */
1249 			print_error(pos,
1250 			    gettext("Invalid %s \"%s\": internal error:"
1251 				" '-' missing.\n"),
1252 			    ID_SID, from);
1253 			return (0);
1254 		}
1255 		/* 32-bit safe parsing of unsigned 32-bit RID */
1256 		errno = 0;
1257 		r = strtoul(cp, &ecp, 10);
1258 
1259 		/* errors parsing the RID */
1260 		if (cp == ecp || (r == 0 && errno == EINVAL)) {
1261 			/* should never happen */
1262 			    print_error(pos,
1263 				gettext("Invalid %s \"%s\": internal error: "
1264 				    "unable to parse the RID "
1265 				    "after \"%.*s\".\n"), ID_SID,
1266 				from, cp - from, from);
1267 			    return (0);
1268 		}
1269 
1270 		if (r == ULONG_MAX && errno == ERANGE) {
1271 			print_error(pos,
1272 			    gettext("Invalid %s \"%s\": the RID \"%.*s\""
1273 				" is too large.\n"), ID_SID,
1274 			    from, ecp - cp, cp);
1275 			return (0);
1276 		}
1277 		prefix_end = cp;
1278 		cp = ecp;
1279 	}
1280 
1281 	/* check that all of the string SID has been consumed */
1282 	if (*cp != '\0') {
1283 		/* Should never happen */
1284 		print_error(pos,
1285 		    gettext("Invalid %s \"%s\": internal error: "
1286 			"something is still left.\n"),
1287 		    ID_SID, from);
1288 		return (0);
1289 	}
1290 
1291 	*rid = (idmap_rid_t)r;
1292 
1293 	/* -1 for the '-' at the end: */
1294 	*prefix = strndup(from, prefix_end - from - 1);
1295 	if (*prefix == NULL) {
1296 		print_error(pos,
1297 		    gettext("Not enough memory.\n"));
1298 		return (0);
1299 	}
1300 
1301 	return (1);
1302 }
1303 
1304 /* Does the line start with USERMAP_CFG IP qualifier? */
1305 static int
1306 ucp_is_IP_qualifier(char *line) {
1307 	char *it;
1308 	it = line + strcspn(line, " \t\n#:");
1309 	return (*(it + 1) == ':' ? 1 : 0);
1310 }
1311 
1312 
1313 /*
1314  * returns interior of quotation marks in USERMAP_CFG. In this format,
1315  * there cannot be a protected quotation mark inside.
1316  */
1317 static char *
1318 ucp_qm_interior(char **line, cmd_pos_t *pos) {
1319 	char *out;
1320 	char *qm = strchr(*line + 1, '"');
1321 	if (qm == NULL) {
1322 		print_error(pos,
1323 		    gettext("Unclosed quotations\n"));
1324 		return (NULL);
1325 	}
1326 
1327 	out = strndup(*line + 1, qm - *line - 1);
1328 	*line = qm + 1;
1329 	return (out);
1330 }
1331 
1332 /*
1333  * Grab next token from the line in USERMAP_CFG format. terminators,
1334  * the 3rd parameter, contains all the characters which can terminate
1335  * the token. line_num is the line number of input used for error
1336  * reporting.
1337  */
1338 static char *
1339 ucp_grab_token(char **line, cmd_pos_t *pos, const char *terminators) {
1340 	char *token;
1341 	if (**line == '"')
1342 		token = ucp_qm_interior(line, pos);
1343 	else {
1344 		int length = strcspn(*line, terminators);
1345 		token = strndup(*line, length);
1346 		*line += length;
1347 	}
1348 
1349 	return (token);
1350 }
1351 
1352 
1353 /*
1354  * Convert a line in usermap.cfg format to name_mapping.
1355  *
1356  * Return values: -1 for error, 0 for empty line, 1 for a mapping
1357  * found.
1358  */
1359 static int
1360 ucp_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm) {
1361 	char *it;
1362 	char *token;
1363 	char *token2;
1364 	char separator;
1365 	int is_direction = 0;
1366 
1367 	it = line + strspn(line, " \t\n");
1368 
1369 	/* empty or comment lines are OK: */
1370 	if (*it == '\0' || *it == '#')
1371 		return (0);
1372 
1373 	/* We do not support network qualifiers */
1374 	if (ucp_is_IP_qualifier(it)) {
1375 		print_error(pos,
1376 		    gettext("Unable to handle network qualifier.\n"));
1377 		return (-1);
1378 	}
1379 
1380 	/* The windows name: */
1381 	token = ucp_grab_token(&it, pos, " \t#\\\n@=<");
1382 	if (token == NULL)
1383 		return (-1);
1384 
1385 	separator = *it;
1386 
1387 	/* Didn't we bump to the end of line? */
1388 	if (separator == '\0' || separator == '#') {
1389 		free(token);
1390 		print_error(pos,
1391 		    gettext("UNIX_name not found.\n"));
1392 		return (-1);
1393 	}
1394 
1395 	/* Do we have a domainname? */
1396 	if (separator == '\\' || separator == '@') {
1397 		it ++;
1398 		token2 = ucp_grab_token(&it, pos, " \t\n#");
1399 		if (token2 == NULL) {
1400 			free(token);
1401 			return (-1);
1402 		} else if (*it == '\0' || *it == '#') {
1403 			free(token);
1404 			free(token2);
1405 			print_error(pos,
1406 			    gettext("UNIX_name not found.\n"));
1407 		}
1408 
1409 		if (separator == '\\') {
1410 			nm->windomain = token;
1411 			nm->winname = token2;
1412 			nm->is_nt4 = 1;
1413 		} else {
1414 			nm->windomain = token2;
1415 			nm->winname = token;
1416 			nm->is_nt4 = 0;
1417 
1418 		}
1419 	} else {
1420 		nm->windomain = NULL;
1421 		nm->winname = token;
1422 		nm->is_nt4 = 0;
1423 	}
1424 
1425 
1426 	it = it + strspn(it, " \t\n");
1427 
1428 	/* Direction string is optional: */
1429 	if (strncmp(it, "==", 2) == 0) {
1430 		nm->direction = IDMAP_DIRECTION_BI;
1431 		is_direction = 1;
1432 	} else if (strncmp(it, "<=", 2) == 0) {
1433 		nm->direction = IDMAP_DIRECTION_U2W;
1434 		is_direction = 1;
1435 	} else if (strncmp(it, "=>", 2) == 0) {
1436 		nm->direction = IDMAP_DIRECTION_W2U;
1437 		is_direction = 1;
1438 	} else {
1439 		nm->direction = IDMAP_DIRECTION_BI;
1440 		is_direction = 0;
1441 	}
1442 
1443 	if (is_direction) {
1444 		it += 2;
1445 		it += strspn(it, " \t\n");
1446 
1447 		if (*it == '\0' || *it == '#') {
1448 			print_error(pos,
1449 			    gettext("UNIX_name not found.\n"));
1450 			return (-1);
1451 		}
1452 	}
1453 
1454 	/* Now unixname: */
1455 	it += strspn(it, " \t\n");
1456 	token = ucp_grab_token(&it, pos, " \t\n#");
1457 
1458 	if (token == NULL)
1459 		/* nm->winname to be freed by name_mapping_fini */
1460 		return (-1);
1461 
1462 	/* Neither here we support IP qualifiers */
1463 	if (ucp_is_IP_qualifier(token)) {
1464 		print_error(pos,
1465 		    gettext("Unable to handle network qualifier.\n"));
1466 		free(token);
1467 		return (-1);
1468 	}
1469 
1470 	nm->unixname = token;
1471 
1472 	it += strspn(it, " \t\n");
1473 
1474 	/* Does something remain on the line */
1475 	if (*it  != '\0' && *it != '#') {
1476 		print_error(pos,
1477 		    gettext("Unrecognized parameters \"%s\".\n"), it);
1478 		return (-1);
1479 	}
1480 
1481 	return (1);
1482 }
1483 
1484 /*
1485  * Parse SMBUSERS line to name_mapping_t. if line is NULL, then
1486  * pasrsing of the previous line is continued. line_num is input line
1487  * number used for error reporting.
1488  * Return values:
1489  *    rc -1: error
1490  *    rc = 0: mapping found and the line is finished,
1491  *    rc = 1: mapping found and there remains other on the line
1492  */
1493 static int
1494 sup_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm) {
1495 	static char *ll = NULL;
1496 	static char *unixname = NULL;
1497 	static size_t unixname_l = 0;
1498 	char *token;
1499 
1500 	if (line != NULL) {
1501 		ll = line;
1502 
1503 		unixname = ll += strspn(ll, " \t");
1504 		if (*ll == '\0' || *ll == '#')
1505 			return (0);
1506 
1507 		unixname_l = strcspn(ll, " \t:=#\n");
1508 		ll += unixname_l;
1509 
1510 		if (*ll == '\0'|| *ll == '#')
1511 			return (0);
1512 
1513 		ll +=  strspn(ll, " \t:=#\n");
1514 
1515 	}
1516 
1517 	if (*ll == '\0'|| *ll == '#')
1518 		return (0);
1519 
1520 	token = ucp_grab_token(&ll, pos, " \t\n");
1521 	if (token == NULL)
1522 		return (-1);
1523 
1524 	nm->is_nt4 = 0;
1525 	nm->direction = IDMAP_DIRECTION_W2U;
1526 
1527 	nm->windomain = NULL;
1528 	nm->winname = token;
1529 	nm->unixname = strndup(unixname, unixname_l);
1530 	if (nm->unixname == NULL)
1531 		return (-1);
1532 
1533 	ll += strspn(ll, " \t\n");
1534 	return (1);
1535 }
1536 
1537 /* Parse line to name_mapping_t. Basicaly just a format switch. */
1538 static int
1539 line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm, format_t f) {
1540 	switch (f) {
1541 	case USERMAP_CFG:
1542 		if (line == NULL)
1543 			return (0);
1544 		else
1545 			return (ucp_line2nm(line, pos, nm));
1546 	case SMBUSERS:
1547 		return (sup_line2nm(line, pos, nm));
1548 	default:
1549 		/* This can never happen */
1550 		print_error(pos,
1551 		    gettext("Internal error: invalid line format.\n"));
1552 	}
1553 
1554 	return (-1);
1555 }
1556 
1557 
1558 /* Examine -f flag and return the appropriate format_t */
1559 static format_t
1560 ff2format(char *ff, int is_mandatory) {
1561 
1562 	if (ff == NULL && is_mandatory) {
1563 		print_error(NULL, gettext("Format not given.\n"));
1564 		return (UNDEFINED_FORMAT);
1565 	}
1566 
1567 	if (ff == NULL)
1568 		return (DEFAULT_FORMAT);
1569 
1570 	if (strcasecmp(ff, "usermap.cfg") == 0)
1571 		return (USERMAP_CFG);
1572 
1573 	if (strcasecmp(ff, "smbusers") == 0)
1574 		return (SMBUSERS);
1575 
1576 	print_error(NULL,
1577 		    gettext("The only known formats are: \"usermap.cfg\" and "
1578 			"\"smbusers\".\n"));
1579 	return (UNDEFINED_FORMAT);
1580 }
1581 
1582 /* Delete all namerules of the given type */
1583 static int
1584 flush_nm(boolean_t is_user, cmd_pos_t *pos)
1585 {
1586 	idmap_stat stat;
1587 
1588 	stat = idmap_udt_flush_namerules(udt, is_user);
1589 	if (stat < 0) {
1590 		print_error(pos,
1591 		    is_user ? gettext("Unable to flush users (%s).\n")
1592 		    : gettext("Unable to flush groups (%s).\n"),
1593 		    idmap_stat2string(handle, stat));
1594 		return (-1);
1595 	}
1596 
1597 	if (positions_add(pos) < 0)
1598 		return (-1);
1599 
1600 	return (0);
1601 }
1602 
1603 /* import command handler */
1604 static int
1605 /* LINTED E_FUNC_ARG_UNUSED */
1606 do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
1607 {
1608 	name_mapping_t *nm;
1609 	cmd_pos_t pos2;
1610 	char line[MAX_INPUT_LINE_SZ];
1611 	format_t format;
1612 	int rc = 0;
1613 	idmap_stat stat;
1614 	FILE *file = NULL;
1615 
1616 	if (batch_mode) {
1617 		print_error(pos,
1618 		    gettext("Import is not allowed in the batch mode.\n"));
1619 		return (-1);
1620 	}
1621 
1622 	format = ff2format(argv[0], 1);
1623 	if (format == UNDEFINED_FORMAT)
1624 		return (-1);
1625 
1626 	if (init_udt_command())
1627 		return (-1);
1628 
1629 	/* We don't flush groups in the usermap.cfg nor smbusers format */
1630 	if (f[F_FLAG] != NULL &&
1631 	    flush_nm(B_TRUE, pos) < 0 &&
1632 	    (format == USERMAP_CFG || format == SMBUSERS ||
1633 	    flush_nm(B_FALSE, pos) < 0)) {
1634 		rc = -1;
1635 		goto cleanup;
1636 	}
1637 
1638 	/* Where we import from? */
1639 	if (f[f_FLAG] == NULL)
1640 		file = stdin;
1641 	else {
1642 		file = fopen(f[f_FLAG], "r");
1643 		if (file == NULL) {
1644 			perror(f[f_FLAG]);
1645 			goto cleanup;
1646 		}
1647 	}
1648 
1649 	pos2.linenum = 0;
1650 	pos2.line = line;
1651 
1652 	while (fgets(line, MAX_INPUT_LINE_SZ, file)) {
1653 		char *line2 = line;
1654 		pos2.linenum++;
1655 
1656 		/*
1657 		 * In SMBUSERS format there can be more mappings on
1658 		 * each line. So we need the internal cycle for each line.
1659 		 */
1660 		do {
1661 			nm = name_mapping_init();
1662 			if (nm == NULL) {
1663 				rc = -1;
1664 				goto cleanup;
1665 			}
1666 
1667 			rc = line2nm(line2, &pos2, nm, format);
1668 			line2 = NULL;
1669 
1670 			if (rc < 1) {
1671 				name_mapping_fini(nm);
1672 				break;
1673 			}
1674 
1675 			stat = idmap_udt_add_namerule(udt, nm->windomain,
1676 			    nm->is_user ? B_TRUE : B_FALSE, nm->winname,
1677 			    nm->unixname, nm->is_nt4, nm->direction);
1678 			if (stat < 0) {
1679 				print_error(&pos2,
1680 				    gettext("Transaction error (%s)\n"),
1681 				    idmap_stat2string(handle, stat));
1682 				rc = -1;
1683 			}
1684 
1685 			if (rc >= 0)
1686 				rc = positions_add(&pos2);
1687 
1688 			name_mapping_fini(nm);
1689 
1690 		} while (rc >= 0);
1691 
1692 		if (rc < 0) {
1693 			print_error(NULL,
1694 			    gettext("Import canceled.\n"));
1695 			break;
1696 		}
1697 	}
1698 
1699 cleanup:
1700 	if (fini_udt_command((rc < 0 ? 0 : 1), pos))
1701 		rc = -1;
1702 	if (file != NULL && file != stdin)
1703 		(void) fclose(file);
1704 	return (rc);
1705 }
1706 
1707 
1708 /*
1709  * List name mappings in the format specified. list_users /
1710  * list_groups determine which type to list. The output goes to the
1711  * file fi.
1712  */
1713 static int
1714 list_name_mappings(format_t format, FILE *fi)
1715 {
1716 	idmap_stat stat;
1717 	idmap_iter_t *ihandle;
1718 	name_mapping_t *nm;
1719 	int is_user;
1720 
1721 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
1722 		/* Only users can be in USERMAP_CFG format, not a group */
1723 		if (!is_user && format == USERMAP_CFG)
1724 			continue;
1725 
1726 		stat = idmap_iter_namerules(handle, NULL, is_user, NULL,
1727 		    NULL, &ihandle);
1728 		if (stat < 0) {
1729 			print_error(NULL,
1730 			    gettext("Iteration handle not obtained (%s)\n"),
1731 			    idmap_stat2string(handle, stat));
1732 			idmap_iter_destroy(ihandle);
1733 			return (-1);
1734 		}
1735 
1736 		(void) print_mapping_init(format, fi);
1737 
1738 		do {
1739 			nm = name_mapping_init();
1740 			if (nm == NULL) {
1741 				idmap_iter_destroy(ihandle);
1742 				return (-1);
1743 			}
1744 
1745 			stat = idmap_iter_next_namerule(ihandle, &nm->windomain,
1746 			    &nm->winname, &nm->unixname, &nm->is_nt4,
1747 			    &nm->direction);
1748 			if (stat >= 0) {
1749 				nm->is_user = is_user;
1750 				(void) print_mapping(nm);
1751 			}
1752 
1753 			name_mapping_fini(nm);
1754 
1755 		} while (stat > 0);
1756 
1757 		(void) print_mapping_fini();
1758 
1759 		if (stat < 0 && stat !=  IDMAP_ERR_NOTFOUND) {
1760 			print_error(NULL,
1761 			    gettext("Error during iteration (%s)\n"),
1762 			    idmap_stat2string(handle, stat));
1763 			idmap_iter_destroy(ihandle);
1764 			return (-1);
1765 		}
1766 
1767 		idmap_iter_destroy(ihandle);
1768 	}
1769 	return (0);
1770 }
1771 
1772 /* Export command handler  */
1773 static int
1774 /* LINTED E_FUNC_ARG_UNUSED */
1775 do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos) {
1776 	int rc;
1777 	format_t format;
1778 	FILE *fi;
1779 
1780 	format = ff2format(argv[0], 1);
1781 	if (format == UNDEFINED_FORMAT)
1782 		return (-1);
1783 
1784 	/* Where do we output to? */
1785 	if (f[f_FLAG] == NULL)
1786 		fi = stdout;
1787 	else {
1788 		fi = fopen(f[f_FLAG], "w");
1789 		if (fi == NULL) {
1790 			perror(f[f_FLAG]);
1791 			return (-1);
1792 		}
1793 	}
1794 
1795 	if (init_command() < 0) {
1796 		rc = -1;
1797 		goto cleanup;
1798 	}
1799 
1800 	/* List the requested types: */
1801 	rc = list_name_mappings(format, fi);
1802 
1803 	fini_command();
1804 
1805 cleanup:
1806 	if (fi != NULL && fi != stdout)
1807 		(void) fclose(fi);
1808 	return (rc);
1809 }
1810 
1811 /* List command handler */
1812 static int
1813 /* LINTED E_FUNC_ARG_UNUSED */
1814 do_list_name_mappings(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
1815 {
1816 	int rc;
1817 
1818 	if (init_command()) {
1819 		return (-1);
1820 	}
1821 
1822 	/* List the requested types: */
1823 	rc = list_name_mappings(DEFAULT_FORMAT, stdout);
1824 
1825 	fini_command();
1826 	return (rc);
1827 }
1828 
1829 /* This is just a debug function for dumping flags */
1830 static void
1831 print_flags(flag_t *f)
1832 {
1833 	int c;
1834 	for (c = 0; c < FLAG_ALPHABET_SIZE; c++) {
1835 		if (f[c] == FLAG_SET)
1836 			(void) printf("FLAG: -%c, VALUE: %p\n", c,
1837 			    (void *) f[c]);
1838 		else if (f[c])
1839 			(void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]);
1840 	}
1841 }
1842 
1843 /*
1844  * This function splits name to the relevant pieces: is_user, winname,
1845  * windomain unixname. Sometimes it is not possible to determine OS
1846  * side, because it could be determined by the opposite name in idmap
1847  * show. So this function must be called several times.
1848  *
1849  * Return values: -1 ... clear syntax error
1850  *                0  ... it wasnt possible to determine
1851  *                1  ... determined
1852  */
1853 static int
1854 name2parts(char *name, name_mapping_t *nm, cmd_pos_t *pos) {
1855 	char *it;
1856 	int is_win = I_NO;
1857 	int is_unix = I_NO;
1858 
1859 	if (nm->winname != NULL && nm->unixname != NULL)
1860 		return (0);
1861 
1862 	/* If it starts with type string, that is easy: */
1863 	if (it = strchr(name, ':')) {
1864 		if (strcmp_no0(name, ID_UNIXUSER ":") == 0) {
1865 			if (nm->is_user == I_NO) {
1866 				print_error(pos,
1867 				    gettext("%s - invalid combination of user"
1868 					" and group identity types.\n"),
1869 				    ID_UNIXUSER);
1870 				return (-1);
1871 			}
1872 			if (nm->unixname != NULL)
1873 				return (0);
1874 
1875 			nm->is_user = I_YES;
1876 			is_unix = I_YES;
1877 
1878 		} else if (strcmp_no0(name, ID_UNIXGROUP ":") == 0) {
1879 			if (nm->is_user == I_YES) {
1880 				print_error(pos,
1881 				    gettext("%s - invalid combination of user"
1882 					" and group identity types.\n"),
1883 				    ID_UNIXGROUP);
1884 				return (-1);
1885 			}
1886 			if (nm->unixname != NULL)
1887 				return (0);
1888 
1889 			nm->is_user = I_NO;
1890 			is_unix = I_YES;
1891 
1892 		} else if (strcmp_no0(name, ID_WINUSER ":") == 0) {
1893 			if (nm->is_user == I_NO) {
1894 				print_error(pos,
1895 				    gettext("%s - invalid combination of user"
1896 					" and group identity types.\n"),
1897 				    ID_WINUSER);
1898 				return (-1);
1899 			}
1900 			if (nm->winname != NULL)
1901 				return (0);
1902 
1903 			nm->is_user = I_YES;
1904 			is_win = I_YES;
1905 
1906 		} else if (strcmp_no0(name, ID_WINGROUP ":") == 0) {
1907 			if (nm->is_user == I_YES) {
1908 				print_error(pos,
1909 				    gettext("%s - invalid combination of user"
1910 					" and group identity types.\n"),
1911 				    ID_WINGROUP);
1912 				return (-1);
1913 			}
1914 			if (nm->winname != NULL)
1915 				return (0);
1916 
1917 			nm->is_user = I_NO;
1918 			is_win = I_YES;
1919 
1920 		} else if (strcmp_no0(name, ID_WINNAME ":") == 0) {
1921 			if (nm->winname != NULL)
1922 				return (0);
1923 			is_win = I_YES;
1924 		} else {
1925 			print_error(pos,
1926 			    gettext("Error: invalid identity type \"%.*s\"\n"),
1927 			    it - name, name);
1928 			return (-1);
1929 		}
1930 		name = it + 1;
1931 	}
1932 
1933 	/* If it contains '@' or '\\', then it is a winname with domain */
1934 	if (!is_unix && nm->winname == NULL) {
1935 		if ((it = strchr(name, '@')) != NULL) {
1936 			int length = it - name + 1;
1937 			nm->winname = (char *)malloc(length * sizeof (char));
1938 			(void) strncpy(nm->winname, name, length - 1);
1939 			nm->winname[length - 1] = '\0';
1940 			nm->windomain = strdup(it + 1);
1941 			return (1);
1942 		} else if ((it = strrchr(name, '\\')) != NULL) {
1943 			int length = it - name + 1;
1944 			nm->windomain = (char *)malloc(length * sizeof (char));
1945 			(void) strncpy(nm->windomain, name, length - 1);
1946 			nm->windomain[length - 1] = '\0';
1947 			nm->winname = strdup(it + 1);
1948 			nm->is_nt4 = B_TRUE;
1949 			return (1);
1950 		}
1951 	}
1952 
1953 	/*
1954 	 * if is_unix/is_win is not yet determined, then the last
1955 	 * hope is that the opposite side is known already. In that
1956 	 * case, it is the only remaining side.
1957 	 */
1958 	if (is_unix || nm->unixname == NULL && nm->winname != NULL) {
1959 		if (strlen(name) == 0)
1960 			nm->unixname = strdup("\"\"");
1961 		else
1962 			nm->unixname = strdup(name);
1963 		return (1);
1964 	} else if (is_win || nm->unixname != NULL && nm->winname == NULL) {
1965 		if (strlen(name) == 0)
1966 			nm->winname = strdup("\"\"");
1967 		else
1968 			nm->winname = strdup(name);
1969 		nm->windomain = NULL;
1970 		return (1);
1971 	}
1972 
1973 	return (0);
1974 }
1975 
1976 /* add command handler. */
1977 static int
1978 do_add_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
1979 {
1980 	name_mapping_t *nm;
1981 	int rc = 0;
1982 	int i;
1983 	int is_argv0_unix = -1;
1984 	idmap_stat stat;
1985 
1986 
1987 	/* Exactly two arguments must be specified */
1988 	if (argc < 2) {
1989 		print_error(pos, gettext("Not enough arguments.\n"));
1990 		return (-1);
1991 	} else if (argc > 2)  {
1992 		print_error(pos, gettext("Too many arguments.\n"));
1993 		return (-1);
1994 	}
1995 
1996 	/*
1997 	 * Direction can be determined by the opposite name, so we
1998 	 * need to run name2parts twice for the first name, i.e. 3x in
1999 	 * total.
2000 	 */
2001 	nm = name_mapping_init();
2002 	if (nm == NULL)
2003 		return (-1);
2004 
2005 	for (i = 0; i < 3; i++) {
2006 		switch (name2parts(argv[i % 2], nm, pos)) {
2007 		case -1:
2008 			name_mapping_fini(nm);
2009 			return (-1);
2010 		case 1:
2011 			if (is_argv0_unix < 0)
2012 				is_argv0_unix =
2013 				    i % 2 ^ (nm->unixname != NULL ? 1 : 0);
2014 			break;
2015 		}
2016 	}
2017 
2018 	if (nm->winname == NULL || nm->unixname == NULL) {
2019 		print_error(pos, gettext("Name types not determined.\n"));
2020 		name_mapping_fini(nm);
2021 		return (-1);
2022 	}
2023 
2024 	if (f[d_FLAG] != NULL)
2025 		nm->direction = is_argv0_unix
2026 		    ? IDMAP_DIRECTION_U2W
2027 		    : IDMAP_DIRECTION_W2U;
2028 	else
2029 		nm->direction = IDMAP_DIRECTION_BI;
2030 
2031 	/* Now let us write it: */
2032 
2033 	if (init_udt_command()) {
2034 		name_mapping_fini(nm);
2035 		return (-1);
2036 	}
2037 
2038 	stat = idmap_udt_add_namerule(udt, nm->windomain,
2039 	    nm->is_user ? B_TRUE : B_FALSE, nm->winname, nm->unixname,
2040 	    nm->is_nt4, nm->direction);
2041 
2042 	/* We echo the mapping */
2043 	(void) print_mapping_init(DEFAULT_FORMAT, stdout);
2044 	(void) print_mapping(nm);
2045 	(void) print_mapping_fini();
2046 
2047 	if (stat < 0) {
2048 		print_error(pos,
2049 		    gettext("Mapping not created (%s)\n"),
2050 		    idmap_stat2string(handle, stat));
2051 		rc = -1;
2052 	}
2053 
2054 	if (rc == 0)
2055 		rc = positions_add(pos);
2056 
2057 cleanup:
2058 	name_mapping_fini(nm);
2059 	if (fini_udt_command(1, pos))
2060 		rc = -1;
2061 	return (rc);
2062 }
2063 
2064 /* remove command handler */
2065 static int
2066 do_remove_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2067 {
2068 	name_mapping_t *nm;
2069 	int rc = 0;
2070 	int i;
2071 	int is_argv0_unix = -1;
2072 	idmap_stat stat;
2073 	int is_user;
2074 
2075 	/* "-a" means we flush all of them */
2076 	if (f[a_FLAG] != NULL) {
2077 		if (argc) {
2078 			print_error(pos,
2079 			    gettext("Too many arguments.\n"));
2080 			return (-1);
2081 		}
2082 
2083 		if (init_udt_command())
2084 			return (-1);
2085 		rc = flush_nm(B_TRUE, pos);
2086 
2087 		if (rc >= 0)
2088 			rc = flush_nm(B_FALSE, pos);
2089 
2090 		if (fini_udt_command(rc ? 0 : 1, pos))
2091 			rc = -1;
2092 		return (rc);
2093 	}
2094 
2095 	/* Contrary to add_name_mapping, we can have only one argument */
2096 	if (argc < 1) {
2097 		print_error(pos, gettext("Not enough arguments.\n"));
2098 		return (-1);
2099 	} else if (argc > 2) {
2100 		print_error(pos, gettext("Too many arguments.\n"));
2101 		return (-1);
2102 	} else if (
2103 		/* both -f and -t: */
2104 	    f[f_FLAG] != NULL && f[t_FLAG] != NULL ||
2105 		/* -d with a single argument: */
2106 	    argc == 1 && f[d_FLAG] != NULL ||
2107 		/* -f or -t with two arguments: */
2108 	    argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) {
2109 		print_error(pos,
2110 		    gettext("Direction ambiguous.\n"));
2111 		return (-1);
2112 	}
2113 
2114 
2115 	/*
2116 	 * Similar to do_add_name_mapping - see the comments
2117 	 * there. Except we may have only one argument here.
2118 	 */
2119 	nm = name_mapping_init();
2120 	if (nm == NULL)
2121 		return (-1);
2122 
2123 	for (i = 0; i < 2 * argc - 1; i++) {
2124 		switch (name2parts(argv[i % 2], nm, pos)) {
2125 		case -1:
2126 			name_mapping_fini(nm);
2127 			return (-1);
2128 		case 1:
2129 			if (is_argv0_unix < 0)
2130 				is_argv0_unix = i % 2 ^ (nm->unixname ? 1 : 0);
2131 			break;
2132 		}
2133 	}
2134 
2135 
2136 	if (nm->winname == NULL && nm->unixname == NULL) {
2137 		print_error(pos, gettext("Name types not determined.\n"));
2138 		name_mapping_fini(nm);
2139 		return (-1);
2140 	}
2141 
2142 	/*
2143 	 * If the direction is not specified by a -d/-f/-t flag, then it
2144 	 * is IDMAP_DIRECTION_UNDEF, because in that case we want to
2145 	 * remove any mapping. If it was IDMAP_DIRECTION_BI, idmap_api would
2146 	 * delete a bidirectional one only.
2147 	 */
2148 	if (f[d_FLAG] != NULL || f[f_FLAG] != NULL)
2149 		nm->direction = is_argv0_unix
2150 		    ? IDMAP_DIRECTION_U2W
2151 		    : IDMAP_DIRECTION_W2U;
2152 	else if (f[t_FLAG] != NULL)
2153 		nm->direction = is_argv0_unix
2154 		    ? IDMAP_DIRECTION_W2U
2155 		    : IDMAP_DIRECTION_U2W;
2156 	else
2157 		nm->direction = IDMAP_DIRECTION_UNDEF;
2158 
2159 	if (init_udt_command()) {
2160 		name_mapping_fini(nm);
2161 		return (-1);
2162 	}
2163 
2164 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
2165 		if ((is_user == I_YES && nm->is_user == I_NO) ||
2166 		    (is_user == I_NO && nm->is_user == I_YES))
2167 			continue;
2168 
2169 		stat = idmap_udt_rm_namerule(udt, is_user ? B_TRUE : B_FALSE,
2170 		    nm->windomain, nm->winname, nm->unixname, nm->direction);
2171 
2172 		if (stat < 0) {
2173 			print_error(pos,
2174 			    gettext("Mapping not deleted (%s)\n"),
2175 			    idmap_stat2string(handle, stat));
2176 			rc = -1;
2177 			break;
2178 		}
2179 	}
2180 
2181 	if (rc == 0)
2182 		rc = positions_add(pos);
2183 
2184 cleanup:
2185 	name_mapping_fini(nm);
2186 	if (fini_udt_command(1, pos))
2187 		rc = -1;
2188 	return (rc);
2189 }
2190 
2191 
2192 /* exit command handler */
2193 static int
2194 /* LINTED E_FUNC_ARG_UNUSED */
2195 do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos) {
2196 	return (0);
2197 }
2198 
2199 
2200 /* debug command handler: just print the parameters */
2201 static int
2202 /* LINTED E_STATIC_UNUSED */
2203 debug_print_params(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2204 {
2205 	int i;
2206 #if 0
2207 	char *leaktest = (char *)malloc(100);
2208 #endif
2209 
2210 	print_flags(f);
2211 
2212 	for (i = 0; i < argc; i++) {
2213 		(void) printf("Argument %d: %s\n", i, argv[i]);
2214 	}
2215 
2216 	(void) fflush(stdout);
2217 	return (0);
2218 }
2219 
2220 /*
2221  * Return a pointer after a given prefix. If there is no such prefix,
2222  * return NULL
2223  */
2224 static char *
2225 get_root(char *string, char *typestring) {
2226 	if (strcasecmp_no0(string, typestring) != 0)
2227 		return (NULL);
2228 	return (string + strlen(typestring));
2229 }
2230 
2231 /*
2232  * From name_mapping_t, asseble a string containing identity of the
2233  * given type.
2234  */
2235 static int
2236 nm2type(name_mapping_t *nm, int type, char **to) {
2237 	switch (type) {
2238 	case TYPE_SID:
2239 		if (nm->sidprefix == NULL)
2240 			return (-1);
2241 		*to = sid_format(nm->sidprefix, nm->rid);
2242 		return (0);
2243 	case TYPE_WN:
2244 	case TYPE_WU:
2245 	case TYPE_WG:
2246 		return (nm2winqn(nm, to));
2247 	case TYPE_UID:
2248 	case TYPE_GID:
2249 	case TYPE_PID:
2250 		*to = pid_format(nm->pid, nm->is_user);
2251 		if (*to == NULL)
2252 			return (-1);
2253 		else
2254 			return (0);
2255 	case TYPE_UN:
2256 	case TYPE_UU:
2257 	case TYPE_UG:
2258 		return (nm2unixname(nm, to));
2259 	default:
2260 		/* This can never happen: */
2261 		print_error(NULL,
2262 		    gettext("Internal error: invalid name type.\n"));
2263 		return (-1);
2264 	}
2265 	/* never reached */
2266 }
2267 
2268 /* show command handler */
2269 static int
2270 do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
2271 {
2272 	idmap_stat stat = 0;
2273 	int flag;
2274 	idmap_stat map_stat = 0;
2275 	int type_from;
2276 	int type_to;
2277 	char *root;
2278 	name_mapping_t *nm = NULL;
2279 	char *fromname;
2280 	char *toname;
2281 
2282 	if (argc == 0) {
2283 		print_error(pos,
2284 		    gettext("No identity given\n"));
2285 		return (-1);
2286 	} else if (argc > 2) {
2287 		print_error(pos,
2288 		    gettext("Too many arguments.\n"));
2289 		return (-1);
2290 	}
2291 
2292 	flag = f[c_FLAG] != NULL ? 0 : IDMAP_REQ_FLG_NO_NEW_ID_ALLOC;
2293 
2294 	if (init_command())
2295 		return (-1);
2296 
2297 	nm = name_mapping_init();
2298 	if (nm == NULL)
2299 		goto cleanup;
2300 
2301 	/* First, determine type_from: */
2302 	if ((root = get_root(argv[0], ID_UID ":")) != NULL)
2303 		type_from = TYPE_UID;
2304 	else if ((root = get_root(argv[0], ID_GID ":")) != NULL)
2305 		type_from = TYPE_GID;
2306 	else if ((root = get_root(argv[0], ID_SID ":")) != NULL)
2307 		type_from = TYPE_SID;
2308 	else
2309 		switch (name2parts(argv[0], nm, pos)) {
2310 		case 1:		/* name2parts determined the type */
2311 			if (nm->unixname != NULL)
2312 				switch (nm->is_user) {
2313 				case I_YES:
2314 					type_from = TYPE_UU;
2315 					break;
2316 				case I_NO:
2317 					type_from = TYPE_UG;
2318 					break;
2319 				default:
2320 					type_from = TYPE_UN;
2321 				}
2322 			else
2323 				switch (nm->is_user) {
2324 				case I_YES:
2325 					type_from = TYPE_WU;
2326 					break;
2327 				case I_NO:
2328 					type_from = TYPE_WG;
2329 					break;
2330 				default:
2331 					type_from = TYPE_WN;
2332 				}
2333 			break;
2334 		case 0:		/* name2parts didn't determine the type: */
2335 			print_error(pos, gettext("Invalid type.\n"));
2336 			/* LINTED E_CASE_FALLTHRU */
2337 		case -1:
2338 			stat = IDMAP_ERR_ARG;
2339 			goto cleanup;
2340 		}
2341 
2342 	/* Second, determine type_to: */
2343 	if (argc < 2) {
2344 		type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID;
2345 		if (type_from & IS_NAME)
2346 			type_to |= IS_NAME;
2347 	} else if (strcasecmp(argv[1], ID_UID) == 0)
2348 		type_to = TYPE_UID;
2349 	else if (strcasecmp(argv[1], ID_GID) == 0)
2350 		type_to = TYPE_GID;
2351 	else if (strcasecmp(argv[1], ID_SID) == 0)
2352 		type_to = TYPE_SID;
2353 	else if (strcmp(argv[1], ID_UNIXUSER) == 0)
2354 		type_to = TYPE_UU;
2355 	else if (strcmp(argv[1], ID_UNIXGROUP) == 0)
2356 		type_to = TYPE_UG;
2357 	else if (strcmp(argv[1], ID_WINNAME) == 0)
2358 		type_to = TYPE_WN;
2359 	else if (strcmp(argv[1], ID_WINUSER) == 0)
2360 		type_to = TYPE_WU;
2361 	else if (strcmp(argv[1], ID_WINGROUP) == 0)
2362 		type_to = TYPE_WG;
2363 	else {
2364 		print_error(pos,
2365 		    gettext("Invalid target type.\n"));
2366 		stat = IDMAP_ERR_ARG;
2367 		goto cleanup;
2368 	}
2369 
2370 	/* Are both arguments the same OS side? */
2371 	if (!(type_from & IS_WIN ^ type_to & IS_WIN)) {
2372 		print_error(pos,
2373 		    gettext("Direction ambiguous.\n"));
2374 		stat = IDMAP_ERR_ARG;
2375 		goto cleanup;
2376 	}
2377 
2378 	/* Are both arguments the same userness/groupness? */
2379 	if ((type_from & IS_USER) && (type_to & IS_GROUP)) {
2380 		print_error(pos,
2381 		    gettext("Mapping of user to group impossible.\n"));
2382 		stat = IDMAP_ERR_ARG;
2383 		goto cleanup;
2384 	} else if ((type_from & IS_GROUP) && (type_to & IS_USER)) {
2385 		print_error(pos,
2386 		    gettext("Mapping of group to user impossible.\n"));
2387 		stat = IDMAP_ERR_ARG;
2388 		goto cleanup;
2389 	}
2390 
2391 	if (type_from == TYPE_SID) {
2392 		if (!sid_convert(root, &nm->sidprefix, &nm->rid, pos)) {
2393 			stat = IDMAP_ERR_ARG;
2394 			goto cleanup;
2395 		}
2396 	} else if (type_from == TYPE_UID || type_from == TYPE_GID) {
2397 		if (!pid_convert(root, &nm->pid, type_from, pos)) {
2398 			stat = IDMAP_ERR_ARG;
2399 			goto cleanup;
2400 		}
2401 	}
2402 
2403 /*
2404  * We have two interfaces for retrieving the mappings:
2405  * idmap_get_sidbyuid & comp (the batch interface) and
2406  * idmap_get_w2u_mapping & comp. We  want to use both of them, because
2407  * the former mimicks kernel interface better and the later offers the
2408  * string names. In the batch case, our batch has always size 1.
2409  */
2410 	if (type_from & IS_NAME || type_to & IS_NAME) {
2411 		if (type_from & IS_WIN) {
2412 			if (type_to == TYPE_UID)
2413 				nm->is_user = I_YES;
2414 			else if (type_to == TYPE_GID)
2415 			nm->is_user = I_NO;
2416 
2417 			map_stat = idmap_get_w2u_mapping(handle,
2418 			    nm->sidprefix,
2419 			    &nm->rid,
2420 			    nm->winname,
2421 			    nm->windomain,
2422 			    flag,
2423 			    &nm->is_user,
2424 			    &nm->pid,
2425 			    &nm->unixname,
2426 			    &nm->direction);
2427 		} else {
2428 			if (type_from == TYPE_UID)
2429 				nm->is_user = I_YES;
2430 			else if (type_from == TYPE_GID)
2431 				nm->is_user = I_NO;
2432 
2433 			map_stat = idmap_get_u2w_mapping(handle,
2434 			    &nm->pid,
2435 			    nm->unixname,
2436 			    flag,
2437 			    nm->is_user,
2438 			    &nm->sidprefix,
2439 			    &nm->rid,
2440 			    &nm->winname,
2441 			    &nm->windomain,
2442 			    &nm->direction);
2443 		}
2444 
2445 	} else {
2446 		/* batch handle */
2447 		idmap_get_handle_t *ghandle = NULL;
2448 		/* To be passed to idmap_get_uidbysid  */
2449 		gid_t gid = UNDEFINED_GID;
2450 		/* To be passed to idmap_get_gidbysid  */
2451 		uid_t uid = UNDEFINED_UID;
2452 
2453 
2454 		/* Create an in-memory structure for all the batch: */
2455 		stat = idmap_get_create(handle, &ghandle);
2456 		if (stat < 0) {
2457 			print_error(pos,
2458 			    gettext("Unable to create handle for communicating"
2459 			    " with idmapd(1M) (%s)\n"),
2460 			    idmap_stat2string(handle, stat));
2461 			idmap_get_destroy(ghandle);
2462 			goto cleanup;
2463 		}
2464 
2465 		/* Schedule the request: */
2466 		if (type_from == TYPE_SID && type_to == TYPE_UID) {
2467 			stat = idmap_get_uidbysid(ghandle,
2468 			    nm->sidprefix,
2469 			    nm->rid,
2470 			    flag,
2471 			    &uid,
2472 			    &map_stat);
2473 			nm->is_user = I_YES;
2474 		} else if (type_from == TYPE_SID && type_to == TYPE_GID) {
2475 			stat =  idmap_get_gidbysid(ghandle,
2476 			    nm->sidprefix,
2477 			    nm->rid,
2478 			    flag,
2479 			    &gid,
2480 			    &map_stat);
2481 			nm->is_user = I_NO;
2482 		} else if (type_from == TYPE_SID && type_to == TYPE_PID)
2483 			stat = idmap_get_pidbysid(ghandle,
2484 			    nm->sidprefix,
2485 			    nm->rid,
2486 			    flag,
2487 			    &nm->pid,
2488 			    &nm->is_user,
2489 			    &map_stat);
2490 		else if (type_from == TYPE_UID && type_to == TYPE_SID) {
2491 			stat = idmap_get_sidbyuid(ghandle,
2492 			    nm->pid,
2493 			    flag,
2494 			    &nm->sidprefix,
2495 			    &nm->rid,
2496 			    &map_stat);
2497 			nm->is_user = I_YES;
2498 		} else if (type_from == TYPE_GID && type_to == TYPE_SID) {
2499 			stat = idmap_get_sidbygid(ghandle,
2500 			    (gid_t)nm->pid,
2501 			    flag,
2502 			    &nm->sidprefix,
2503 			    &nm->rid,
2504 			    &map_stat);
2505 			nm->is_user = I_NO;
2506 		} else {
2507 			/* This can never happen: */
2508 			print_error(pos,
2509 			    gettext("Internal error in show.\n"));
2510 			exit(1);
2511 		}
2512 
2513 		if (stat < 0) {
2514 			print_error(pos,
2515 			    gettext("Request for %.3s not sent (%s)\n"),
2516 			    argv[0], idmap_stat2string(handle, stat));
2517 			idmap_get_destroy(ghandle);
2518 			goto cleanup;
2519 		}
2520 
2521 		/* Send the batch to idmapd and obtain results: */
2522 		stat = idmap_get_mappings(ghandle);
2523 		if (stat < 0) {
2524 			print_error(pos,
2525 			    gettext("Mappings not obtained because of"
2526 			    " RPC problem (%s)\n"),
2527 			    idmap_stat2string(handle, stat));
2528 			idmap_get_destroy(ghandle);
2529 			goto cleanup;
2530 		}
2531 
2532 		/* Destroy the batch handle: */
2533 		idmap_get_destroy(ghandle);
2534 
2535 		if (type_to == TYPE_UID)
2536 			nm->pid = uid;
2537 		else if (type_to == TYPE_GID)
2538 			nm->pid = (uid_t)gid;
2539 
2540 	}
2541 
2542 	/*
2543 	 * If there was -c flag, we do output whatever we can even in
2544 	 * the case of error:
2545 	 */
2546 	if (map_stat < 0) {
2547 		print_error(pos,
2548 		    gettext("%s\n"),
2549 		    idmap_stat2string(handle, map_stat));
2550 		if (flag == IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
2551 			goto cleanup;
2552 	}
2553 
2554 
2555 	/*
2556 	 * idmapd returns fallback uid/gid in case of errors. However
2557 	 * it uses special sentinel value i.e 4294967295 (or -1) to
2558 	 * indicate that falbback pid is not available either. In such
2559 	 * case idmap(1M) should not display the mapping because there
2560 	 * is no fallback mapping.
2561 	 */
2562 
2563 	if (type_to == TYPE_UID && nm->pid == UNDEFINED_UID ||
2564 	    type_to == TYPE_GID && nm->pid == (uid_t)UNDEFINED_GID) {
2565 		goto cleanup;
2566 	}
2567 
2568 	if (nm2type(nm, type_from, &fromname) < 0)
2569 		goto cleanup;
2570 
2571 	if (nm2type(nm, type_to, &toname) < 0) {
2572 		if (flag == 0)
2573 			(void) printf("%s -> %s:%u\n",
2574 			    fromname,
2575 			    (type_from | type_to) & IS_GROUP ? ID_GID : ID_UID,
2576 			    UID_NOBODY);
2577 		free(fromname);
2578 		goto cleanup;
2579 	}
2580 
2581 	(void) printf("%s -> %s\n", fromname, toname);
2582 	free(fromname);
2583 	free(toname);
2584 
2585 cleanup:
2586 	if (nm != NULL)
2587 		name_mapping_fini(nm);
2588 	fini_command();
2589 	return (stat < 0 || map_stat < 0 ? -1 : 0);
2590 }
2591 
2592 /* main function. Returns 1 for error, 0 otherwise */
2593 int
2594 main(int argc, char *argv[]) {
2595 	int rc;
2596 
2597 	/* set locale and domain for internationalization */
2598 	(void) setlocale(LC_ALL, "");
2599 	(void) textdomain(TEXT_DOMAIN);
2600 
2601 	/* idmap_engine determines the batch_mode: */
2602 	rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t),
2603 		commands,
2604 		argc - 1,
2605 		argv + 1,
2606 		&batch_mode);
2607 
2608 	if (rc < 0) {
2609 		(void) engine_fini();
2610 		if (rc == IDMAP_ENG_ERROR_SILENT)
2611 			help();
2612 		return (1);
2613 	}
2614 
2615 	udt_used = 0;
2616 	if (batch_mode) {
2617 		if (init_udt_batch() < 0)
2618 			return (1);
2619 	}
2620 
2621 	rc = run_engine(argc - 1, argv + 1);
2622 
2623 	if (batch_mode) {
2624 		batch_mode = 0;
2625 		if (fini_udt_command(rc == 0 ? 1 : 0, NULL))
2626 			rc = -1;
2627 	}
2628 
2629 	(void) engine_fini();
2630 	return (rc == 0 ? 0 : 1);
2631 }
2632