xref: /illumos-gate/usr/src/cmd/idmap/idmap/idmap.c (revision f875b4ebb1dd9fdbeb043557cab38ab3bf7f6e01)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <locale.h>
31 #include <strings.h>
32 #include "idmap_engine.h"
33 #include "idmap_priv.h"
34 
35 /* Initialization values for pids/rids: */
36 
37 #define	UNDEFINED_UID (uid_t)-1
38 #define	UNDEFINED_GID (gid_t)-1
39 #define	UNDEFINED_RID (idmap_rid_t)-1;
40 
41 /* is_user values */
42 
43 #define	I_YES 1
44 #define	I_NO 0
45 #define	I_UNKNOWN -1
46 
47 /* Directions */
48 
49 #define	DIR_W2U 1
50 #define	DIR_U2W 2
51 #define	DIR_BI 0
52 #define	DIR_UNKNOWN -1
53 
54 /*
55  * used in do_show for the type of argument, which can be winname,
56  * unixname, uid, gid, sid or not given at all:
57  */
58 
59 #define	TYPE_SID	0x010	/* sid */
60 #define	TYPE_WN		0x110	/* winname */
61 #define	TYPE_UID	0x001	/* uid */
62 #define	TYPE_GID	0x002	/* gid */
63 #define	TYPE_PID	0x000	/* pid */
64 #define	TYPE_UN		0x100	/* unixname */
65 
66 #define	IS_WIN		0x010	/* mask for the windows types */
67 #define	IS_NAME		0x100	/* mask for string name types */
68 #define	IS_GROUP	0x002	/* mask for, well, TYPE_GID */
69 
70 
71 /* Identity type strings */
72 
73 #define	ID_WINNAME	"winname"
74 #define	ID_UNIXNAME	"unixname"
75 #define	ID_SID	"sid"
76 #define	ID_UID	"uid"
77 #define	ID_GID	"gid"
78 
79 /* Flags */
80 
81 #define	g_FLAG	'g'
82 #define	u_FLAG	'u'
83 #define	f_FLAG	'f'
84 #define	t_FLAG	't'
85 #define	d_FLAG	'd'
86 #define	F_FLAG	'F'
87 #define	a_FLAG	'a'
88 #define	n_FLAG	'n'
89 #define	c_FLAG	'c'
90 
91 
92 /* used in the function do_import */
93 #define	MAX_INPUT_LINE_SZ 2047
94 
95 
96 typedef struct {
97 	int is_user;
98 	int direction;
99 	boolean_t is_nt4;
100 	char *unixname;
101 	char *winname;
102 	char *windomain;
103 	char *sidprefix;
104 	idmap_rid_t rid;
105 	uid_t pid;
106 } name_mapping_t;
107 
108 /*
109  * Formats of the output:
110  *
111  * Idmap reads/prints mappings in several formats: ordinary mappings,
112  * name mappings in Samba username map format (smbusers), Netapp
113  * usermap.cfg.
114  *
115  * DEFAULT_FORMAT are in fact the idmap subcommands suitable for
116  * piping to idmap standart input. For example
117  * add -u -d winname:bob@foo.com unixname:fred
118  * add -u -d winname:bob2bar.com unixname:fred
119  *
120  * SMBUSERS is the format of Samba username map (smbusers). For full
121  * documentation, search for "username map" in smb.conf manpage.
122  * The format is for example
123  *    fred = bob@foo.com bob2@bar.com
124  *
125  * USERMAP_CFG is the format of Netapp usermap.cfg file. Search
126  * http://www.netapp.com/ for more documentation. IP qualifiers are not
127  * supported.
128  * The format is for example
129  *    bob@foo.com => fred
130  *    "Bob With Spaces"@bar.com => fred  #comment
131  *
132  * The previous formats were for name rules. MAPPING_NAME and
133  * MAPPING_ID are for the actual mappings, as seen in show/dump
134  * commands. MAPPING_NAME prefers the string names of the user over
135  * their numerical identificators. MAPPING_ID prints just the
136  * identificators.
137  * Example of the MAPPING_NAME:
138  *   winname:bob@foo.com -> unixname:fred
139  *
140  * Example of the MAPPING_ID:
141  *   sid:S-1-2-3-4 -> uid:5678
142  */
143 
144 typedef enum {
145 	UNDEFINED_FORMAT = -1,
146 	DEFAULT_FORMAT = 0,
147 	MAPPING_ID,
148 	MAPPING_NAME,
149 	USERMAP_CFG,
150 	SMBUSERS
151 } format_t;
152 
153 /* Gives the format to use. Set in print_mapping_init  */
154 static format_t pnm_format;
155 
156 /* The file for print_mapping_init output. Mostly just stdout. */
157 static FILE *pnm_file;
158 
159 /* In smbusers format, more unixnames can be aggregated to one line. */
160 static char *pnm_last_unixname;
161 
162 /*
163  * idmap_api batch related variables:
164  *
165  * idmap can operate in two modes. It the batch mode, the idmap_api
166  * batch is commited at the end of a batch of several
167  * commands. At the end of input file, typically. This mode is used
168  * for processing input from a file.
169  *  In the non-batch mode, each command is commited immediately. This
170  * mode is used for tty input.
171  */
172 
173 /* Are we in the batch mode? */
174 static int batch_mode = 0;
175 
176 /* Handles for idmap_api batch */
177 static idmap_handle_t *handle = NULL;
178 static idmap_udt_handle_t *udt = NULL;
179 
180 /* Do we need to commit the udt batch at the end? */
181 static int udt_used;
182 
183 /* Command handlers */
184 
185 static int do_show_mapping(flag_t *f, int argc, char **argv);
186 static int do_dump(flag_t *f, int argc, char **argv);
187 static int do_import(flag_t *f, int argc, char **argv);
188 static int do_list_name_mappings(flag_t *f, int argc, char **argv);
189 static int do_add_name_mapping(flag_t *f, int argc, char **argv);
190 static int do_remove_name_mapping(flag_t *f, int argc, char **argv);
191 static int do_exit(flag_t *f, int argc, char **argv);
192 static int do_export(flag_t *f, int argc, char **argv);
193 static int do_help(flag_t *f, int argc, char **argv);
194 
195 /* Command names and their hanlers to be passed to idmap_engine */
196 
197 static cmd_ops_t commands[] = {
198 	{
199 		"show",
200 		"c(create)",
201 		do_show_mapping
202 	},
203 	{
204 		"dump",
205 		"n(names)g(group)u(user)",
206 		do_dump
207 	},
208 	{
209 		"import",
210 		"F(flush)f:(file)",
211 		do_import
212 	},
213 	{
214 		"export",
215 		"f:(file)",
216 		do_export
217 	},
218 	{
219 		"list",
220 		"g(group)u(user)",
221 		do_list_name_mappings
222 	},
223 	{
224 		"add",
225 		"g(group)u(user)d(directional)",
226 		do_add_name_mapping
227 	},
228 	{
229 		"remove",
230 		"a(all)u(user)g(group)t(to)f(from)d(directional)",
231 		do_remove_name_mapping
232 	},
233 	{
234 		"exit",
235 		"",
236 		do_exit
237 	},
238 	{
239 		"help",
240 		"",
241 		do_help
242 	}
243 };
244 
245 /* Print help message */
246 static void
247 help() {
248 	(void) fprintf(stderr,
249 	    "idmap\n"
250 	    "idmap -f command-file\n"
251 	    "idmap show [-c] identity [targettype]\n"
252 	    "idmap dump [-u|-g] [-n]\n"
253 	    "idmap add -u|-g [-d] name1 name2\n"
254 	    "idmap remove -u|-g -a\n"
255 	    "idmap remove -u|-g name\n"
256 	    "idmap remove -u|-g [-d] name1 name2\n"
257 	    "idmap list [-u|-g]\n"
258 	    "idmap import [-F] [-f file] format\n"
259 	    "idmap export [-f file] format\n"
260 	    "idmap help\n");
261 }
262 
263 /* The handler for the "help" command. */
264 static int
265 /* LINTED E_FUNC_ARG_UNUSED */
266 do_help(flag_t *f, int argc, char **argv)
267 {
268 	help();
269 	return (0);
270 }
271 
272 /* Initialization of the idmap api batch */
273 static int
274 init_batch() {
275 	idmap_stat stat;
276 
277 	stat = idmap_init(&handle);
278 	if (stat < 0) {
279 		(void) fprintf(stderr,
280 		    gettext("Connection not established (%s)\n"),
281 		    idmap_stat2string(NULL, stat));
282 		return (-1);
283 	}
284 
285 	return (0);
286 }
287 
288 /* Initialization common to all commands */
289 static int
290 init_command() {
291 	if (batch_mode)
292 		return (0);
293 
294 	return (init_batch());
295 }
296 
297 /* Finalization common to all commands */
298 static void
299 fini_command() {
300 	if (batch_mode)
301 		return;
302 	(void) idmap_fini(handle);
303 	handle = NULL;
304 }
305 
306 /* Initialization of the commands which perform write operations  */
307 static int
308 init_udt_batch() {
309 	idmap_stat stat;
310 
311 	if (init_batch())
312 		return (-1);
313 
314 	stat = idmap_udt_create(handle, &udt);
315 	if (stat < 0) {
316 		(void) fprintf(stderr,
317 		    gettext("Error initiating transaction (%s)"),
318 		    idmap_stat2string(handle, stat));
319 		return (-1);
320 	}
321 	return (0);
322 }
323 
324 
325 /* Finalization of the write commands  */
326 static int
327 init_udt_command() {
328 	udt_used = 1;
329 	if (batch_mode)
330 		return (0);
331 
332 	return (init_udt_batch());
333 }
334 
335 
336 /* If everythings is OK, send the udt batch to idmapd  */
337 static void
338 fini_udt_command(int ok) {
339 	idmap_stat stat;
340 
341 	if (batch_mode)
342 		return;
343 	if (udt == NULL)
344 		return;
345 
346 	if (ok && udt_used) {
347 		stat = idmap_udt_commit(udt);
348 		if (stat < 0) {
349 			(void) fprintf(stderr,
350 			    gettext("Error commiting transaction (%s)\n"),
351 			    idmap_stat2string(handle, stat));
352 		}
353 	}
354 
355 	idmap_udt_destroy(udt);
356 	udt = NULL;
357 	udt_used = 0;
358 	fini_command();
359 }
360 
361 
362 /* Convert numeric expression of the direction to it's string form */
363 static char *
364 direction2string(int direction) {
365 	switch (direction) {
366 	case DIR_BI:
367 		return ("==");
368 	case DIR_W2U:
369 		return ("=>");
370 	case DIR_U2W:
371 		return ("<=");
372 	default:
373 		(void) fprintf(stderr, gettext("Internal error.\n"));
374 		return ("");
375 	}
376 	/* never reached */
377 }
378 
379 /* Do we need quotation marks around winname in the USERMAP_CFG format? */
380 static int
381 needs_protection(char *what) {
382 	if (strchr(what, ' ') != NULL)
383 		return (1);
384 
385 	if (strchr(what, '\t') != NULL)
386 		return (1);
387 
388 	if (strchr(what, '#') != NULL)
389 		return (1);
390 
391 	return (0);
392 }
393 
394 /* Protect all shell-special characters by '\\'  */
395 static int
396 shell_app(char **res, char *string) {
397 	size_t res_len = 0;
398 	size_t res_size = 24;
399 	int i;
400 	char c;
401 
402 	*res = (char *)malloc(res_size * sizeof (char));
403 	if (*res == NULL) {
404 		(void) fprintf(stderr, gettext("Not enough memory.\n"));
405 		return (-1);
406 	}
407 
408 	for (i = 0; string[i] != '\0'; i++) {
409 		c = string[i];
410 
411 		if (strchr("\"\\ \t#$", c) != NULL)
412 			(*res)[res_len++] = '\\';
413 		(*res)[res_len++] = c;
414 
415 		if (res_size - 1 <= res_len) {
416 			res_size *= 2;
417 			*res = (char *)realloc(*res, res_size * sizeof (char));
418 			if (*res == NULL) {
419 				(void) fprintf(stderr,
420 				    gettext("Not enough memory.\n"));
421 				return (-1);
422 			}
423 		}
424 	}
425 
426 	(*res)[res_len++] = '\0';
427 	return (0);
428 }
429 
430 /* Assemble string form sid */
431 static char *
432 sid_format(char *sidprefix, idmap_rid_t rid) {
433 	char *to;
434 	size_t len;
435 
436 	/* 'sid:' + sidprefix + '-' + rid + '\0' */
437 	len = strlen(sidprefix) + 6 + 3 * sizeof (rid);
438 	to = (char *)malloc(len * sizeof (char));
439 	if (to == NULL)
440 		return (NULL);
441 
442 	(void) snprintf(to, len, "sid:%s-%u", sidprefix, rid);
443 	return (to);
444 }
445 
446 /* Assemble string form uid or gid */
447 static char *
448 pid_format(uid_t from, int is_user) {
449 	char *to;
450 	size_t len;
451 
452 	/* ID_UID ":" + uid + '\0' */
453 	len = 5 + 3 * sizeof (uid_t);
454 	to = (char *)malloc(len * sizeof (char));
455 	if (to == NULL)
456 		return (NULL);
457 
458 	(void) snprintf(to, 16, "%s:%u", is_user ? ID_UID : ID_GID, from);
459 	return (to);
460 }
461 
462 /* Assemble winname, e.g. "winname:bob@foo.sun.com", from name_mapping_t */
463 static int
464 nm2winqn(name_mapping_t *nm, char **winqn) {
465 	char *out;
466 	size_t length = 0;
467 	int is_domain = 1;
468 
469 	/* Sometimes there are no text names. Return a sid, then. */
470 	if (nm->winname == NULL) {
471 		if (nm->sidprefix == NULL)
472 			return (-1);
473 
474 		*winqn = sid_format(nm->sidprefix, nm->rid);
475 		return (0);
476 	}
477 
478 	length = strlen(ID_WINNAME ":") + strlen(nm->winname);
479 
480 	/* Windomain is not mandatory: */
481 	if (nm->windomain == NULL ||
482 	    *nm->winname == '\0' ||
483 	    strcmp(nm->winname, "\"\"") == 0)
484 		is_domain = 0;
485 	else
486 		length += strlen(nm->windomain) + 1;
487 
488 	out = (char *)malloc((length + 1) * sizeof (char));
489 	if (out == NULL) {
490 		(void) fprintf(stderr,
491 		    gettext("Not enough memory.\n"));
492 		return (-1);
493 	}
494 
495 	(void) strcpy(out, ID_WINNAME ":");
496 
497 	if (!is_domain)
498 		(void) strcat(out, nm->winname);
499 	else if (nm->is_nt4) {
500 		(void) strcat(out, nm->windomain);
501 		(void) strcat(out, "\\");
502 		(void) strcat(out, nm->winname);
503 	} else {
504 		(void) strcat(out, nm->winname);
505 		(void) strcat(out, "@");
506 		(void) strcat(out, nm->windomain);
507 	}
508 
509 	*winqn = out;
510 	return (0);
511 }
512 
513 /* Assemble a text unixname, e.g. unixname:fred */
514 static int
515 nm2unixname(name_mapping_t *nm, char **unixname) {
516 	size_t length = 0;
517 	char *out;
518 	char *it;
519 
520 	/* Sometimes there is no name, just pid: */
521 	if (nm->unixname == NULL) {
522 		if (nm->pid == UNDEFINED_UID)
523 			return (-1);
524 
525 		*unixname = pid_format(nm->pid, nm->is_user);
526 		return (0);
527 	}
528 
529 	if (shell_app(&it, nm->unixname))
530 		return (-1);
531 
532 	length = strlen(ID_UNIXNAME ":") + strlen(it);
533 
534 	out = (char *)malloc((length + 1) * sizeof (char));
535 	if (out == NULL) {
536 		(void) fprintf(stderr,
537 		    gettext("Not enough memory.\n"));
538 		free(it);
539 		return (-1);
540 	}
541 
542 	(void) strcpy(out, ID_UNIXNAME ":");
543 	(void) strcat(out, it);
544 	free(it);
545 
546 	*unixname = out;
547 	return (0);
548 }
549 
550 /* Initialize print_mapping variables. Must be called before print_mapping */
551 static int
552 print_mapping_init(format_t f, FILE *fi) {
553 	pnm_format = f;
554 	pnm_file = fi;
555 
556 	switch (pnm_format) {
557 	case SMBUSERS:
558 		pnm_last_unixname = NULL;
559 		break;
560 	default:
561 		;
562 	}
563 
564 	return (0);
565 }
566 
567 /* Finalize print_mapping. */
568 static int
569 print_mapping_fini() {
570 	switch (pnm_format) {
571 	case SMBUSERS:
572 		if (pnm_last_unixname != NULL) {
573 			(void) fprintf(pnm_file, "\n");
574 			free(pnm_last_unixname);
575 		}
576 		break;
577 	default:
578 		;
579 	}
580 
581 	pnm_file = stderr;
582 	pnm_format = UNDEFINED_FORMAT;
583 
584 	return (0);
585 }
586 
587 /*
588  * This prints both name rules and ordinary mappings, based on the pnm_format
589  * set in print_mapping_init().
590  */
591 
592 static int
593 print_mapping(name_mapping_t *nm)
594 {
595 	char *dirstring;
596 	char *winname_qm, *windomain_qm, *unixname_qm;
597 	char type;
598 	char *winname = NULL;
599 	char *winname1 = NULL;
600 	char *unixname = NULL;
601 	FILE *f = pnm_file;
602 
603 
604 	switch (pnm_format) {
605 	case MAPPING_NAME:
606 		if (nm2winqn(nm, &winname) < 0)
607 			return (-1);
608 		if (nm2unixname(nm, &unixname) < 0) {
609 			free(winname);
610 			return (-1);
611 		}
612 	/* LINTED E_CASE_FALLTHRU */
613 	case MAPPING_ID:
614 		if (pnm_format == MAPPING_ID) {
615 			if (nm->sidprefix == NULL) {
616 				(void) fprintf(stderr,
617 				    gettext("SID not given.\n"));
618 				return (-1);
619 			}
620 			winname = sid_format(nm->sidprefix, nm->rid);
621 			if (winname == NULL)
622 				return (-1);
623 			unixname = pid_format(nm->pid, nm->is_user);
624 			if (unixname == NULL) {
625 				free(winname);
626 				return (-1);
627 			}
628 		}
629 
630 		dirstring = direction2string(nm->direction);
631 
632 		(void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring,
633 		    unixname);
634 
635 		free(winname);
636 		free(unixname);
637 		break;
638 	case SMBUSERS:
639 		if (!nm->is_user) {
640 			(void) fprintf(stderr,
641 			    gettext("Group rule: "));
642 			f = stderr;
643 		} else 	if (nm->direction == DIR_U2W) {
644 			(void) fprintf(stderr,
645 			    gettext("Opposite direction of the mapping: "));
646 			f = stderr;
647 		}
648 		if (shell_app(&winname, nm->winname))
649 			return (-1);
650 
651 		if (pnm_file != f) {
652 			(void) fprintf(f, "%s = %s\n", nm->unixname, winname);
653 		} else if (pnm_last_unixname != NULL &&
654 		    strcmp(pnm_last_unixname, nm->unixname) == 0) {
655 			(void) fprintf(f, " %s", winname);
656 		} else {
657 			if (pnm_last_unixname != NULL) {
658 				(void) fprintf(f, "\n");
659 				free(pnm_last_unixname);
660 			}
661 			pnm_last_unixname = strdup(nm->unixname);
662 			(void) fprintf(f, "%s = %s", nm->unixname, winname);
663 		}
664 
665 		free(winname);
666 
667 		break;
668 	case USERMAP_CFG:
669 		if (!nm->is_user) {
670 			(void) fprintf(stderr,
671 			    gettext("Group rule: "));
672 			f = stderr;
673 		}
674 
675 		dirstring = direction2string(nm->direction);
676 
677 		winname_qm = needs_protection(nm->winname) ? "\"" : "";
678 		windomain_qm =  nm->windomain &&
679 		    needs_protection(nm->windomain) ? "\"" : "";
680 		unixname_qm = needs_protection(nm->unixname) ? "\"" : "";
681 
682 		if (nm->windomain == NULL)
683 			(void) fprintf(f, "%s%s%s\t%s\t%s%s%s\n",
684 			    winname_qm,
685 			    nm->winname, winname_qm, dirstring,
686 			    unixname_qm, nm->unixname, unixname_qm);
687 		else
688 			(void) fprintf(f, nm->is_nt4 ?
689 			    "%s%s%1$s\\%3$s%4$s%3$s\t%5$s\t%6$s%7$s%6$s\n" :
690 			    "%3$s%4$s%3$s@%1$s%2$s%1$s\t%5$s\t%6$s%7$s%6$s\n",
691 			    windomain_qm, nm->windomain,
692 			    winname_qm, nm->winname,
693 			    dirstring,
694 			    unixname_qm, nm->unixname);
695 		break;
696 
697 	case DEFAULT_FORMAT:
698 		/* 'u', 'g' refer to -u, -g switch of idmap add */
699 		type = nm->is_user ? 'u' : 'g';
700 		if (nm2winqn(nm, &winname1) < 0)
701 			return (-1);
702 
703 		if (shell_app(&winname, winname1)) {
704 			free(winname1);
705 			return (-1);
706 		}
707 
708 		free(winname1);
709 
710 		if (nm2unixname(nm, &unixname)) {
711 			free(winname);
712 			return (-1);
713 		}
714 
715 		if (nm->direction == DIR_U2W) {
716 			(void) fprintf(f,
717 			    "add -%c -d\t%s\t%s\n",
718 			    type, unixname, winname);
719 		} else {
720 			(void) fprintf(f,
721 			    "add -%c %s\t%s\t%s\n",
722 			    type, nm->direction == DIR_BI ? "" : "-d",
723 			    winname, unixname);
724 		}
725 		free(winname);
726 		free(unixname);
727 		break;
728 	default:
729 		(void) fprintf(stderr, gettext("Internal error.\n"));
730 		return (-1);
731 	}
732 
733 	return (0);
734 }
735 
736 /* Allocate a new name_mapping_t and initialize the values. */
737 static name_mapping_t *
738 name_mapping_init() {
739 	name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t));
740 	if (nm == NULL) {
741 		(void) fprintf(stderr, gettext("Not enough memory.\n"));
742 		return (NULL);
743 	}
744 	nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL;
745 	nm->rid = UNDEFINED_RID;
746 	nm->is_nt4 = B_FALSE;
747 	nm->is_user = I_UNKNOWN;
748 	nm->direction = DIR_UNKNOWN;
749 	nm->pid = UNDEFINED_UID;
750 	return (nm);
751 }
752 
753 /* Free name_mapping_t */
754 static void
755 name_mapping_fini(name_mapping_t *nm) {
756 
757 	free(nm->winname);
758 	free(nm->windomain);
759 	free(nm->unixname);
760 	free(nm->sidprefix);
761 
762 	free(nm);
763 }
764 
765 /* Is there exactly one of -g, -u flags? */
766 static int
767 is_type_determined(flag_t *f)
768 {
769 	if (f[u_FLAG] == NULL && f[g_FLAG] == NULL || /* none */
770 	    f[u_FLAG] != NULL && f[g_FLAG] != NULL) /* both */ {
771 		(void) fprintf(stderr,
772 		    gettext("Type (-u|-g) not determined.\n"));
773 		return (0);
774 	}
775 	return (1);
776 }
777 
778 /* Does user request a user-related operation? */
779 static int
780 is_user_wanted(flag_t *f) {
781 	if (f[u_FLAG] != NULL || f[g_FLAG] == NULL)
782 		return (1);
783 	return (0);
784 }
785 
786 /* Does user request a group-related operation? */
787 static int
788 is_group_wanted(flag_t *f) {
789 	if (f[g_FLAG] != NULL || f[u_FLAG] == NULL)
790 		return (1);
791 	return (0);
792 }
793 
794 
795 /* dump command handler */
796 static int
797 /* LINTED E_FUNC_ARG_UNUSED */
798 do_dump(flag_t *f, int argc, char **argv)
799 {
800 	idmap_stat stat;
801 	idmap_iter_t *ihandle;
802 	int is_user;
803 	int rc = 0;
804 
805 	if (init_command())
806 		return (-1);
807 
808 	(void) print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID,
809 	    stdout);
810 
811 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
812 		/*
813 		 * If there is exactly one of -u, -g flags, we print
814 		 * only that type. Otherwise both of them:
815 		 */
816 		if (!is_user_wanted(f) && is_user ||
817 		    !is_group_wanted(f) && !is_user)
818 			continue;
819 
820 		stat = idmap_iter_mappings(handle, is_user, &ihandle);
821 		if (stat < 0) {
822 			(void) fprintf(stderr,
823 			    gettext("Iteration handle not obtained (%s)\n"),
824 			    idmap_stat2string(handle, stat));
825 			rc = -1;
826 			goto cleanup;
827 		}
828 
829 		do {
830 			name_mapping_t *nm = name_mapping_init();
831 			if (nm == NULL) {
832 				rc = -1;
833 				goto cleanup;
834 			}
835 			nm->is_user = is_user;
836 
837 
838 			stat = idmap_iter_next_mapping(ihandle,
839 			    &nm->sidprefix, &nm->rid, &nm->pid,
840 			    &nm->winname, &nm->windomain,
841 			    &nm->unixname, &nm->direction);
842 
843 			if (stat >= 0)
844 				(void) print_mapping(nm);
845 
846 			name_mapping_fini(nm);
847 
848 		} while (stat > 0);
849 
850 		/* IDMAP_ERR_NOTFOUND indicates end of the list */
851 		if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) {
852 			(void) fprintf(stderr,
853 			    gettext("Error during iteration (%s)\n"),
854 			    idmap_stat2string(handle, stat));
855 			rc = -1;
856 			goto cleanup;
857 		}
858 
859 		idmap_iter_destroy(ihandle);
860 	}
861 cleanup:
862 	(void) print_mapping_fini();
863 	fini_command();
864 	return (rc);
865 }
866 
867 /*
868  * The same as strdup, but length chars is duplicated, no matter on
869  * '\0'. The caller must guarantee "length" chars in "from".
870  */
871 static char *
872 strndup(char *from, size_t length) {
873 	char *out = (char *)malloc((length + 1) * sizeof (char));
874 	if (out == NULL) {
875 		(void) fprintf(stderr, gettext("Not enough memory\n"));
876 		return (NULL);
877 	}
878 	(void) strncpy(out, from, length);
879 	out[length] = '\0';
880 	return (out);
881 }
882 
883 /* Does line start with USERMAP_CFG IP qualifier? */
884 static int
885 ucp_is_IP_qualifier(char *line) {
886 	char *it;
887 	it = line + strcspn(line, " \t\n#:");
888 	return (*(it + 1) == ':' ? 1 : 0);
889 }
890 
891 
892 /*
893  * returns interior of quotation marks in USERMAP_CFG. In this format,
894  * there cannot be a protected quotation mark inside.
895  */
896 static char *
897 ucp_qm_interior(char **line, int line_num) {
898 	char *out;
899 	char *qm = strchr(*line + 1, '"');
900 	if (qm == NULL) {
901 		(void) fprintf(stderr,
902 		    gettext("Line %d: Unclosed quotations\n"),
903 		    line_num);
904 		return (NULL);
905 	}
906 
907 	out = strndup(*line + 1, qm - *line - 1);
908 	*line = qm + 1;
909 	return (out);
910 }
911 
912 /*
913  * Grab next token from the line in USERMAP_CFG format. terminators,
914  * the 3rd parameter, contains all the characters which can terminate
915  * the token. line_num is the line number of input used for error
916  * reporting.
917  */
918 static char *
919 ucp_grab_token(char **line, int line_num, const char *terminators) {
920 	char *token;
921 	if (**line == '"')
922 		token = ucp_qm_interior(line, line_num);
923 	else {
924 		int length = strcspn(*line, terminators);
925 		token = strndup(*line, length);
926 		*line += length;
927 	}
928 
929 	return (token);
930 }
931 
932 
933 /*
934  * Convert a line in usermap.cfg format to name_mapping. line_num is
935  * the line number of input used for error reporting.
936  *
937  * Return values: -1 for error, 0 for empty line, 1 for a mapping
938  * found.
939  */
940 static int
941 ucp_line2nm(char *line, int line_num, name_mapping_t *nm) {
942 	char *it;
943 	char *token;
944 	char *token2;
945 	char separator;
946 	int is_direction = 0;
947 
948 	it = line + strspn(line, " \t\n");
949 
950 	/* empty or comment lines are OK: */
951 	if (*it == '\0' || *it == '#')
952 		return (0);
953 
954 	/* We do not support network qualifiers */
955 	if (ucp_is_IP_qualifier(it)) {
956 		(void) fprintf(stderr,
957 		    gettext("Line %d: unable to handle network qualifier.\n"),
958 		    line_num);
959 		return (-1);
960 	}
961 
962 	/* The windows name: */
963 	token = ucp_grab_token(&it, line_num, " \t#\\\n@=<");
964 	if (token == NULL)
965 		return (-1);
966 
967 	separator = *it;
968 
969 	/* Didn't we bump to the end of line? */
970 	if (separator == '\0' || separator == '#') {
971 		free(token);
972 		(void) fprintf(stderr,
973 		    gettext("Line %d: UNIX_name not found.\n"),
974 		    line_num);
975 		return (-1);
976 	}
977 
978 	/* Do we have a domainname? */
979 	if (separator == '\\' || separator == '@') {
980 		it ++;
981 		token2 = ucp_grab_token(&it, line_num, " \t\n#");
982 		if (token2 == NULL) {
983 			free(token);
984 			return (-1);
985 		} else if (*it == '\0' || *it == '#') {
986 			free(token);
987 			free(token2);
988 			(void) fprintf(stderr,
989 			    gettext("Line %d: UNIX_name not found.\n"),
990 			    line_num);
991 		}
992 
993 		if (separator == '\\') {
994 			nm->windomain = token;
995 			nm->winname = token2;
996 			nm->is_nt4 = 1;
997 		} else {
998 			nm->windomain = token2;
999 			nm->winname = token;
1000 			nm->is_nt4 = 0;
1001 
1002 		}
1003 	} else {
1004 		nm->windomain = NULL;
1005 		nm->winname = token;
1006 		nm->is_nt4 = 0;
1007 	}
1008 
1009 
1010 	it = it + strspn(it, " \t\n");
1011 
1012 	/* Direction string is optional: */
1013 	if (strncmp(it, "==", 2) == 0) {
1014 		nm->direction = DIR_BI;
1015 		is_direction = 1;
1016 	} else if (strncmp(it, "<=", 2) == 0) {
1017 		nm->direction = DIR_U2W;
1018 		is_direction = 1;
1019 	} else if (strncmp(it, "=>", 2) == 0) {
1020 		nm->direction = DIR_W2U;
1021 		is_direction = 1;
1022 	} else {
1023 		nm->direction = DIR_BI;
1024 		is_direction = 0;
1025 	}
1026 
1027 	if (is_direction) {
1028 		it += 2;
1029 		it += strspn(it, " \t\n");
1030 
1031 		if (*it == '\0' || *it == '#') {
1032 			(void) fprintf(stderr,
1033 			    gettext("Line %d: UNIX_name not found.\n"),
1034 			    line_num);
1035 			return (-1);
1036 		}
1037 	}
1038 
1039 	/* Now unixname: */
1040 	it += strspn(it, " \t\n");
1041 	token = ucp_grab_token(&it, line_num, " \t\n#");
1042 
1043 	if (token == NULL)
1044 		/* nm->winname to be freed by name_mapping_fini */
1045 		return (-1);
1046 
1047 	/* Neither here we support IP qualifiers */
1048 	if (ucp_is_IP_qualifier(token)) {
1049 		(void) fprintf(stderr,
1050 		    gettext("Line %d: unable to handle network qualifier.\n"),
1051 		    line_num);
1052 		free(token);
1053 		return (-1);
1054 	}
1055 
1056 	nm->unixname = token;
1057 
1058 	it += strspn(it, " \t\n");
1059 
1060 	/* Does something remain on the line */
1061 	if (*it  != '\0' && *it != '#') {
1062 		(void) fprintf(stderr,
1063 		    gettext("Line %d: unrecognized parameters \"%s\".\n"),
1064 		    line_num, it);
1065 		return (-1);
1066 	}
1067 
1068 	return (1);
1069 }
1070 
1071 /*
1072  * Parse SMBUSERS line to name_mapping_t. if line is NULL, then
1073  * pasrsing of the previous line is continued. line_num is input line
1074  * number used for error reporting.
1075  * Return values:
1076  *    rc -1: error
1077  *    rc = 0: mapping found and the line is finished,
1078  *    rc = 1: mapping found and there remains other on the line
1079  */
1080 static int
1081 sup_line2nm(char *line, int line_num, name_mapping_t *nm) {
1082 	static char *ll = NULL;
1083 	static char *unixname = NULL;
1084 	static size_t unixname_l = 0;
1085 	char *token;
1086 
1087 	if (line != NULL) {
1088 		ll = line;
1089 
1090 		unixname = ll += strspn(ll, " \t");
1091 		if (*ll == '\0' || *ll == '#')
1092 			return (0);
1093 
1094 		unixname_l = strcspn(ll, " \t:=#\n");
1095 		ll += unixname_l;
1096 
1097 		if (*ll == '\0'|| *ll == '#')
1098 			return (0);
1099 
1100 		ll +=  strspn(ll, " \t:=#\n");
1101 
1102 	}
1103 
1104 	if (*ll == '\0'|| *ll == '#')
1105 		return (0);
1106 
1107 	token = ucp_grab_token(&ll, line_num, " \t\n");
1108 	if (token == NULL)
1109 		return (-1);
1110 
1111 	nm->is_nt4 = 0;
1112 	nm->direction = DIR_W2U;
1113 
1114 	nm->windomain = NULL;
1115 	nm->winname = token;
1116 	nm->unixname = strndup(unixname, unixname_l);
1117 	if (nm->unixname == NULL)
1118 		return (-1);
1119 
1120 	ll += strspn(ll, " \t\n");
1121 	return (1);
1122 }
1123 
1124 /* Parse line to name_mapping_t. Basicaly just a format switch. */
1125 static int
1126 line2nm(char *line, int line_num, name_mapping_t *nm, format_t f) {
1127 	switch (f) {
1128 	case USERMAP_CFG:
1129 		if (line == NULL)
1130 			return (0);
1131 		else
1132 			return (ucp_line2nm(line, line_num, nm));
1133 	case SMBUSERS:
1134 		return (sup_line2nm(line, line_num, nm));
1135 	default:
1136 		(void) fprintf(stderr, gettext("Internal error.\n"));
1137 	}
1138 
1139 	return (-1);
1140 }
1141 
1142 
1143 /* Examine -f flag and return the appropriate format_t */
1144 static format_t
1145 ff2format(char *ff, int is_mandatory) {
1146 
1147 	if (ff == NULL && is_mandatory) {
1148 		(void) fprintf(stderr, gettext("Format not given.\n"));
1149 		return (UNDEFINED_FORMAT);
1150 	}
1151 
1152 	if (ff == NULL)
1153 		return (DEFAULT_FORMAT);
1154 
1155 	if (strcasecmp(ff, "usermap.cfg") == 0)
1156 		return (USERMAP_CFG);
1157 
1158 	if (strcasecmp(ff, "smbusers") == 0)
1159 		return (SMBUSERS);
1160 
1161 	(void) fprintf(stderr,
1162 		    gettext("The only known formats are: \"usermap.cfg\" and "
1163 			"\"smbusers\".\n"));
1164 	return (UNDEFINED_FORMAT);
1165 }
1166 
1167 /* Delete all namerules of the given type */
1168 static int
1169 flush_nm(boolean_t is_user)
1170 {
1171 	idmap_stat stat;
1172 
1173 	stat = idmap_udt_flush_namerules(udt, is_user);
1174 	if (stat < 0) {
1175 		(void) fprintf(stderr,
1176 		    is_user ? gettext("Unable to flush users (%s).\n")
1177 		    : gettext("Unable to flush groups (%s).\n"),
1178 		    idmap_stat2string(handle, stat));
1179 		return (-1);
1180 	}
1181 	return (0);
1182 }
1183 
1184 /* import command handler */
1185 static int
1186 /* LINTED E_FUNC_ARG_UNUSED */
1187 do_import(flag_t *f, int argc, char **argv)
1188 {
1189 	name_mapping_t *nm;
1190 	char line[MAX_INPUT_LINE_SZ];
1191 	format_t format;
1192 	int rc = 0;
1193 	idmap_stat stat;
1194 	int line_num;
1195 	FILE *file = NULL;
1196 
1197 	if (batch_mode) {
1198 		(void) fprintf(stderr,
1199 		    gettext("Import is not allowed in the batch mode.\n"));
1200 		return (-1);
1201 	}
1202 
1203 	format = ff2format(argv[0], 1);
1204 	if (format == UNDEFINED_FORMAT)
1205 		return (-1);
1206 
1207 	if (init_udt_command())
1208 		return (-1);
1209 
1210 	/* We don't flush groups in the usermap.cfg nor smbusers format */
1211 	if (f[F_FLAG] != NULL &&
1212 	    flush_nm(B_TRUE) < 0 &&
1213 	    (format == USERMAP_CFG || format == SMBUSERS ||
1214 	    flush_nm(B_FALSE) < 0)) {
1215 		rc = -1;
1216 		goto cleanup;
1217 	}
1218 
1219 	line_num = 0;
1220 
1221 	/* Where we import from? */
1222 	if (f[f_FLAG] == NULL)
1223 		file = stdin;
1224 	else {
1225 		file = fopen(f[f_FLAG], "r");
1226 		if (file == NULL) {
1227 			perror(f[f_FLAG]);
1228 			goto cleanup;
1229 		}
1230 	}
1231 
1232 
1233 	while (fgets(line, MAX_INPUT_LINE_SZ, file)) {
1234 		char *line2 = line;
1235 		line_num++;
1236 
1237 		/*
1238 		 * In SMBUSERS format there can be more mappings on
1239 		 * each line. So we need the internal cycle for each line.
1240 		 */
1241 		do {
1242 			nm = name_mapping_init();
1243 			if (nm == NULL) {
1244 				rc = -1;
1245 				goto cleanup;
1246 			}
1247 
1248 			rc = line2nm(line2, line_num, nm, format);
1249 			line2 = NULL;
1250 
1251 			if (rc < 1) {
1252 				name_mapping_fini(nm);
1253 				break;
1254 			}
1255 
1256 			stat = idmap_udt_add_namerule(udt, nm->windomain,
1257 			    nm->is_user ? B_TRUE : B_FALSE, nm->winname,
1258 			    nm->unixname, nm->is_nt4, nm->direction);
1259 			if (stat < 0) {
1260 				(void) fprintf(stderr,
1261 				    gettext("Transaction error (%s)\n"),
1262 				    idmap_stat2string(handle, stat));
1263 				rc = -1;
1264 			}
1265 
1266 			name_mapping_fini(nm);
1267 
1268 		} while (rc >= 0);
1269 
1270 		if (rc < 0) {
1271 			(void) fprintf(stderr,
1272 			    gettext("Import canceled.\n"));
1273 			break;
1274 		}
1275 	}
1276 
1277 cleanup:
1278 	fini_udt_command(rc < 0 ? 0 : 1);
1279 	if (file != NULL && file != stdin)
1280 		(void) fclose(file);
1281 	return (rc);
1282 }
1283 
1284 
1285 /*
1286  * List name mappings in the format specified. list_users /
1287  * list_groups determine which type to list. The output goes to the
1288  * file fi.
1289  */
1290 static int
1291 list_name_mappings(int list_users, int list_groups, format_t format, FILE *fi)
1292 {
1293 	idmap_stat stat;
1294 	idmap_iter_t *ihandle;
1295 	name_mapping_t *nm;
1296 	int is_user;
1297 
1298 	for (is_user = I_YES; is_user >= I_NO; is_user--) {
1299 		if (is_user && !list_users)
1300 			continue;
1301 		if (!is_user && !list_groups)
1302 			continue;
1303 		/* Only users can be in USERMAP_CFG format, not a group */
1304 		if (!is_user && format == USERMAP_CFG)
1305 			continue;
1306 
1307 		stat = idmap_iter_namerules(handle, NULL, is_user, NULL,
1308 		    NULL, &ihandle);
1309 		if (stat < 0) {
1310 			(void) fprintf(stderr,
1311 			    gettext("Iteration handle not obtained (%s)\n"),
1312 			    idmap_stat2string(handle, stat));
1313 			idmap_iter_destroy(ihandle);
1314 			return (-1);
1315 		}
1316 
1317 		(void) print_mapping_init(format, fi);
1318 
1319 		do {
1320 			nm = name_mapping_init();
1321 			if (nm == NULL) {
1322 				idmap_iter_destroy(ihandle);
1323 				return (-1);
1324 			}
1325 
1326 			stat = idmap_iter_next_namerule(ihandle, &nm->windomain,
1327 			    &nm->winname, &nm->unixname, &nm->is_nt4,
1328 			    &nm->direction);
1329 			if (stat >= 0) {
1330 				nm->is_user = is_user;
1331 				(void) print_mapping(nm);
1332 			}
1333 
1334 			name_mapping_fini(nm);
1335 
1336 		} while (stat > 0);
1337 
1338 		(void) print_mapping_fini();
1339 
1340 		if (stat < 0 && stat !=  IDMAP_ERR_NOTFOUND) {
1341 			(void) fprintf(stderr,
1342 			    gettext("Error during iteration (%s)\n"),
1343 			    idmap_stat2string(handle, stat));
1344 			idmap_iter_destroy(ihandle);
1345 			return (-1);
1346 		}
1347 
1348 		idmap_iter_destroy(ihandle);
1349 	}
1350 	return (0);
1351 }
1352 
1353 /* Export command handler */
1354 static int
1355 /* LINTED E_FUNC_ARG_UNUSED */
1356 do_export(flag_t *f, int argc, char **argv) {
1357 	int rc;
1358 	format_t format;
1359 	FILE *fi;
1360 
1361 	format = ff2format(argv[0], 1);
1362 	if (format == UNDEFINED_FORMAT)
1363 		return (-1);
1364 
1365 	/* Where do we output to? */
1366 	if (f[f_FLAG] == NULL)
1367 		fi = stdout;
1368 	else {
1369 		fi = fopen(f[f_FLAG], "w");
1370 		if (fi == NULL) {
1371 			perror(f[f_FLAG]);
1372 			return (-1);
1373 		}
1374 	}
1375 
1376 	if (init_command() < 0) {
1377 		rc = -1;
1378 		goto cleanup;
1379 	}
1380 
1381 	/* List the requested types: */
1382 	rc = list_name_mappings(is_user_wanted(f),
1383 	    is_group_wanted(f),
1384 	    format,
1385 	    fi);
1386 
1387 	fini_command();
1388 
1389 cleanup:
1390 	if (fi != NULL && fi != stdout)
1391 		(void) fclose(fi);
1392 	return (rc);
1393 }
1394 
1395 /* List command handler */
1396 static int
1397 /* LINTED E_FUNC_ARG_UNUSED */
1398 do_list_name_mappings(flag_t *f, int argc, char **argv)
1399 {
1400 	int rc;
1401 
1402 	if (init_command()) {
1403 		return (-1);
1404 	}
1405 
1406 	/* List the requested types: */
1407 	rc = list_name_mappings(is_user_wanted(f),
1408 	    is_group_wanted(f),
1409 	    DEFAULT_FORMAT,
1410 	    stdout);
1411 
1412 	fini_command();
1413 	return (rc);
1414 }
1415 
1416 /* This is just a debug function for dumping flags */
1417 static void
1418 print_flags(flag_t *f)
1419 {
1420 	int c;
1421 	for (c = 0; c < FLAG_ALPHABET_SIZE; c++) {
1422 		if (f[c] == FLAG_SET)
1423 			(void) printf("FLAG: -%c, VALUE: %p\n", c,
1424 			    (void *) f[c]);
1425 		else if (f[c])
1426 			(void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]);
1427 	}
1428 }
1429 
1430 /*
1431  * Compare two strings just like strcmp, but stop before the end of
1432  * the s2
1433  */
1434 static int
1435 strcmp_no0(const char *s1, const char *s2) {
1436 	return (strncmp(s1, s2, strlen(s2)));
1437 }
1438 
1439 /* The same as strcmp_no0, but case insensitive. */
1440 static int
1441 strcasecmp_no0(const char *s1, const char *s2) {
1442 	return (strncasecmp(s1, s2, strlen(s2)));
1443 }
1444 
1445 /*
1446  * This function splits name to the relevant pieces: is_user, winname,
1447  * windomain unixname. Sometimes it is not possible to determine OS
1448  * side, because it could be determined by the opposite name in idmap
1449  * show. So this function must be called several times.
1450  *
1451  * Return values: -1 ... clear syntax error
1452  *                0  ... it wasnt possible to determine
1453  *                1  ... determined
1454  */
1455 static int
1456 name2parts(char *name, name_mapping_t *nm) {
1457 	char *it;
1458 	int is_win = I_NO;
1459 	int is_unix = I_NO;
1460 
1461 	if (nm->winname != NULL && nm->unixname != NULL)
1462 		return (0);
1463 
1464 	/* If it starts with type string, that is easy: */
1465 	if (it = strchr(name, ':')) {
1466 		if (strcmp_no0(name, ID_UNIXNAME ":") == 0) {
1467 			if (nm->unixname != NULL)
1468 				return (0);
1469 			is_unix = I_YES;
1470 		} else if (strcmp_no0(name, ID_WINNAME ":") == 0) {
1471 			if (nm->winname != NULL)
1472 				return (0);
1473 			is_win = I_YES;
1474 		} else {
1475 			(void) fprintf(stderr,
1476 			    gettext("Error: invalid identity type\n"));
1477 			return (-1);
1478 		}
1479 		name = it + 1;
1480 	}
1481 
1482 	/* If it contains '@' or '\\', then it is a winname with domain */
1483 	if (!is_unix && nm->winname == NULL) {
1484 		if ((it = strchr(name, '@')) != NULL) {
1485 			int length = it-name+1;
1486 			nm->winname = (char *)malloc(length * sizeof (char));
1487 			(void) strncpy(nm->winname, name, length - 1);
1488 			nm->winname[length - 1] = '\0';
1489 			nm->windomain = strdup(it + 1);
1490 			return (1);
1491 		} else if ((it = strrchr(name, '\\')) != NULL) {
1492 			int length = it-name+1;
1493 			nm->windomain = (char *)malloc(length * sizeof (char));
1494 			(void) strncpy(nm->windomain, name, length - 1);
1495 			nm->windomain[length - 1] = '\0';
1496 			nm->winname = strdup(it + 1);
1497 			nm->is_nt4 = B_TRUE;
1498 			return (1);
1499 		}
1500 	}
1501 
1502 	/*
1503 	 * if is_unix/is_win is not yet determined, then the last
1504 	 * hope is that the opposite side is known already. In that
1505 	 * case, it is the only remaining side.
1506 	 */
1507 	if (is_unix || nm->unixname == NULL && nm->winname != NULL) {
1508 		if (strlen(name) == 0)
1509 			nm->unixname = strdup("\"\"");
1510 		else
1511 			nm->unixname = strdup(name);
1512 		return (1);
1513 	} else if (is_win || nm->unixname != NULL && nm->winname == NULL) {
1514 		if (strlen(name) == 0)
1515 			nm->winname = strdup("\"\"");
1516 		else
1517 			nm->winname = strdup(name);
1518 		nm->windomain = NULL;
1519 		return (1);
1520 	}
1521 
1522 	return (0);
1523 }
1524 
1525 /* add command handler. */
1526 static int
1527 do_add_name_mapping(flag_t *f, int argc, char **argv)
1528 {
1529 	name_mapping_t *nm;
1530 	int rc = 0;
1531 	int i;
1532 	int is_argv0_unix = -1;
1533 	idmap_stat stat;
1534 
1535 
1536 	/* Two arguments and exactly one of -u, -g must be specified */
1537 	if (argc < 2) {
1538 		(void) fprintf(stderr, gettext("Not enough arguments.\n"));
1539 		return (-1);
1540 	} else if (argc > 2)  {
1541 		(void) fprintf(stderr, gettext("Too many arguments.\n"));
1542 		return (-1);
1543 	} else if (!is_type_determined(f))
1544 		return (-1);
1545 
1546 	/*
1547 	 * Direction can be determined by the opposite name, so we
1548 	 * need to run name2parts twice for the first name, i.e. 3x in
1549 	 * total.
1550 	 */
1551 	nm = name_mapping_init();
1552 	if (nm == NULL)
1553 		return (-1);
1554 
1555 	nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO;
1556 
1557 	for (i = 0; i < 3; i++) {
1558 		switch (name2parts(argv[i % 2], nm)) {
1559 		case -1:
1560 			name_mapping_fini(nm);
1561 			return (-1);
1562 		case 1:
1563 			if (is_argv0_unix < 0)
1564 				is_argv0_unix =
1565 				    i % 2 ^ (nm->unixname != NULL ? 1 : 0);
1566 			break;
1567 		}
1568 	}
1569 
1570 	if (nm->winname == NULL || nm->unixname == NULL) {
1571 		(void) fprintf(stderr, gettext("Name types not determined.\n"));
1572 		name_mapping_fini(nm);
1573 		return (-1);
1574 	}
1575 
1576 	if (f[d_FLAG] != NULL)
1577 		nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U;
1578 	else
1579 		nm->direction = DIR_BI;
1580 
1581 	/* Now let us write it: */
1582 
1583 	if (init_udt_command()) {
1584 		name_mapping_fini(nm);
1585 		return (-1);
1586 	}
1587 
1588 	stat = idmap_udt_add_namerule(udt, nm->windomain,
1589 	    nm->is_user ? B_TRUE : B_FALSE, nm->winname, nm->unixname,
1590 	    nm->is_nt4, nm->direction);
1591 
1592 	/* We echo the mapping */
1593 	(void) print_mapping_init(DEFAULT_FORMAT, stdout);
1594 	(void) print_mapping(nm);
1595 	(void) print_mapping_fini();
1596 
1597 	if (stat < 0) {
1598 		(void) fprintf(stderr,
1599 		    gettext("Mapping not created (%s)\n"),
1600 		    idmap_stat2string(handle, stat));
1601 		rc = -1;
1602 	}
1603 
1604 cleanup:
1605 	name_mapping_fini(nm);
1606 	fini_udt_command(1);
1607 	return (rc);
1608 }
1609 
1610 /* remove command handler */
1611 static int
1612 do_remove_name_mapping(flag_t *f, int argc, char **argv)
1613 {
1614 	name_mapping_t *nm;
1615 	int rc = 0;
1616 	int i;
1617 	int is_argv0_unix = -1;
1618 	idmap_stat stat;
1619 
1620 	/* "-a" means we flush all of them */
1621 	if (f[a_FLAG] != NULL) {
1622 		if (argc) {
1623 			(void) fprintf(stderr,
1624 			    gettext("Too many arguments.\n"));
1625 			return (-1);
1626 		}
1627 
1628 		if (!is_type_determined(f))
1629 			return (-1);
1630 
1631 		if (init_udt_command())
1632 			return (-1);
1633 		rc = flush_nm(f[u_FLAG] != NULL ? B_TRUE : B_FALSE);
1634 
1635 		fini_udt_command(rc ? 0 : 1);
1636 		return (rc);
1637 	}
1638 
1639 	/* Contrary to add_name_mapping, we can have only one argument */
1640 	if (argc < 1) {
1641 		(void) fprintf(stderr, gettext("Not enough arguments.\n"));
1642 		return (-1);
1643 	} else if (argc > 2) {
1644 		(void) fprintf(stderr, gettext("Too many arguments.\n"));
1645 		return (-1);
1646 	} else if (!is_type_determined(f)) {
1647 		return (-1);
1648 	} else if (
1649 		/* both -f and -t: */
1650 	    f[f_FLAG] != NULL && f[t_FLAG] != NULL ||
1651 		/* -d with a single argument: */
1652 	    argc == 1 && f[d_FLAG] != NULL ||
1653 		/* -f or -t with two arguments: */
1654 	    argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) {
1655 		(void) fprintf(stderr,
1656 		    gettext("Direction ambiguous.\n"));
1657 		return (-1);
1658 	}
1659 
1660 
1661 	/*
1662 	 * Similar to do_add_name_mapping - see the comments
1663 	 * there. Except we may have only one argument here.
1664 	 */
1665 	nm = name_mapping_init();
1666 	if (nm == NULL)
1667 		return (-1);
1668 
1669 	nm->is_user = f[u_FLAG] != NULL ? I_YES : I_NO;
1670 
1671 	for (i = 0; i < 2 * argc - 1; i++) {
1672 		switch (name2parts(argv[i % 2], nm)) {
1673 		case -1:
1674 			name_mapping_fini(nm);
1675 			return (-1);
1676 		case 1:
1677 			if (is_argv0_unix < 0)
1678 				is_argv0_unix = i % 2 ^ (nm->unixname ? 1 : 0);
1679 			break;
1680 		}
1681 	}
1682 
1683 
1684 	if (nm->winname == NULL && nm->unixname == NULL) {
1685 		(void) fprintf(stderr, gettext("Name types not determined.\n"));
1686 		name_mapping_fini(nm);
1687 		return (-1);
1688 	}
1689 
1690 	/*
1691 	 * If the direction is not specified by a -d/-f/-t flag, then it
1692 	 * is DIR_UNKNOWN, because in that case we want to remove any
1693 	 * mapping. If it was DIR_BI, idmap_api would delete a
1694 	 * bidirectional one only.
1695 	 */
1696 	if (f[d_FLAG] != NULL || f[f_FLAG] != NULL)
1697 		nm->direction = is_argv0_unix ? DIR_U2W : DIR_W2U;
1698 	else if (f[t_FLAG] != NULL)
1699 		nm->direction = is_argv0_unix ? DIR_W2U : DIR_U2W;
1700 	else
1701 		nm->direction = DIR_UNKNOWN;
1702 
1703 	if (init_udt_command()) {
1704 		name_mapping_fini(nm);
1705 		return (-1);
1706 	}
1707 
1708 	stat = idmap_udt_rm_namerule(udt, nm->is_user ? B_TRUE : B_FALSE,
1709 	    nm->windomain, nm->winname, nm->unixname, nm->direction);
1710 
1711 	if (stat < 0) {
1712 		(void) fprintf(stderr,
1713 		    gettext("Mapping not deleted (%s)\n"),
1714 		    idmap_stat2string(handle, stat));
1715 		rc = -1;
1716 	}
1717 
1718 cleanup:
1719 	name_mapping_fini(nm);
1720 	fini_udt_command(1);
1721 	return (rc);
1722 }
1723 
1724 
1725 /* exit command handler */
1726 static int
1727 /* LINTED E_FUNC_ARG_UNUSED */
1728 do_exit(flag_t *f, int argc, char **argv) {
1729 	return (0);
1730 }
1731 
1732 
1733 /* debug command handler: just print the parameters */
1734 static int
1735 /* LINTED E_STATIC_UNUSED */
1736 debug_print_params(flag_t *f, int argc, char **argv)
1737 {
1738 	int i;
1739 #if 0
1740 	char *leaktest = (char *)malloc(100);
1741 #endif
1742 
1743 	print_flags(f);
1744 
1745 	for (i = 0; i < argc; i++) {
1746 		(void) printf("Argument %d: %s\n", i, argv[i]);
1747 	}
1748 
1749 	(void) fflush(stdout);
1750 	return (0);
1751 }
1752 
1753 /*
1754  * Return a pointer after a given prefix. If there is no such prefix,
1755  * return NULL
1756  */
1757 static char *
1758 get_root(char *string, char *typestring) {
1759 	if (strcasecmp_no0(string, typestring) != 0)
1760 		return (NULL);
1761 	return (string + strlen(typestring));
1762 }
1763 
1764 /*
1765  * From name_mapping_t, asseble a string containing identity of the
1766  * given type.
1767  */
1768 static int
1769 nm2type(name_mapping_t *nm, int type, char **to) {
1770 	switch (type) {
1771 	case TYPE_SID:
1772 		if (nm->sidprefix == NULL)
1773 			return (-1);
1774 		*to = sid_format(nm->sidprefix, nm->rid);
1775 		return (0);
1776 	case TYPE_WN:
1777 		return (nm2winqn(nm, to));
1778 	case TYPE_UID:
1779 	case TYPE_GID:
1780 	case TYPE_PID:
1781 		*to = pid_format(nm->pid, nm->is_user);
1782 		if (*to == NULL)
1783 			return (-1);
1784 		else
1785 			return (0);
1786 	case TYPE_UN:
1787 		return (nm2unixname(nm, to));
1788 	default:
1789 		(void) fprintf(stderr, gettext("Internal error.\n"));
1790 		return (-1);
1791 	}
1792 	/* never reached */
1793 }
1794 
1795 /* show command handler */
1796 static int
1797 do_show_mapping(flag_t *f, int argc, char **argv)
1798 {
1799 	idmap_stat stat = 0;
1800 	int flag;
1801 	idmap_stat map_stat = 0;
1802 	int type_from;
1803 	int type_to;
1804 	char *root;
1805 	name_mapping_t *nm = NULL;
1806 	char *fromname;
1807 	char *toname;
1808 
1809 	if (argc == 0) {
1810 		(void) fprintf(stderr,
1811 		    gettext("No identity given\n"));
1812 		return (-1);
1813 	} else if (argc > 2) {
1814 		(void) fprintf(stderr,
1815 		    gettext("Too many arguments.\n"));
1816 		return (-1);
1817 	}
1818 
1819 	flag = f[c_FLAG] != NULL ? 0 : IDMAP_REQ_FLG_NO_NEW_ID_ALLOC;
1820 
1821 	if (init_command())
1822 		return (-1);
1823 
1824 	nm = name_mapping_init();
1825 	if (nm == NULL)
1826 		goto cleanup;
1827 
1828 	/* First, determine type_from: */
1829 	if ((root = get_root(argv[0], ID_UID ":")) != NULL)
1830 		type_from = TYPE_UID;
1831 	else if ((root = get_root(argv[0], ID_GID ":")) != NULL)
1832 		type_from = TYPE_GID;
1833 	else if ((root = get_root(argv[0], ID_SID ":")) != NULL)
1834 		type_from = TYPE_SID;
1835 	else if (name2parts(argv[0], nm) > 0) {
1836 		if (nm->unixname != NULL)
1837 			type_from = TYPE_UN;
1838 		else
1839 			type_from = TYPE_WN;
1840 	} else {
1841 		(void) fprintf(stderr,
1842 		    gettext("Invalid type.\n"));
1843 		stat = IDMAP_ERR_ARG;
1844 		goto cleanup;
1845 	}
1846 
1847 	/* Second, determine type_to: */
1848 	if (argc < 2) {
1849 		type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID;
1850 		if (type_from & IS_NAME)
1851 			type_to |= IS_NAME;
1852 	} else if (strcasecmp(argv[1], ID_UID) == 0)
1853 		type_to = TYPE_UID;
1854 	else if (strcasecmp(argv[1], ID_GID) == 0)
1855 		type_to = TYPE_GID;
1856 	else if (strcasecmp(argv[1], ID_SID) == 0)
1857 		type_to = TYPE_SID;
1858 	else if (strcmp(argv[1], ID_UNIXNAME) == 0)
1859 		type_to = TYPE_UN;
1860 	else if (strcmp(argv[1], ID_WINNAME) == 0)
1861 		type_to = TYPE_WN;
1862 	else {
1863 		(void) fprintf(stderr,
1864 		    gettext("Ivnalid target type.\n"));
1865 		stat = IDMAP_ERR_ARG;
1866 		goto cleanup;
1867 	}
1868 
1869 	/* Are both arguments the same OS side? */
1870 	if (!(type_from & IS_WIN ^ type_to & IS_WIN)) {
1871 		(void) fprintf(stderr,
1872 		    gettext("Direction ambiguous.\n"));
1873 		stat = IDMAP_ERR_ARG;
1874 		goto cleanup;
1875 	}
1876 
1877 	if (type_from == TYPE_SID) {
1878 		char *p, *end, *sid;
1879 		sid = argv[0] + 4;
1880 		if ((p = strrchr(sid, '-')) == NULL) {
1881 			(void) fprintf(stderr,
1882 			    gettext("Invalid SID %s\n"), sid);
1883 			goto cleanup;
1884 		}
1885 		/* Replace '-' by string terminator so that sid = sidprefix */
1886 		*p = 0;
1887 		nm->sidprefix = strdup(sid);
1888 		nm->rid = strtoll(p + 1, &end, 10);
1889 		/* Restore '-' */
1890 		*p = '-';
1891 
1892 	} else if (type_from == TYPE_UID || type_from == TYPE_GID) {
1893 		nm->pid = (uid_t)atol(root);
1894 	}
1895 
1896 /*
1897  * We have two interfaces for retrieving the mappings:
1898  * idmap_get_sidbyuid & comp (the batch interface) and
1899  * idmap_get_w2u_mapping & comp. We  want to use both of them, because
1900  * the former mimicks kernel interface better and the later offers the
1901  * string names. In the batch case, our batch has always size 1.
1902  */
1903 	if (type_from & IS_NAME || type_to & IS_NAME) {
1904 		if (type_from & IS_WIN) {
1905 			if (type_to == TYPE_UID)
1906 				nm->is_user = I_YES;
1907 			else if (type_to == TYPE_GID)
1908 			nm->is_user = I_NO;
1909 
1910 			map_stat = idmap_get_w2u_mapping(handle,
1911 			    nm->sidprefix,
1912 			    &nm->rid,
1913 			    nm->winname,
1914 			    nm->windomain,
1915 			    flag,
1916 			    &nm->is_user,
1917 			    &nm->pid,
1918 			    &nm->unixname,
1919 			    &nm->direction);
1920 		} else {
1921 			if (type_from == TYPE_UID)
1922 				nm->is_user = I_YES;
1923 			else if (type_from == TYPE_GID)
1924 				nm->is_user = I_NO;
1925 
1926 			map_stat = idmap_get_u2w_mapping(handle,
1927 			    &nm->pid,
1928 			    nm->unixname,
1929 			    flag,
1930 			    nm->is_user,
1931 			    &nm->sidprefix,
1932 			    &nm->rid,
1933 			    &nm->winname,
1934 			    &nm->windomain,
1935 			    &nm->direction);
1936 		}
1937 
1938 	} else {
1939 		/* batch handle */
1940 		idmap_get_handle_t *ghandle = NULL;
1941 		/* To be passed to idmap_get_uidbysid  */
1942 		gid_t gid = UNDEFINED_GID;
1943 		/* To be passed to idmap_get_gidbysid  */
1944 		uid_t uid = UNDEFINED_UID;
1945 
1946 
1947 		/* Create an in-memory structure for all the batch: */
1948 		stat = idmap_get_create(handle, &ghandle);
1949 		if (stat < 0) {
1950 			(void) fprintf(stderr,
1951 			    gettext("Unable to create handle for communicating"
1952 			    " with idmapd(1M) (%s)\n"),
1953 			    idmap_stat2string(handle, stat));
1954 			idmap_get_destroy(ghandle);
1955 			goto cleanup;
1956 		}
1957 
1958 		/* Schedule the request: */
1959 		if (type_from == TYPE_SID && type_to == TYPE_UID) {
1960 			stat = idmap_get_uidbysid(ghandle,
1961 			    nm->sidprefix,
1962 			    nm->rid,
1963 			    flag,
1964 			    &uid,
1965 			    &map_stat);
1966 			nm->is_user = I_YES;
1967 		} else if (type_from == TYPE_SID && type_to == TYPE_GID) {
1968 			stat =  idmap_get_gidbysid(ghandle,
1969 			    nm->sidprefix,
1970 			    nm->rid,
1971 			    flag,
1972 			    &gid,
1973 			    &map_stat);
1974 			nm->is_user = I_NO;
1975 		} else if (type_from == TYPE_SID && type_to == TYPE_PID)
1976 			stat = idmap_get_pidbysid(ghandle,
1977 			    nm->sidprefix,
1978 			    nm->rid,
1979 			    flag,
1980 			    &nm->pid,
1981 			    &nm->is_user,
1982 			    &map_stat);
1983 		else if (type_from == TYPE_UID && type_to == TYPE_SID) {
1984 			stat = idmap_get_sidbyuid(ghandle,
1985 			    nm->pid,
1986 			    flag,
1987 			    &nm->sidprefix,
1988 			    &nm->rid,
1989 			    &map_stat);
1990 			nm->is_user = I_YES;
1991 		} else if (type_from == TYPE_GID && type_to == TYPE_SID) {
1992 			stat = idmap_get_sidbygid(ghandle,
1993 			    (gid_t)nm->pid,
1994 			    flag,
1995 			    &nm->sidprefix,
1996 			    &nm->rid,
1997 			    &map_stat);
1998 			nm->is_user = I_NO;
1999 		} else {
2000 			(void) fprintf(stderr, gettext("Internal error.\n"));
2001 			exit(1);
2002 		}
2003 
2004 		if (stat < 0) {
2005 			(void) fprintf(stderr,
2006 			    gettext("Request for %.3s not sent (%s)\n"),
2007 			    argv[0], idmap_stat2string(handle, stat));
2008 			idmap_get_destroy(ghandle);
2009 			goto cleanup;
2010 		}
2011 
2012 		/* Send the batch to idmapd and obtain results: */
2013 		stat = idmap_get_mappings(ghandle);
2014 		if (stat < 0) {
2015 			(void) fprintf(stderr,
2016 			    gettext("Mappings not obtained because of"
2017 			    " RPC problem (%s)\n"),
2018 			    idmap_stat2string(handle, stat));
2019 			idmap_get_destroy(ghandle);
2020 			goto cleanup;
2021 		}
2022 
2023 		/* Destroy the batch handle: */
2024 		idmap_get_destroy(ghandle);
2025 
2026 		if (type_to == TYPE_UID)
2027 			nm->pid = uid;
2028 		else if (type_to == TYPE_GID)
2029 			nm->pid = (uid_t)gid;
2030 
2031 	}
2032 
2033 	/*
2034 	 * If there was -c flag, we do output whatever we can even in
2035 	 * the case of error:
2036 	 */
2037 	if (map_stat < 0) {
2038 		(void) fprintf(stderr,
2039 		    gettext("%s\n"),
2040 		    idmap_stat2string(handle, map_stat));
2041 		if (flag == IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
2042 			goto cleanup;
2043 	}
2044 
2045 
2046 	if (nm2type(nm, type_from, &fromname) < 0)
2047 		goto cleanup;
2048 
2049 	if (nm2type(nm, type_to, &toname) < 0) {
2050 		if (flag == 0)
2051 			(void) printf("%s -> %s:%u\n",
2052 			    fromname,
2053 			    (type_from | type_to) & IS_GROUP ? ID_GID : ID_UID,
2054 			    UID_NOBODY);
2055 		free(fromname);
2056 		goto cleanup;
2057 	}
2058 
2059 	(void) printf("%s -> %s\n", fromname, toname);
2060 	free(fromname);
2061 	free(toname);
2062 
2063 cleanup:
2064 	if (nm != NULL)
2065 		name_mapping_fini(nm);
2066 	fini_command();
2067 	return (stat < 0 || map_stat < 0 ? -1 : 0);
2068 }
2069 
2070 /* main function. Returns 1 for error, 0 otherwise */
2071 int
2072 main(int argc, char *argv[]) {
2073 	int rc;
2074 
2075 	/* set locale and domain for internationalization */
2076 	(void) setlocale(LC_ALL, "");
2077 	(void) textdomain(TEXT_DOMAIN);
2078 
2079 	/* idmap_engine determines the batch_mode: */
2080 	rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t),
2081 		commands,
2082 		argc - 1,
2083 		argv + 1,
2084 		&batch_mode);
2085 
2086 	if (rc < 0) {
2087 		(void) engine_fini();
2088 		if (rc == IDMAP_ENG_ERROR_SILENT)
2089 			help();
2090 		return (1);
2091 	}
2092 
2093 	udt_used = 0;
2094 	if (batch_mode) {
2095 		if (init_udt_batch() < 0)
2096 			return (1);
2097 	}
2098 
2099 	rc = run_engine(argc - 1, argv + 1);
2100 
2101 	if (batch_mode) {
2102 		batch_mode = 0;
2103 		fini_udt_command(rc == 0 ? 1 : 0);
2104 	}
2105 
2106 	(void) engine_fini();
2107 	return (rc == 0 ? 0 : 1);
2108 }
2109