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