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