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