idmap.c revision 1fdeec650620e8498c06f832ea4bd2292f7e9632
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <strings.h>
#include <errno.h>
#include <limits.h>
#include <syslog.h>
#include <stdarg.h>
#include <note.h>
#include "idmap_engine.h"
#include "idmap_priv.h"
#include "namemaps.h"
#include "libadutils.h"
/* Initialization values for pids/rids: */
#define UNDEFINED_UID (uid_t)-1
#define UNDEFINED_GID (gid_t)-1
#define UNDEFINED_RID (idmap_rid_t)-1;
#define CHECK_NULL(s) (s != NULL ? s : "null")
/*
* used in do_show for the type of argument, which can be winname,
* unixname, uid, gid, sid or not given at all:
*/
#define TYPE_SID 0x010 /* sid */
#define TYPE_USID 0x011 /* usid */
#define TYPE_GSID 0x012 /* gsid */
#define TYPE_WN 0x110 /* winname */
#define TYPE_WU 0x111 /* winuser */
#define TYPE_WG 0x112 /* wingroup */
#define TYPE_UID 0x001 /* uid */
#define TYPE_GID 0x002 /* gid */
#define TYPE_PID 0x000 /* pid */
#define TYPE_UN 0x100 /* unixname */
#define TYPE_UU 0x101 /* unixuser */
#define TYPE_UG 0x102 /* unixgroup */
#define IS_WIN 0x010 /* mask for the windows types */
#define IS_NAME 0x100 /* mask for string name types */
#define IS_USER 0x001 /* mask for user types */
#define IS_GROUP 0x002 /* mask for group types */
#define TYPE_INVALID 0x1000 /* Invalid input */
#define TYPE_AUTO 0xaaa /* Autodetection required */
/* Identity type strings */
#define ID_WINNAME "winname"
#define ID_UNIXUSER "unixuser"
#define ID_UNIXGROUP "unixgroup"
#define ID_WINUSER "winuser"
#define ID_WINGROUP "wingroup"
#define ID_USID "usid"
#define ID_GSID "gsid"
#define ID_SID "sid"
#define ID_UID "uid"
#define ID_GID "gid"
#define ID_UNKNOWN "unknown"
#define INHIBITED(str) (str == NULL || *str == 0 || strcmp(str, "\"\"") == 0)
typedef struct {
char *identity;
int code;
} id_code_t;
id_code_t identity2code[] = {
{ID_WINNAME, TYPE_WN},
{ID_UNIXUSER, TYPE_UU},
{ID_UNIXGROUP, TYPE_UG},
{ID_WINUSER, TYPE_WU},
{ID_WINGROUP, TYPE_WG},
{ID_USID, TYPE_USID},
{ID_GSID, TYPE_GSID},
{ID_SID, TYPE_SID},
{ID_UID, TYPE_UID},
{ID_GID, TYPE_GID}
};
/* Flags */
#define f_FLAG 'f'
#define t_FLAG 't'
#define d_FLAG 'd'
#define D_FLAG 'D'
#define F_FLAG 'F'
#define a_FLAG 'a'
#define n_FLAG 'n'
#define c_FLAG 'c'
#define v_FLAG 'v'
#define V_FLAG 'V'
#define j_FLAG 'j'
/* used in the function do_import */
#define MAX_INPUT_LINE_SZ 2047
typedef struct {
int is_user;
int is_wuser;
int direction;
boolean_t is_nt4;
char *unixname;
char *winname;
char *windomain;
char *sidprefix;
idmap_rid_t rid;
uid_t pid;
} name_mapping_t;
/*
* Formats of the output:
*
* Idmap reads/prints mappings in several formats: ordinary mappings,
* name mappings in Samba username map format (smbusers), Netapp
* usermap.cfg.
*
* DEFAULT_FORMAT are in fact the idmap subcommands suitable for
* piping to idmap standart input. For example
* add -d winuser:bob@foo.com unixuser:fred
* add -d winuser:bob2bar.com unixuser:fred
*
* SMBUSERS is the format of Samba username map (smbusers). For full
* documentation, search for "username map" in smb.conf manpage.
* The format is for example
* fred = bob@foo.com bob2@bar.com
*
* USERMAP_CFG is the format of Netapp usermap.cfg file. Search
* http://www.netapp.com/ for more documentation. IP qualifiers are not
* supported.
* The format is for example
* bob@foo.com => fred
* "Bob With Spaces"@bar.com => fred #comment
*
* The previous formats were for name rules. MAPPING_NAME and
* MAPPING_ID are for the actual mappings, as seen in show/dump
* commands. MAPPING_NAME prefers the string names of the user over
* their numerical identificators. MAPPING_ID prints just the
* identificators.
* Example of the MAPPING_NAME:
* winname:bob@foo.com -> unixname:fred
*
* Example of the MAPPING_ID:
* sid:S-1-2-3-4 -> uid:5678
*/
typedef enum {
UNDEFINED_FORMAT = -1,
DEFAULT_FORMAT = 0,
MAPPING_ID,
MAPPING_NAME,
USERMAP_CFG,
SMBUSERS
} format_t;
typedef struct {
format_t format;
FILE *file;
name_mapping_t *last;
} print_handle_t;
/*
* idmap_api batch related variables:
*
* idmap can operate in two modes. It the batch mode, the idmap_api
* batch is committed at the end of a batch of several
* commands. At the end of input file, typically. This mode is used
* for processing input from a file.
* In the non-batch mode, each command is committed immediately. This
* mode is used for tty input.
*/
/* Are we in the batch mode? */
static int batch_mode = 0;
/* Self describing stricture for positions */
struct pos_sds {
int size;
int last;
cmd_pos_t *pos[1];
};
static struct pos_sds *positions;
/* Handles for idmap_api batch */
static idmap_udt_handle_t *udt = NULL;
typedef struct {
char *user;
char *passwd;
char *auth;
char *windomain;
int direction;
idmap_nm_handle_t *handle;
} namemaps_t;
static namemaps_t namemaps = {NULL, NULL, NULL, NULL, 0, NULL};
/* Do we need to commit the udt batch at the end? */
static int udt_used;
/* Command handlers */
static int do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_list_name_mappings(flag_t *f, int argc, char **argv,
cmd_pos_t *pos);
static int do_add_name_mapping(flag_t *f, int argc, char **argv,
cmd_pos_t *pos);
static int do_remove_name_mapping(flag_t *f, int argc, char **argv,
cmd_pos_t *pos);
static int do_flush(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_set_namemap(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_unset_namemap(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
static int do_get_namemap(flag_t *f, int argc, char **argv, cmd_pos_t *pos);
/* Command names and their handlers to be passed to idmap_engine */
static cmd_ops_t commands[] = {
{
"show",
"c(create)v(verbose)V(trace)",
do_show_mapping
},
{
"dump",
"n(names)v(verbose)",
do_dump
},
{
"import",
"F(flush)f:(file)",
do_import
},
{
"export",
"f:(file)",
do_export
},
{
"list",
"",
do_list_name_mappings
},
{
"add",
"d(directional)",
do_add_name_mapping
},
{
"remove",
"a(all)t(to)f(from)d(directional)",
do_remove_name_mapping
},
{
"flush",
"a(all)",
do_flush
},
{
"exit",
"",
do_exit
},
{
"help",
"",
do_help
},
{
"set-namemap",
"a:(authentication)D:(bindDN)j:(passwd-file)",
do_set_namemap
},
{
"get-namemap",
"",
do_get_namemap
},
{
"unset-namemap",
"a:(authentication)D:(bindDN)j:(passwd-file):",
do_unset_namemap
}
};
/* Print error message, possibly with a position */
/* printflike */
static void
print_error(cmd_pos_t *pos, const char *format, ...)
{
size_t length;
va_list ap;
va_start(ap, format);
if (pos != NULL) {
length = strlen(pos->line);
/* Skip newlines etc at the end: */
while (length > 0 && isspace(pos->line[length - 1]))
length--;
(void) fprintf(stderr,
gettext("Error at line %d: %.*s\n"),
pos->linenum,
length,
pos->line);
}
(void) vfprintf(stderr, format, ap);
va_end(ap);
}
/* Inits positions sds. 0 means everything went OK, -1 for errors */
static int
init_positions()
{
int init_size = 32; /* Initial size of the positions array */
positions = (struct pos_sds *) malloc(sizeof (struct pos_sds) +
(init_size - 1) * sizeof (cmd_pos_t *));
if (positions == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
positions->size = init_size;
positions->last = 0;
return (0);
}
/* Free the positions array */
static void
fini_positions()
{
int i;
for (i = 0; i < positions->last; i++) {
if (positions->pos[i] == NULL)
continue;
free(positions->pos[i]->line);
free(positions->pos[i]);
}
free(positions);
positions = NULL;
}
/*
* Add another position to the positions array. 0 means everything
* went OK, -1 for errors
*/
static int
positions_add(cmd_pos_t *pos)
{
if (positions->last >= positions->size) {
positions->size *= 2;
positions = (struct pos_sds *)realloc(positions,
sizeof (struct pos_sds) +
(positions->size - 1) * sizeof (cmd_pos_t *));
if (positions == NULL)
goto nomemory;
}
if (pos == NULL)
positions->pos[positions->last] = NULL;
else {
positions->pos[positions->last] = (cmd_pos_t *)calloc(1,
sizeof (cmd_pos_t));
if (positions->pos[positions->last] == NULL)
goto nomemory;
*positions->pos[positions->last] = *pos;
positions->pos[positions->last]->line = strdup(pos->line);
if (positions->pos[positions->last]->line == NULL)
goto nomemory;
}
positions->last++;
return (0);
nomemory:
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
/*
* Compare two strings just like strcmp, but stop before the end of
* the s2
*/
static int
strcmp_no0(const char *s1, const char *s2)
{
return (strncmp(s1, s2, strlen(s2)));
}
/* Print help message */
static void
help()
{
(void) fprintf(stderr,
"idmap\n"
"idmap -f command-file\n"
"idmap add [-d] name1 name2\n"
"idmap dump [-n] [-v]\n"
"idmap export [-f file] format\n"
"idmap flush [-a]\n"
"idmap get-namemap name\n"
"idmap help\n"
"idmap import [-F] [-f file] format\n"
"idmap list\n"
"idmap remove -a\n"
"idmap remove [-f|-t] name\n"
"idmap remove [-d] name1 name2\n"
"idmap set-namemap [-a authenticationMethod] [-D bindDN] "
"[-j passwdfile] name1 name2\n"
"idmap show [-c] [-v] identity [targettype]\n"
"idmap unset-namemap [-a authenticationMethod] [-D bindDN]"
"[-j passwdfile] name\n");
}
/* The handler for the "help" command. */
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_help(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
help();
return (0);
}
/* Initialization of the commands which perform write operations */
static int
init_udt_batch()
{
idmap_stat stat;
stat = idmap_udt_create(&udt);
if (stat != IDMAP_SUCCESS) {
print_error(NULL,
gettext("Error initiating transaction (%s)"),
idmap_stat2string(stat));
return (-1);
}
if (init_positions() < 0)
return (-1);
return (0);
}
/* Finalization of the write commands */
static int
init_udt_command()
{
udt_used = 1;
if (batch_mode)
return (0);
return (init_udt_batch());
}
/* If everythings is OK, send the udt batch to idmapd */
static int
fini_udt_command(int ok, cmd_pos_t *pos)
{
int rc = 0;
int64_t failpos;
idmap_stat stat, stat1;
cmd_pos_t *reported_pos;
if (batch_mode)
return (0);
if (udt == NULL) {
print_error(pos,
gettext("Internal error: uninitiated batch.\n"));
return (-1);
}
if (ok && udt_used) {
stat = idmap_udt_commit(udt);
if (stat == IDMAP_SUCCESS)
goto out;
rc = -1;
stat1 = idmap_udt_get_error_index(udt, &failpos);
if (stat1 != IDMAP_SUCCESS) {
print_error(NULL,
gettext("Error diagnosing transaction (%s)\n"),
idmap_stat2string(stat1));
goto out;
}
if (failpos < 0)
reported_pos = pos;
else
reported_pos = positions->pos[failpos];
print_error(reported_pos,
gettext("Error commiting transaction (%s)\n"),
idmap_stat2string(stat));
}
out:
idmap_udt_destroy(udt);
udt = NULL;
udt_used = 0;
fini_positions();
return (rc);
}
/*
* Compare two possibly NULL strings
*/
static int
strcasecmp_null(char *a, char *b)
{
if (a == NULL && b == NULL)
return (0);
if (a == NULL)
return (-1);
if (b == NULL)
return (1);
return (strcasecmp(a, b));
}
/*
* Compare two possibly NULL strings
*/
static int
strcmp_null(char *a, char *b)
{
if (a == NULL && b == NULL)
return (0);
if (a == NULL)
return (-1);
if (b == NULL)
return (1);
return (strcmp(a, b));
}
static void
free_null(char **ptr)
{
if (*ptr != NULL) {
free(*ptr);
*ptr = NULL;
}
}
static
void
namemaps_free()
{
free_null(&namemaps.user);
if (namemaps.passwd != NULL)
(void) memset(namemaps.passwd, 0, strlen(namemaps.passwd));
free_null(&namemaps.passwd);
free_null(&namemaps.auth);
free_null(&namemaps.windomain);
namemaps.direction = IDMAP_DIRECTION_UNDEF;
if (namemaps.handle != NULL) {
idmap_fini_namemaps(namemaps.handle);
namemaps.handle = NULL;
}
}
/* Initialization of the commands which perform write operations */
static
int
init_nm_command(char *user, char *passwd, char *auth, char *windomain,
int direction, cmd_pos_t *pos)
{
idmap_stat stat;
if (namemaps.handle != NULL && (
strcmp_null(user, namemaps.user) != 0 ||
strcmp_null(passwd, namemaps.passwd) != 0 ||
strcasecmp_null(auth, namemaps.auth) != 0 ||
strcasecmp_null(windomain, namemaps.windomain) != 0 ||
direction != namemaps.direction)) {
namemaps_free();
}
if (namemaps.handle == NULL) {
stat = idmap_init_namemaps(&namemaps.handle, user,
passwd, auth, windomain, direction);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Error: could not perform directory-based "
"name mapping operation (%s)"),
idmap_stat2string(stat));
namemaps_free();
return (-1);
}
if (user != NULL && (namemaps.user = strdup(user)) == NULL ||
passwd != NULL && (namemaps.passwd =
strdup(passwd)) == NULL ||
auth != NULL && (namemaps.auth = strdup(auth)) == NULL ||
windomain != NULL && (namemaps.windomain =
strdup(windomain)) == NULL) {
print_error(pos, "%s.\n", strerror(ENOMEM));
namemaps_free();
return (-1);
}
namemaps.direction = direction;
}
return (0);
}
/* Cleanup after the xxx-namemaps commands */
static void
fini_nm_command()
{
if (batch_mode)
return;
namemaps_free();
}
/* Convert numeric expression of the direction to it's string form */
static char *
direction2string(int direction)
{
switch (direction) {
case IDMAP_DIRECTION_BI:
return ("==");
case IDMAP_DIRECTION_W2U:
return ("=>");
case IDMAP_DIRECTION_U2W:
return ("<=");
default:
/* This can never happen: */
print_error(NULL,
gettext("Internal error: invalid direction.\n"));
return ("");
}
/* never reached */
}
/*
* Returns 1 if c is a shell-meta-character requiring quoting, 0
* otherwise.
*
* We don't quote '*' and ':' because they cannot do any harm
* a) they have no meaning to idmap_engine b) even ifsomebody copy &
* paste idmap output to a shell commandline, there is the identity
* type string in front of them. On the other hand, '*' and ':' are
* everywhere.
*/
static int
is_shell_special(char c)
{
if (isspace(c))
return (1);
if (strchr("&^{}#;'\"\\`!$()[]><|~", c) != NULL)
return (1);
return (0);
}
/*
* Returns 1 if c is a shell-meta-character requiring quoting even
* inside double quotes, 0 otherwise. It means \, " and $ .
*
* This set of characters is a subset of those in is_shell_special().
*/
static int
is_dq_special(char c)
{
if (strchr("\\\"$", c) != NULL)
return (1);
return (0);
}
/*
* Quote any shell meta-characters in the given string. If 'quote' is
* true then use double-quotes to quote the whole string, else use
* back-slash to quote each individual meta-character.
*
* The resulting string is placed in *res. Callers must free *res if the
* return value isn't 0 (even if the given string had no meta-chars).
* If there are any errors this returns -1, else 0.
*/
static int
shell_app(char **res, char *string, int quote)
{
int i, j;
uint_t noss = 0; /* Number Of Shell Special chars in the input */
uint_t noqb = 0; /* Number Of Quotes and Backslahes in the input */
char *out;
size_t len_orig = strlen(string);
size_t len;
if (INHIBITED(string)) {
out = strdup("\"\"");
if (out == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
*res = out;
return (0);
}
/* First, let us count how many characters we need to quote: */
for (i = 0; i < len_orig; i++) {
if (is_shell_special(string[i])) {
noss++;
if (is_dq_special(string[i]))
noqb++;
}
}
/* Do we need to quote at all? */
if (noss == 0) {
out = strdup(string);
if (out == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
*res = out;
return (0);
}
/* What is the length of the result? */
if (quote)
len = strlen(string) + 2 + noqb + 1; /* 2 for quotation marks */
else
len = strlen(string) + noss + 1;
out = (char *)malloc(len);
if (out == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
j = 0;
if (quote)
out[j++] = '"';
for (i = 0; i < len_orig; i++) {
/* Quote the dangerous chars by a backslash */
if (quote && is_dq_special(string[i]) ||
(!quote && is_shell_special(string[i]))) {
out[j++] = '\\';
}
out[j++] = string[i];
}
if (quote)
out[j++] = '"';
out[j] = '\0';
*res = out;
return (0);
}
/* Assemble string form sid */
static char *
sid_format(name_mapping_t *nm)
{
char *to;
size_t len;
char *typestring;
switch (nm->is_wuser) {
case IDMAP_YES:
typestring = ID_USID;
break;
case IDMAP_NO:
typestring = ID_GSID;
break;
default:
typestring = ID_SID;
break;
}
/* 'usid:' + sidprefix + '-' + rid + '\0' */
len = strlen(nm->sidprefix) + 7 + 3 * sizeof (nm->rid);
to = (char *)malloc(len);
if (to == NULL)
return (NULL);
(void) snprintf(to, len, "%s:%s-%u", typestring, nm->sidprefix,
nm->rid);
return (to);
}
/* Assemble string form uid or gid */
static char *
pid_format(uid_t from, int is_user)
{
char *to;
size_t len;
/* ID_UID ":" + uid + '\0' */
len = 5 + 3 * sizeof (uid_t);
to = (char *)malloc(len);
if (to == NULL)
return (NULL);
(void) snprintf(to, len, "%s:%u", is_user ? ID_UID : ID_GID, from);
return (to);
}
/* Assemble winname, e.g. "winuser:bob@foo.sun.com", from name_mapping_t */
static int
nm2winqn(name_mapping_t *nm, char **winqn)
{
char *out;
size_t length = 0;
int is_domain = 1;
char *prefix;
/* Sometimes there are no text names. Return a sid, then. */
if (nm->winname == NULL && nm->sidprefix != NULL) {
*winqn = sid_format(nm);
return (0);
}
switch (nm->is_wuser) {
case IDMAP_YES:
prefix = ID_WINUSER ":";
break;
case IDMAP_NO:
prefix = ID_WINGROUP ":";
break;
case IDMAP_UNKNOWN:
prefix = ID_WINNAME ":";
break;
}
length = strlen(prefix);
if (nm->winname != NULL)
length += strlen(nm->winname);
/* Windomain is not mandatory: */
if (nm->windomain == NULL || INHIBITED(nm->winname))
is_domain = 0;
else
length += strlen(nm->windomain) + 1;
out = (char *)malloc(length + 1);
if (out == NULL) {
print_error(NULL,
"%s.\n", strerror(ENOMEM));
return (-1);
}
(void) strcpy(out, prefix);
/* LINTED E_NOP_IF_STMT */
if (nm->winname == NULL)
;
else if (!is_domain)
(void) strcat(out, nm->winname);
else if (nm->is_nt4) {
(void) strcat(out, nm->windomain);
(void) strcat(out, "\\");
(void) strcat(out, nm->winname);
} else {
(void) strcat(out, nm->winname);
(void) strcat(out, "@");
(void) strcat(out, nm->windomain);
}
*winqn = out;
return (0);
}
/*
* Assemble a text unixname, e.g. unixuser:fred. Use only for
* mapping, not namerules - there an empty name means inhibited
* mappings, while here pid is printed if there is no name.
*/
static
int
nm2unixname(name_mapping_t *nm, char **unixname)
{
size_t length = 0;
char *out, *it, *prefix;
/* Sometimes there is no name, just pid: */
if (nm->unixname == NULL) {
if (nm->pid == UNDEFINED_UID)
return (-1);
*unixname = pid_format(nm->pid, nm->is_user);
return (0);
}
if (shell_app(&it, nm->unixname, 0))
return (-1);
switch (nm->is_user) {
case IDMAP_YES:
prefix = ID_UNIXUSER ":";
break;
case IDMAP_NO:
prefix = ID_UNIXGROUP ":";
break;
case IDMAP_UNKNOWN:
prefix = ID_UNIXUSER ":";
break;
}
length = strlen(prefix) + strlen(it);
out = (char *)malloc(length + 1);
if (out == NULL) {
print_error(NULL,
"%s.\n", strerror(ENOMEM));
free(it);
return (-1);
}
(void) strcpy(out, prefix);
(void) strcat(out, it);
free(it);
*unixname = out;
return (0);
}
/* Allocate a new name_mapping_t and initialize the values. */
static name_mapping_t *
name_mapping_init()
{
name_mapping_t *nm = (name_mapping_t *)malloc(sizeof (name_mapping_t));
if (nm == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (NULL);
}
nm->winname = nm->windomain = nm->unixname = nm->sidprefix = NULL;
nm->rid = UNDEFINED_RID;
nm->is_nt4 = B_FALSE;
nm->is_user = IDMAP_UNKNOWN;
nm->is_wuser = IDMAP_UNKNOWN;
nm->direction = IDMAP_DIRECTION_UNDEF;
nm->pid = UNDEFINED_UID;
return (nm);
}
/* Free name_mapping_t */
static void
name_mapping_fini(name_mapping_t *nm)
{
free(nm->winname);
free(nm->windomain);
free(nm->unixname);
free(nm->sidprefix);
free(nm);
}
static int
name_mapping_cpy(name_mapping_t *to, name_mapping_t *from)
{
free(to->winname);
free(to->windomain);
free(to->unixname);
free(to->sidprefix);
(void) memcpy(to, from, sizeof (name_mapping_t));
to->winname = to->windomain = to->unixname = to->sidprefix = NULL;
if (from->winname != NULL) {
to->winname = strdup(from->winname);
if (to->winname == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
}
if (from->windomain != NULL) {
to->windomain = strdup(from->windomain);
if (to->windomain == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
}
if (from->unixname != NULL) {
to->unixname = strdup(from->unixname);
if (to->unixname == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
}
if (from->sidprefix != NULL) {
to->sidprefix = strdup(from->sidprefix);
if (to->sidprefix == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (-1);
}
}
return (0);
}
static int
name_mapping_format(name_mapping_t *nm, char **out)
{
char *winname = NULL;
char *winname1 = NULL;
char *unixname = NULL;
int maxlen;
*out = NULL;
if (nm2winqn(nm, &winname1) < 0)
return (-1);
if (shell_app(&winname, winname1, 1)) {
free(winname1);
return (-1);
}
free(winname1);
if (nm2unixname(nm, &unixname)) {
free(winname);
return (-1);
}
/* 10 is strlen("add -d\t\t\n") + 1 */
maxlen = 10 + strlen(unixname) + strlen(winname);
*out = (char *)malloc(maxlen);
if (nm->direction == IDMAP_DIRECTION_U2W) {
(void) snprintf(*out, maxlen, "add -d\t%s\t%s\n",
unixname, winname);
} else {
(void) snprintf(*out, maxlen, "add %s\t%s\t%s\n",
nm->direction == IDMAP_DIRECTION_BI? "" : "-d",
winname, unixname);
}
free(winname);
free(unixname);
return (0);
}
/* Initialize print_mapping variables. Must be called before print_mapping */
static print_handle_t *
print_mapping_init(format_t f, FILE *fi)
{
print_handle_t *out;
out = (print_handle_t *)malloc(sizeof (print_handle_t));
if (out == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
return (NULL);
}
out->format = f;
out->file = fi;
out->last = name_mapping_init();
if (out->last == NULL)
return (NULL);
return (out);
}
/* Finalize print_mapping. */
static int
print_mapping_fini(print_handle_t *pnm)
{
char *out = NULL;
int rc = 0;
switch (pnm->format) {
case SMBUSERS:
if (pnm->last->unixname != NULL) {
(void) fprintf(pnm->file, "\n");
}
break;
case DEFAULT_FORMAT:
if (pnm->last->unixname == NULL)
break;
rc = name_mapping_format(pnm->last, &out);
if (rc >= 0) {
(void) fprintf(pnm->file, "%s", out);
free(out);
}
break;
default:
;
}
name_mapping_fini(pnm->last);
free(pnm);
return (rc);
}
static char *
usermap_cfg_string(char *in)
{
int len;
char *out;
if (INHIBITED(in))
return (strdup("\"\""));
len = strlen(in);
if (len == strcspn(in, " \t#"))
return (strdup(in));
out = malloc(len + 3);
if (out == NULL)
return (NULL);
(void) snprintf(out, len + 3, "\"%s\"", in);
return (out);
}
/*
* This prints both name rules and ordinary mappings, based on the pnm_format
* set in print_mapping_init().
*/
static int
print_mapping(print_handle_t *pnm, name_mapping_t *nm)
{
char *dirstring;
char *winname = NULL;
char *windomain = NULL;
char *unixname = NULL;
FILE *f = pnm->file;
switch (pnm->format) {
case MAPPING_NAME:
if (nm2winqn(nm, &winname) < 0)
return (-1);
if (nm2unixname(nm, &unixname) < 0) {
free(winname);
return (-1);
}
/* LINTED E_CASE_FALLTHRU */
case MAPPING_ID:
if (pnm->format == MAPPING_ID) {
if (nm->sidprefix == NULL) {
print_error(NULL,
gettext("SID not given.\n"));
return (-1);
}
winname = sid_format(nm);
if (winname == NULL)
return (-1);
unixname = pid_format(nm->pid, nm->is_user);
if (unixname == NULL) {
free(winname);
return (-1);
}
}
dirstring = direction2string(nm->direction);
(void) fprintf(f, "%s\t%s\t%s\n", winname, dirstring,
unixname);
break;
case SMBUSERS:
if (nm->is_user != IDMAP_YES || nm->is_wuser != IDMAP_YES) {
print_error(NULL,
gettext("Group rule: "));
f = stderr;
} else if (nm->direction == IDMAP_DIRECTION_U2W) {
print_error(NULL,
gettext("Opposite direction of the mapping: "));
f = stderr;
} else if (INHIBITED(nm->winname) || INHIBITED(nm->unixname)) {
print_error(NULL, gettext("Inhibited rule: "));
f = stderr;
}
if (shell_app(&winname, nm->winname, 1))
return (-1);
unixname = INHIBITED(nm->unixname) ? "\"\"" : nm->unixname;
if (pnm->file != f) {
(void) fprintf(f, "%s=%s\n", unixname, winname);
} else if (pnm->last->unixname != NULL &&
strcmp(pnm->last->unixname, unixname) == 0) {
(void) fprintf(f, " %s", winname);
} else {
if (pnm->last->unixname != NULL) {
(void) fprintf(f, "\n");
free(pnm->last->unixname);
}
pnm->last->unixname = strdup(unixname);
if (pnm->last->unixname == NULL) {
print_error(NULL,
"%s.\n", strerror(ENOMEM));
}
(void) fprintf(f, "%s=%s", unixname, winname);
}
unixname = NULL;
break;
case USERMAP_CFG:
if (nm->is_user != IDMAP_YES || nm->is_wuser != IDMAP_YES) {
print_error(NULL,
gettext("Group rule: "));
f = stderr;
}
dirstring = direction2string(nm->direction);
if ((winname = usermap_cfg_string(nm->winname)) == NULL ||
(unixname = usermap_cfg_string(nm->unixname)) == NULL ||
(windomain = usermap_cfg_string(nm->windomain)) == NULL) {
print_error(NULL, "%s.\n", strerror(ENOMEM));
free(winname);
free(unixname);
free(windomain);
return (-1);
}
if (nm->windomain == NULL) {
(void) fprintf(f, "%s\t%s\t%s\n",
winname, dirstring, unixname);
} else
(void) fprintf(f, nm->is_nt4 ?
"%s\\%s\t%s\t%s\n" :
"%2$s@%1$s\t%3$s\t%4$s\n",
windomain, winname, dirstring, unixname);
break;
/* This is a format for namerules */
case DEFAULT_FORMAT:
/*
* If nm is the same as the last one except is_wuser, we combine
* winuser & wingroup to winname
*/
if (nm->direction == pnm->last->direction &&
nm->is_user == pnm->last->is_user &&
strcmp_null(pnm->last->unixname, nm->unixname) == 0 &&
strcmp_null(pnm->last->winname, nm->winname) == 0 &&
strcmp_null(pnm->last->windomain, nm->windomain) == 0) {
pnm->last->is_wuser = IDMAP_UNKNOWN;
} else {
if (pnm->last->unixname != NULL ||
pnm->last->winname != NULL) {
char *out = NULL;
if (name_mapping_format(pnm->last, &out) < 0)
return (-1);
(void) fprintf(f, "%s", out);
free(out);
}
if (name_mapping_cpy(pnm->last, nm) < 0)
return (-1);
}
break;
default:
/* This can never happen: */
print_error(NULL,
gettext("Internal error: invalid print format.\n"));
return (-1);
}
free(winname);
free(unixname);
free(windomain);
return (0);
}
static
void
print_how(idmap_how *how)
{
idmap_namerule *rule;
name_mapping_t nm;
char *rule_text;
switch (how->map_type) {
case IDMAP_MAP_TYPE_DS_AD:
(void) printf(gettext("Method:\tAD Directory\n"));
(void) printf(gettext("DN:\t%s\n"),
CHECK_NULL(how->idmap_how_u.ad.dn));
(void) printf(gettext("Attribute:\t%s=%s\n"),
CHECK_NULL(how->idmap_how_u.ad.attr),
CHECK_NULL(how->idmap_how_u.ad.value));
break;
case IDMAP_MAP_TYPE_DS_NLDAP:
(void) printf(gettext("Method:\tNative LDAP Directory\n"));
(void) printf(gettext("DN:\t%s\n"),
CHECK_NULL(how->idmap_how_u.nldap.dn));
(void) printf(gettext("Attribute:\t%s=%s\n"),
CHECK_NULL(how->idmap_how_u.nldap.attr),
CHECK_NULL(how->idmap_how_u.nldap.value));
break;
case IDMAP_MAP_TYPE_RULE_BASED:
(void) printf(gettext("Method:\tName Rule\n"));
rule = &how->idmap_how_u.rule;
/*
* The name rules as specified by the user can have a
* "winname", "winuser" or "wingroup". "Winname" rules are
* decomposed to a "winuser" and "wingroup" rules by idmap.
* Currently is_wuser is a boolean. Due to these reasons
* the returned is_wuser does not represent the original rule.
* It is therefore better set is_wuser to unknown.
*/
nm.is_user = rule->is_user;
nm.is_wuser = IDMAP_UNKNOWN;
nm.direction = rule->direction;
nm.winname = rule->winname;
nm.windomain = rule->windomain;
nm.unixname = rule->unixname;
nm.is_nt4 = rule->is_nt4;
if (name_mapping_format(&nm, &rule_text) == 0) {
(void) printf(gettext("Rule:\t%s"), rule_text);
free(rule_text);
}
break;
case IDMAP_MAP_TYPE_EPHEMERAL:
(void) printf(gettext("Method:\tEphemeral\n"));
break;
case IDMAP_MAP_TYPE_LOCAL_SID:
(void) printf(gettext("Method:\tLocal SID\n"));
break;
case IDMAP_MAP_TYPE_KNOWN_SID:
(void) printf(gettext("Method:\tWell-Known mapping\n"));
break;
case IDMAP_MAP_TYPE_IDMU:
(void) printf(gettext("Method:\tIDMU\n"));
(void) printf(gettext("DN:\t%s\n"),
CHECK_NULL(how->idmap_how_u.idmu.dn));
(void) printf(gettext("Attribute:\t%s=%s\n"),
CHECK_NULL(how->idmap_how_u.idmu.attr),
CHECK_NULL(how->idmap_how_u.idmu.value));
break;
}
}
static
void
print_info(idmap_info *info)
{
if (info->how.map_type != IDMAP_MAP_TYPE_UNKNOWN) {
switch (info->src) {
case IDMAP_MAP_SRC_NEW:
(void) printf(gettext("Source:\tNew\n"));
break;
case IDMAP_MAP_SRC_CACHE:
(void) printf(gettext("Source:\tCache\n"));
break;
case IDMAP_MAP_SRC_HARD_CODED:
(void) printf(gettext("Source:\tHard Coded\n"));
break;
case IDMAP_MAP_SRC_ALGORITHMIC:
(void) printf(gettext("Source:\tAlgorithmic\n"));
break;
}
print_how(&info->how);
}
if (info->trace != NULL) {
(void) printf(gettext("Trace:\n"));
idmap_trace_print(stdout, "\t", info->trace);
}
}
static
void
print_error_info(idmap_info *info)
{
idmap_how *how = &info->how;
idmap_namerule *rule;
name_mapping_t nm;
char *rule_text;
(void) memset(&nm, 0, sizeof (nm));
switch (how->map_type) {
case IDMAP_MAP_TYPE_DS_AD:
(void) fprintf(stderr,
gettext("Failed Method:\tAD Directory\n"));
(void) fprintf(stderr, gettext("DN:\t%s\n"),
how->idmap_how_u.ad.dn);
(void) fprintf(stderr, gettext("Attribute:\t%s=%s\n"),
how->idmap_how_u.ad.attr,
how->idmap_how_u.ad.value);
break;
case IDMAP_MAP_TYPE_DS_NLDAP:
(void) fprintf(stderr,
gettext("Failed Method:\tNative LDAP Directory\n"));
(void) fprintf(stderr, gettext("DN:\t%s\n"),
how->idmap_how_u.nldap.dn);
(void) fprintf(stderr, gettext("Attribute:\t%s=%s\n"),
how->idmap_how_u.nldap.attr,
how->idmap_how_u.nldap.value);
break;
case IDMAP_MAP_TYPE_RULE_BASED:
(void) fprintf(stderr, gettext("Failed Method:\tName Rule\n"));
rule = &how->idmap_how_u.rule;
/*
* The name rules as specified by the user can have a
* "winname", "winuser" or "wingroup". "Winname" rules are
* decomposed to a "winuser" and "wingroup" rules by idmap.
* Currently is_wuser is a boolean. Due to these reasons
* the returned is_wuser does not represent the original rule.
* It is therefore better to set is_wuser to unknown.
*/
nm.is_user = rule->is_user;
nm.is_wuser = IDMAP_UNKNOWN;
nm.direction = rule->direction;
nm.winname = rule->winname;
nm.windomain = rule->windomain;
nm.unixname = rule->unixname;
nm.is_nt4 = rule->is_nt4;
if (name_mapping_format(&nm, &rule_text) == 0) {
(void) fprintf(stderr, gettext("Rule:\t%s"), rule_text);
free(rule_text);
}
break;
case IDMAP_MAP_TYPE_EPHEMERAL:
(void) fprintf(stderr, gettext("Failed Method:\tEphemeral\n"));
break;
case IDMAP_MAP_TYPE_LOCAL_SID:
(void) fprintf(stderr, gettext("Failed Method:\tLocal SID\n"));
break;
case IDMAP_MAP_TYPE_KNOWN_SID:
(void) fprintf(stderr,
gettext("Failed Method:\tWell-Known mapping\n"));
break;
case IDMAP_MAP_TYPE_IDMU:
(void) fprintf(stderr,
gettext("Failed Method:\tIDMU\n"));
(void) fprintf(stderr, gettext("DN:\t%s\n"),
CHECK_NULL(how->idmap_how_u.idmu.dn));
(void) fprintf(stderr, gettext("Attribute:\t%s=%s\n"),
CHECK_NULL(how->idmap_how_u.idmu.attr),
CHECK_NULL(how->idmap_how_u.idmu.value));
break;
}
if (info->trace != NULL) {
(void) printf(gettext("Trace:\n"));
idmap_trace_print(stderr, "\t", info->trace);
}
}
/* dump command handler */
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_dump(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
idmap_stat stat;
idmap_iter_t *ihandle;
int rc = 0;
boolean_t is_user;
boolean_t is_wuser;
print_handle_t *ph;
int flag = 0;
idmap_info info;
ph = print_mapping_init(f[n_FLAG] != NULL ? MAPPING_NAME : MAPPING_ID,
stdout);
if (ph == NULL)
return (-1);
if (f[v_FLAG] != NULL)
flag = IDMAP_REQ_FLG_MAPPING_INFO;
stat = idmap_iter_mappings(&ihandle, flag);
if (stat < 0) {
print_error(pos,
gettext("Iteration handle not obtained (%s)\n"),
idmap_stat2string(stat));
rc = -1;
goto cleanup;
}
do {
name_mapping_t *nm = name_mapping_init();
if (nm == NULL) {
rc = -1;
goto cleanup;
}
stat = idmap_iter_next_mapping(ihandle,
&nm->sidprefix, &nm->rid, &nm->pid,
&nm->winname, &nm->windomain,
&nm->unixname, &is_user, &is_wuser,
&nm->direction, &info);
nm->is_user = is_user ? IDMAP_YES : IDMAP_NO;
nm->is_wuser = is_wuser ? IDMAP_YES : IDMAP_NO;
if (stat >= 0) {
(void) print_mapping(ph, nm);
print_how(&info.how);
idmap_info_free(&info);
}
name_mapping_fini(nm);
} while (stat > 0);
/* IDMAP_ERR_NOTFOUND indicates end of the list */
if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) {
print_error(pos,
gettext("Error during iteration (%s)\n"),
idmap_stat2string(stat));
rc = -1;
goto cleanup;
}
idmap_iter_destroy(ihandle);
cleanup:
(void) print_mapping_fini(ph);
return (rc);
}
/*
* The same as strdup, but length chars is duplicated, no matter on
* '\0'. The caller must guarantee "length" chars in "from".
*/
static char *
strndup(char *from, size_t length)
{
char *out = (char *)malloc(length + 1);
if (out == NULL) {
print_error(NULL, gettext("Not enough memory\n"));
return (NULL);
}
(void) strncpy(out, from, length);
out[length] = '\0';
return (out);
}
/*
* Convert pid from string to it's numerical representation. If it is
* a valid string, i.e. number of a proper length, return 1. Otherwise
* print an error message and return 0.
*/
static int
pid_convert(char *string, uid_t *number, int type, cmd_pos_t *pos)
{
int i;
long long ll;
char *type_string;
size_t len = strlen(string);
if (type == TYPE_GID)
type_string = ID_GID;
else if (type == TYPE_UID)
type_string = ID_UID;
else
return (0);
for (i = 0; i < len; i++) {
if (!isdigit(string[i])) {
print_error(pos,
gettext("\"%s\" is not a valid %s: the non-digit"
" character '%c' found.\n"), string,
type_string, string[i]);
return (0);
}
}
ll = atoll(string);
/* Isn't it too large? */
if (type == TYPE_UID && (uid_t)ll != ll ||
type == TYPE_GID && (gid_t)ll != ll) {
print_error(pos,
gettext("%llu: too large for a %s.\n"), ll,
type_string);
return (0);
}
*number = (uid_t)ll;
return (1);
}
/*
* Convert SID from string to prefix and rid. If it has a valid
* format, i.e. S(\-\d+)+, return 1. Otherwise print an error
* message and return 0.
*/
static int
sid_convert(char *from, char **prefix, idmap_rid_t *rid, cmd_pos_t *pos)
{
int i, j;
char *cp;
char *ecp;
char *prefix_end;
u_longlong_t a;
unsigned long r;
if (strcmp_no0(from, "S-1-") != 0) {
print_error(pos,
gettext("Invalid %s \"%s\": it doesn't start "
"with \"%s\".\n"), ID_SID, from, "S-1-");
return (0);
}
if (strlen(from) <= strlen("S-1-")) {
print_error(pos,
gettext("Invalid %s \"%s\": the authority and RID parts are"
" missing.\n"),
ID_SID, from);
return (0);
}
/* count '-'s */
for (j = 0, cp = strchr(from, '-');
cp != NULL;
j++, cp = strchr(cp + 1, '-')) {
/* can't end on a '-' */
if (*(cp + 1) == '\0') {
print_error(pos,
gettext("Invalid %s \"%s\": '-' at the end.\n"),
ID_SID, from);
return (0);
} else if (*(cp + 1) == '-') {
print_error(pos,
gettext("Invalid %s \"%s\": double '-'.\n"),
ID_SID, from);
return (0);
}
}
/* check that we only have digits and '-' */
i = strspn(from + 1, "0123456789-") + 1;
if (i < strlen(from)) {
print_error(pos,
gettext("Invalid %s \"%s\": invalid character '%c'.\n"),
ID_SID, from, from[i]);
return (0);
}
cp = from + strlen("S-1-");
/* 64-bit safe parsing of unsigned 48-bit authority value */
errno = 0;
a = strtoull(cp, &ecp, 10);
/* errors parsing the authority or too many bits */
if (cp == ecp || (a == 0 && errno == EINVAL)) {
print_error(pos,
gettext("Invalid %s \"%s\": unable to parse the "
"authority \"%.*s\".\n"), ID_SID, from, ecp - cp,
cp);
return (0);
}
if ((a == ULLONG_MAX && errno == ERANGE) ||
(a & 0x0000ffffffffffffULL) != a) {
print_error(pos,
gettext("Invalid %s \"%s\": the authority "
"\"%.*s\" is too large.\n"), ID_SID, from,
ecp - cp, cp);
return (0);
}
cp = ecp;
if (j < 3) {
print_error(pos,
gettext("Invalid %s \"%s\": must have at least one RID.\n"),
ID_SID, from);
return (0);
}
for (i = 2; i < j; i++) {
if (*cp++ != '-') {
/* Should never happen */
print_error(pos,
gettext("Invalid %s \"%s\": internal error:"
" '-' missing.\n"),
ID_SID, from);
return (0);
}
/* 32-bit safe parsing of unsigned 32-bit RID */
errno = 0;
r = strtoul(cp, &ecp, 10);
/* errors parsing the RID */
if (cp == ecp || (r == 0 && errno == EINVAL)) {
/* should never happen */
print_error(pos,
gettext("Invalid %s \"%s\": internal error: "
"unable to parse the RID "
"after \"%.*s\".\n"), ID_SID,
from, cp - from, from);
return (0);
}
if (r == ULONG_MAX && errno == ERANGE) {
print_error(pos,
gettext("Invalid %s \"%s\": the RID \"%.*s\""
" is too large.\n"), ID_SID,
from, ecp - cp, cp);
return (0);
}
prefix_end = cp;
cp = ecp;
}
/* check that all of the string SID has been consumed */
if (*cp != '\0') {
/* Should never happen */
print_error(pos,
gettext("Invalid %s \"%s\": internal error: "
"something is still left.\n"),
ID_SID, from);
return (0);
}
*rid = (idmap_rid_t)r;
/* -1 for the '-' at the end: */
*prefix = strndup(from, prefix_end - from - 1);
if (*prefix == NULL) {
print_error(pos,
"%s.\n", strerror(ENOMEM));
return (0);
}
return (1);
}
/* Does the line start with USERMAP_CFG IP qualifier? */
static int
ucp_is_IP_qualifier(char *line)
{
char *it;
it = line + strcspn(line, " \t\n#:");
return (*(it + 1) == ':' ? 1 : 0);
}
/*
* returns interior of quotation marks in USERMAP_CFG. In this format,
* there cannot be a protected quotation mark inside.
*/
static char *
ucp_qm_interior(char **line, cmd_pos_t *pos)
{
char *out;
char *qm = strchr(*line + 1, '"');
if (qm == NULL) {
print_error(pos,
gettext("Unclosed quotations\n"));
return (NULL);
}
out = strndup(*line + 1, qm - *line - 1);
*line = qm + 1;
return (out);
}
/*
* Grab next token from the line in USERMAP_CFG format. terminators,
* the 3rd parameter, contains all the characters which can terminate
* the token. line_num is the line number of input used for error
* reporting.
*/
static char *
ucp_grab_token(char **line, cmd_pos_t *pos, const char *terminators)
{
char *token;
if (**line == '"')
token = ucp_qm_interior(line, pos);
else {
int length = strcspn(*line, terminators);
token = strndup(*line, length);
*line += length;
}
return (token);
}
/*
* Convert a line in usermap.cfg format to name_mapping.
*
* Return values: -1 for error, 0 for empty line, 1 for a mapping
* found.
*/
static int
ucp_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm)
{
char *it;
char *token;
char *token2;
char separator;
int is_direction = 0;
it = line + strspn(line, " \t\n");
/* empty or comment lines are OK: */
if (*it == '\0' || *it == '#')
return (0);
/* We do not support network qualifiers */
if (ucp_is_IP_qualifier(it)) {
print_error(pos,
gettext("Unable to handle network qualifier.\n"));
return (-1);
}
/* The windows name: */
token = ucp_grab_token(&it, pos, " \t#\\\n@=<");
if (token == NULL)
return (-1);
separator = *it;
/* Didn't we bump to the end of line? */
if (separator == '\0' || separator == '#') {
free(token);
print_error(pos,
gettext("UNIX_name not found.\n"));
return (-1);
}
/* Do we have a domainname? */
if (separator == '\\' || separator == '@') {
it ++;
token2 = ucp_grab_token(&it, pos, " \t\n#");
if (token2 == NULL) {
free(token);
return (-1);
} else if (*it == '\0' || *it == '#') {
free(token);
free(token2);
print_error(pos,
gettext("UNIX_name not found.\n"));
}
if (separator == '\\') {
nm->windomain = token;
nm->winname = token2;
nm->is_nt4 = 1;
} else {
nm->windomain = token2;
nm->winname = token;
nm->is_nt4 = 0;
}
} else {
nm->windomain = NULL;
nm->winname = token;
nm->is_nt4 = 0;
}
it = it + strspn(it, " \t\n");
/* Direction string is optional: */
if (strncmp(it, "==", 2) == 0) {
nm->direction = IDMAP_DIRECTION_BI;
is_direction = 1;
} else if (strncmp(it, "<=", 2) == 0) {
nm->direction = IDMAP_DIRECTION_U2W;
is_direction = 1;
} else if (strncmp(it, "=>", 2) == 0) {
nm->direction = IDMAP_DIRECTION_W2U;
is_direction = 1;
} else {
nm->direction = IDMAP_DIRECTION_BI;
is_direction = 0;
}
if (is_direction) {
it += 2;
it += strspn(it, " \t\n");
if (*it == '\0' || *it == '#') {
print_error(pos,
gettext("UNIX_name not found.\n"));
return (-1);
}
}
/* Now unixname: */
it += strspn(it, " \t\n");
token = ucp_grab_token(&it, pos, " \t\n#");
if (token == NULL)
/* nm->winname to be freed by name_mapping_fini */
return (-1);
/* Neither here we support IP qualifiers */
if (ucp_is_IP_qualifier(token)) {
print_error(pos,
gettext("Unable to handle network qualifier.\n"));
free(token);
return (-1);
}
nm->unixname = token;
it += strspn(it, " \t\n");
/* Does something remain on the line */
if (*it != '\0' && *it != '#') {
print_error(pos,
gettext("Unrecognized parameters \"%s\".\n"), it);
return (-1);
}
return (1);
}
/*
* Parse SMBUSERS line to name_mapping_t. if line is NULL, then
* pasrsing of the previous line is continued. line_num is input line
* number used for error reporting.
* Return values:
* rc -1: error
* rc = 0: mapping found and the line is finished,
* rc = 1: mapping found and there remains other on the line
*/
static int
sup_line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm)
{
static char *ll = NULL;
static char *unixname = NULL;
static size_t unixname_l = 0;
char *token;
if (line != NULL) {
ll = line;
unixname = ll += strspn(ll, " \t");
if (*ll == '\0' || *ll == '#')
return (0);
unixname_l = strcspn(ll, " \t:=#\n");
ll += unixname_l;
if (*ll == '\0'|| *ll == '#')
return (0);
ll += strspn(ll, " \t:=#\n");
}
if (*ll == '\0'|| *ll == '#')
return (0);
token = ucp_grab_token(&ll, pos, " \t\n");
if (token == NULL)
return (-1);
nm->is_nt4 = 0;
nm->direction = IDMAP_DIRECTION_W2U;
nm->windomain = NULL;
nm->winname = token;
nm->unixname = strndup(unixname, unixname_l);
if (nm->unixname == NULL)
return (-1);
ll += strspn(ll, " \t\n");
return (1);
}
/* Parse line to name_mapping_t. Basicaly just a format switch. */
static int
line2nm(char *line, cmd_pos_t *pos, name_mapping_t *nm, format_t f)
{
switch (f) {
case USERMAP_CFG:
if (line == NULL)
return (0);
else
return (ucp_line2nm(line, pos, nm));
case SMBUSERS:
return (sup_line2nm(line, pos, nm));
default:
/* This can never happen */
print_error(pos,
gettext("Internal error: invalid line format.\n"));
}
return (-1);
}
/* Examine -f flag and return the appropriate format_t */
static format_t
ff2format(char *ff, int is_mandatory)
{
if (ff == NULL && is_mandatory) {
print_error(NULL, gettext("Format not given.\n"));
return (UNDEFINED_FORMAT);
}
if (ff == NULL)
return (DEFAULT_FORMAT);
if (strcasecmp(ff, "usermap.cfg") == 0)
return (USERMAP_CFG);
if (strcasecmp(ff, "smbusers") == 0)
return (SMBUSERS);
print_error(NULL,
gettext("The only known formats are: \"usermap.cfg\" and "
"\"smbusers\".\n"));
return (UNDEFINED_FORMAT);
}
/* Delete all namerules of the given type */
static int
flush_nm(boolean_t is_user, cmd_pos_t *pos)
{
idmap_stat stat;
stat = idmap_udt_flush_namerules(udt);
if (stat < 0) {
print_error(pos,
is_user ? gettext("Unable to flush users (%s).\n")
: gettext("Unable to flush groups (%s).\n"),
idmap_stat2string(stat));
return (-1);
}
if (positions_add(pos) < 0)
return (-1);
return (0);
}
/* import command handler */
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_import(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
name_mapping_t *nm;
cmd_pos_t pos2;
char line[MAX_INPUT_LINE_SZ];
format_t format;
int rc = 0;
idmap_stat stat;
FILE *file = NULL;
if (batch_mode) {
print_error(pos,
gettext("Import is not allowed in the batch mode.\n"));
return (-1);
}
format = ff2format(argv[0], 1);
if (format == UNDEFINED_FORMAT)
return (-1);
if (init_udt_command())
return (-1);
/* We don't flush groups in the usermap.cfg nor smbusers format */
if (f[F_FLAG] != NULL &&
flush_nm(B_TRUE, pos) < 0 &&
(format == USERMAP_CFG || format == SMBUSERS ||
flush_nm(B_FALSE, pos) < 0)) {
rc = -1;
goto cleanup;
}
/* Where we import from? */
if (f[f_FLAG] == NULL)
file = stdin;
else {
file = fopen(f[f_FLAG], "r");
if (file == NULL) {
perror(f[f_FLAG]);
goto cleanup;
}
}
pos2.linenum = 0;
pos2.line = line;
while (fgets(line, MAX_INPUT_LINE_SZ, file)) {
char *line2 = line;
pos2.linenum++;
/*
* In SMBUSERS format there can be more mappings on
* each line. So we need the internal cycle for each line.
*/
do {
nm = name_mapping_init();
if (nm == NULL) {
rc = -1;
goto cleanup;
}
rc = line2nm(line2, &pos2, nm, format);
line2 = NULL;
if (rc < 1) {
name_mapping_fini(nm);
break;
}
stat = idmap_udt_add_namerule(udt, nm->windomain,
nm->is_user ? B_TRUE : B_FALSE,
nm->is_wuser ? B_TRUE : B_FALSE,
nm->winname,
nm->unixname, nm->is_nt4, nm->direction);
if (stat < 0) {
print_error(&pos2,
gettext("Transaction error (%s)\n"),
idmap_stat2string(stat));
rc = -1;
}
if (rc >= 0)
rc = positions_add(&pos2);
name_mapping_fini(nm);
} while (rc >= 0);
if (rc < 0) {
print_error(NULL,
gettext("Import canceled.\n"));
break;
}
}
cleanup:
if (fini_udt_command((rc < 0 ? 0 : 1), pos))
rc = -1;
if (file != NULL && file != stdin)
(void) fclose(file);
return (rc);
}
/*
* List name mappings in the format specified. list_users /
* list_groups determine which type to list. The output goes to the
* file fi.
*/
static int
list_name_mappings(format_t format, FILE *fi)
{
idmap_stat stat;
idmap_iter_t *ihandle;
name_mapping_t *nm;
boolean_t is_user;
boolean_t is_wuser;
print_handle_t *ph;
stat = idmap_iter_namerules(NULL, 0, 0, NULL, NULL, &ihandle);
if (stat < 0) {
print_error(NULL,
gettext("Iteration handle not obtained (%s)\n"),
idmap_stat2string(stat));
idmap_iter_destroy(ihandle);
return (-1);
}
ph = print_mapping_init(format, fi);
if (ph == NULL)
return (-1);
do {
nm = name_mapping_init();
if (nm == NULL) {
idmap_iter_destroy(ihandle);
return (-1);
}
stat = idmap_iter_next_namerule(ihandle, &nm->windomain,
&nm->winname, &nm->unixname, &is_user, &is_wuser,
&nm->is_nt4, &nm->direction);
if (stat >= 0) {
nm->is_user = is_user ? IDMAP_YES : IDMAP_NO;
nm->is_wuser = is_wuser ? IDMAP_YES : IDMAP_NO;
(void) print_mapping(ph, nm);
}
name_mapping_fini(nm);
} while (stat > 0);
(void) print_mapping_fini(ph);
if (stat < 0 && stat != IDMAP_ERR_NOTFOUND) {
print_error(NULL,
gettext("Error during iteration (%s)\n"),
idmap_stat2string(stat));
idmap_iter_destroy(ihandle);
return (-1);
}
idmap_iter_destroy(ihandle);
return (0);
}
/* Export command handler */
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_export(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
int rc;
format_t format;
FILE *fi;
format = ff2format(argv[0], 1);
if (format == UNDEFINED_FORMAT)
return (-1);
/* Where do we output to? */
if (f[f_FLAG] == NULL)
fi = stdout;
else {
fi = fopen(f[f_FLAG], "w");
if (fi == NULL) {
perror(f[f_FLAG]);
return (-1);
}
}
/* List the requested types: */
rc = list_name_mappings(format, fi);
cleanup:
if (fi != NULL && fi != stdout)
(void) fclose(fi);
return (rc);
}
/* List command handler */
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_list_name_mappings(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
int rc;
/* List the requested types: */
rc = list_name_mappings(DEFAULT_FORMAT, stdout);
return (rc);
}
/* This is just a debug function for dumping flags */
static void
print_flags(flag_t *f)
{
int c;
for (c = 0; c < FLAG_ALPHABET_SIZE; c++) {
if (f[c] == FLAG_SET)
(void) printf("FLAG: -%c, VALUE: %p\n", c,
(void *) f[c]);
else if (f[c])
(void) printf("FLAG: -%c, VALUE: %s\n", c, f[c]);
}
}
/* Convert string like sid or winname to the identity type code */
static int
string2type(char *str, cmd_pos_t *pos) {
int i;
int code = TYPE_INVALID;
for (i = 0; i < sizeof (identity2code) / sizeof (id_code_t); i++) {
if (strcasecmp(identity2code[i].identity, str) == 0) {
code = identity2code[i].code;
break;
}
}
if (code == TYPE_INVALID) {
print_error(pos,
gettext("Error: invalid identity type \"%s\"\n"), str);
}
return (code);
}
/*
* Split argument to its identity code and a name part
* return values:
* TYPE_INVALID for unknown identity
* TYPE_AUTO for no identity (to be autodetected)
* <TYPE_XXX> for known identity
*/
static int
get_identity(char *arg, char **name, cmd_pos_t *pos)
{
char *it;
int code = TYPE_INVALID;
if ((it = strchr(arg, ':')) == NULL) {
*name = arg;
return (TYPE_AUTO);
}
*it = '\0';
code = string2type(arg, pos);
*it = ':'; /* restore the original string: */
*name = it + 1;
return (code);
}
/*
* This function splits name to the relevant pieces: is_user, winname,
* windomain unixname. E.g. for winname, it strdups nm->winname and possibly
* nm->windomain and return TYPE_WN.
*
* If there is already one of the text fields allocated, it is OK.
* Return values:
* -1 ... syntax error
* 0 ... it wasnt possible to determine
* <TYPE_XXX> otherwise
*/
static int
name2parts(char *name, name_mapping_t *nm, cmd_pos_t *pos)
{
char *it;
int code;
code = get_identity(name, &it, pos);
switch (code) {
case TYPE_INVALID:
/* syntax error: */
return (-1);
case TYPE_AUTO:
/* autodetection: */
if (nm->winname != NULL && nm->is_wuser != IDMAP_UNKNOWN)
code = nm->is_wuser == IDMAP_YES ? TYPE_UU : TYPE_UG;
else if (nm->unixname != NULL ||
strchr(name, '@') != NULL ||
strchr(name, '\\') != NULL)
/* btw, nm->is_user can never be IDMAP_UNKNOWN here */
code = TYPE_WN;
else
return (0);
/* If the code was guessed succesfully, we are OK. */
break;
default:
name = it;
}
if (code & IS_WIN) {
if (code & IS_USER)
nm->is_wuser = IDMAP_YES;
else if (code & IS_GROUP)
nm->is_wuser = IDMAP_NO;
} else {
if (code & IS_USER)
nm->is_user = IDMAP_YES;
else if (code & IS_GROUP)
nm->is_user = IDMAP_NO;
}
if (code & IS_WIN && code & IS_NAME) {
if (nm->winname != NULL || nm->windomain != NULL)
return (code);
if ((it = strchr(name, '@')) != NULL) {
int length = it - name + 1;
nm->winname = (char *)malloc(length);
(void) strncpy(nm->winname, name, length - 1);
nm->winname[length - 1] = '\0';
nm->windomain = strdup(it + 1);
} else if ((it = strrchr(name, '\\')) != NULL) {
int length = it - name + 1;
nm->windomain = (char *)malloc(length);
(void) strncpy(nm->windomain, name, length - 1);
nm->windomain[length - 1] = '\0';
nm->winname = strdup(it + 1);
nm->is_nt4 = B_TRUE;
} else
nm->winname = strdup(name);
return (code);
}
if (!(code & IS_WIN) && code & IS_NAME) {
if (nm->unixname != NULL)
return (code);
if (strlen(name) == 0)
nm->unixname = strdup("\"\"");
else
nm->unixname = strdup(name);
return (code);
}
if (code & IS_WIN && !(code & IS_NAME)) {
if (!sid_convert(name, &nm->sidprefix, &nm->rid, pos))
return (-1);
else
return (code);
}
/*
* it is (!(code & TYPE_WIN) && !(code & TYPE_NAME)) here - the other
* possiblities are exhausted.
*/
if (!pid_convert(name, &nm->pid, code, pos))
return (-1);
else
return (code);
}
/*
* Cycle through add/remove arguments until they are identified or found
* invalid.
*/
static
name_mapping_t *
args2nm(int *is_first_win, int argc, char **argv,
cmd_pos_t *pos)
{
int code;
int i;
name_mapping_t *nm;
nm = name_mapping_init();
if (nm == NULL)
return (NULL);
for (i = 0; i < 2 * argc - 1; i++) {
code = name2parts(argv[i % 2], nm, pos);
switch (code) {
case -1:
goto fail;
case 0:
if (i > 0) {
print_error(pos,
gettext("Missing identity type"
" cannot be determined for %s.\n"),
argv[i % 2]);
goto fail;
}
break;
default:
if (!(code & IS_NAME)) {
print_error(pos,
gettext("%s is not a valid name\n"),
argv[i % 2]);
goto fail;
}
}
}
if (argc == 2 && nm->winname == NULL) {
print_error(pos, gettext("No windows identity found.\n"));
goto fail;
}
if (argc == 2 && nm->unixname == NULL) {
print_error(pos, gettext("No unix identity found.\n"));
goto fail;
}
if (argc == 1 && nm->winname == NULL && nm->unixname == NULL) {
print_error(pos, gettext("No identity type determined.\n"));
goto fail;
}
if (is_first_win != NULL)
*is_first_win = code & IS_WIN;
return (nm);
fail:
name_mapping_fini(nm);
return (NULL);
}
/* add command handler. */
static int
do_add_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
name_mapping_t *nm;
int rc = 0;
int is_first_win;
idmap_stat stat;
int is_wuser;
print_handle_t *ph;
/* Exactly two arguments must be specified */
if (argc < 2) {
print_error(pos, gettext("Not enough arguments.\n"));
return (-1);
} else if (argc > 2) {
print_error(pos, gettext("Too many arguments.\n"));
return (-1);
}
nm = args2nm(&is_first_win, argc, argv, pos);
if (nm == NULL)
return (-1);
if (f[d_FLAG] != NULL)
nm->direction = is_first_win
? IDMAP_DIRECTION_W2U
: IDMAP_DIRECTION_U2W;
else
nm->direction = IDMAP_DIRECTION_BI;
/* Now let us write it: */
if (init_udt_command()) {
name_mapping_fini(nm);
return (-1);
}
for (is_wuser = IDMAP_YES; is_wuser >= IDMAP_NO; is_wuser--) {
/* nm->is_wuser can be IDMAP_YES, IDMAP_NO or IDMAP_UNKNOWN */
if ((is_wuser == IDMAP_YES && nm->is_wuser == IDMAP_NO) ||
(is_wuser == IDMAP_NO && nm->is_wuser == IDMAP_YES))
continue;
stat = idmap_udt_add_namerule(udt, nm->windomain,
nm->is_user ? B_TRUE : B_FALSE,
is_wuser ? B_TRUE : B_FALSE,
nm->winname, nm->unixname, nm->is_nt4, nm->direction);
}
/* We echo the mapping */
ph = print_mapping_init(DEFAULT_FORMAT, stdout);
if (ph == NULL) {
rc = -1;
goto cleanup;
}
(void) print_mapping(ph, nm);
(void) print_mapping_fini(ph);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Mapping not created (%s)\n"),
idmap_stat2string(stat));
rc = -1;
}
if (rc == 0)
rc = positions_add(pos);
cleanup:
name_mapping_fini(nm);
if (fini_udt_command(1, pos))
rc = -1;
return (rc);
}
/* remove command handler */
static int
do_remove_name_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
name_mapping_t *nm;
int rc = 0;
idmap_stat stat;
int is_first_win;
int is_wuser;
/* "-a" means we flush all of them */
if (f[a_FLAG] != NULL) {
if (argc) {
print_error(pos,
gettext("Too many arguments.\n"));
return (-1);
}
if (init_udt_command())
return (-1);
rc = flush_nm(B_TRUE, pos);
if (rc >= 0)
rc = flush_nm(B_FALSE, pos);
if (fini_udt_command(rc ? 0 : 1, pos))
rc = -1;
return (rc);
}
/* Contrary to add_name_mapping, we can have only one argument */
if (argc < 1) {
print_error(pos, gettext("Not enough arguments.\n"));
return (-1);
} else if (argc > 2) {
print_error(pos, gettext("Too many arguments.\n"));
return (-1);
} else if (
/* both -f and -t: */
f[f_FLAG] != NULL && f[t_FLAG] != NULL ||
/* -d with a single argument: */
argc == 1 && f[d_FLAG] != NULL ||
/* -f or -t with two arguments: */
argc == 2 && (f[f_FLAG] != NULL || f[t_FLAG] != NULL)) {
print_error(pos,
gettext("Direction ambiguous.\n"));
return (-1);
}
/*
* Similar to do_add_name_mapping - see the comments
* there. Except we may have only one argument here.
*/
nm = args2nm(&is_first_win, argc, argv, pos);
if (nm == NULL)
return (-1);
/*
* If the direction is not specified by a -d/-f/-t flag, then it
* is IDMAP_DIRECTION_UNDEF, because in that case we want to
* remove any mapping. If it was IDMAP_DIRECTION_BI, idmap_api would
* delete a bidirectional one only.
*/
if (f[d_FLAG] != NULL || f[f_FLAG] != NULL)
nm->direction = is_first_win
? IDMAP_DIRECTION_W2U
: IDMAP_DIRECTION_U2W;
else if (f[t_FLAG] != NULL)
nm->direction = is_first_win
? IDMAP_DIRECTION_U2W
: IDMAP_DIRECTION_W2U;
else
nm->direction = IDMAP_DIRECTION_UNDEF;
if (init_udt_command()) {
name_mapping_fini(nm);
return (-1);
}
for (is_wuser = IDMAP_YES; is_wuser >= IDMAP_NO; is_wuser--) {
if ((is_wuser == IDMAP_YES && nm->is_wuser == IDMAP_NO) ||
(is_wuser == IDMAP_NO && nm->is_wuser == IDMAP_YES))
continue;
stat = idmap_udt_rm_namerule(udt,
nm->is_user ? B_TRUE : B_FALSE,
is_wuser ? B_TRUE : B_FALSE,
nm->windomain, nm->winname, nm->unixname, nm->direction);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Mapping not deleted (%s)\n"),
idmap_stat2string(stat));
rc = -1;
break;
}
}
if (rc == 0)
rc = positions_add(pos);
cleanup:
name_mapping_fini(nm);
if (fini_udt_command(1, pos))
rc = -1;
return (rc);
}
/* flush command handler */
static int
do_flush(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
NOTE(ARGUNUSED(argv))
idmap_flush_op op;
idmap_stat stat;
int rc = 0;
if (argc > 0) {
print_error(pos,
gettext("Too many arguments.\n"));
return (-1);
}
if (f[a_FLAG] != NULL)
op = IDMAP_FLUSH_DELETE;
else
op = IDMAP_FLUSH_EXPIRE;
stat = idmap_flush(op);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("%s\n"),
idmap_stat2string(stat));
rc = -1;
}
return (rc);
}
/* exit command handler */
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_exit(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
return (0);
}
/* debug command handler: just print the parameters */
static int
/* LINTED E_STATIC_UNUSED */
debug_print_params(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
int i;
#if 0
char *leaktest = (char *)malloc(100);
#endif
print_flags(f);
for (i = 0; i < argc; i++) {
(void) printf("Argument %d: %s\n", i, argv[i]);
}
(void) fflush(stdout);
return (0);
}
/*
* From name_mapping_t, asseble a string containing identity of the
* given type.
*/
static int
nm2type(name_mapping_t *nm, int type, char **to)
{
switch (type) {
case TYPE_SID:
case TYPE_USID:
case TYPE_GSID:
if (nm->sidprefix == NULL)
return (-1);
*to = sid_format(nm);
return (0);
case TYPE_WN:
case TYPE_WU:
case TYPE_WG:
return (nm2winqn(nm, to));
case TYPE_UID:
case TYPE_GID:
case TYPE_PID:
*to = pid_format(nm->pid, nm->is_user);
if (*to == NULL)
return (-1);
else
return (0);
case TYPE_UN:
case TYPE_UU:
case TYPE_UG:
return (nm2unixname(nm, to));
default:
/* This can never happen: */
print_error(NULL,
gettext("Internal error: invalid name type.\n"));
return (-1);
}
/* never reached */
}
/* show command handler */
static int
do_show_mapping(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
idmap_stat stat = 0;
int flag;
idmap_stat map_stat = 0;
int type_from;
int type_to;
name_mapping_t *nm = NULL;
char *fromname;
char *toname;
idmap_info info;
(void) memset(&info, 0, sizeof (info));
if (argc == 0) {
print_error(pos,
gettext("No identity given\n"));
return (-1);
} else if (argc > 2) {
print_error(pos,
gettext("Too many arguments.\n"));
return (-1);
}
flag = 0;
if (f[c_FLAG] == NULL)
flag |= IDMAP_REQ_FLG_NO_NEW_ID_ALLOC;
if (f[v_FLAG] != NULL)
flag |= IDMAP_REQ_FLG_MAPPING_INFO;
if (f[V_FLAG] != NULL)
flag |= IDMAP_REQ_FLG_TRACE;
nm = name_mapping_init();
if (nm == NULL)
goto cleanup;
type_from = name2parts(argv[0], nm, pos);
if (type_from <= 0) {
stat = IDMAP_ERR_ARG;
goto cleanup;
}
/* Second, determine type_to: */
if (argc < 2) {
type_to = type_from & IS_WIN ? TYPE_PID : TYPE_SID;
if (type_from & IS_NAME)
type_to |= IS_NAME;
} else {
type_to = string2type(argv[1], pos);
if (type_to == TYPE_INVALID) {
stat = IDMAP_ERR_ARG;
goto cleanup;
}
}
if (type_to & IS_WIN) {
if (type_to & IS_USER)
nm->is_wuser = IDMAP_YES;
else if (type_to & IS_GROUP)
nm->is_wuser = IDMAP_NO;
else
nm->is_wuser = IDMAP_UNKNOWN;
} else {
if (type_to & IS_USER)
nm->is_user = IDMAP_YES;
else if (type_to & IS_GROUP)
nm->is_user = IDMAP_NO;
}
/* Are both arguments the same OS side? */
if (!(type_from & IS_WIN ^ type_to & IS_WIN)) {
print_error(pos,
gettext("Direction ambiguous.\n"));
stat = IDMAP_ERR_ARG;
goto cleanup;
}
/*
* We have two interfaces for retrieving the mappings:
* idmap_get_sidbyuid & comp (the batch interface) and
* idmap_get_w2u_mapping & comp. We want to use both of them, because
* the former mimicks kernel interface better and the later offers the
* string names. In the batch case, our batch has always size 1.
*
* Btw, type_from cannot be IDMAP_PID, because there is no type string
* for it.
*/
if (type_from & IS_NAME || type_to & IS_NAME ||
type_from == TYPE_GSID || type_from == TYPE_USID ||
type_to == TYPE_GSID || type_to == TYPE_USID) {
if (type_from & IS_WIN) {
map_stat = idmap_get_w2u_mapping(
nm->sidprefix,
&nm->rid,
nm->winname,
nm->windomain,
flag,
&nm->is_user, &nm->is_wuser,
&nm->pid,
&nm->unixname,
&nm->direction,
&info);
} else {
map_stat = idmap_get_u2w_mapping(
&nm->pid,
nm->unixname,
flag,
nm->is_user, &nm->is_wuser,
&nm->sidprefix,
&nm->rid,
&nm->winname,
&nm->windomain,
&nm->direction,
&info);
}
} else {
/* batch handle */
idmap_get_handle_t *ghandle = NULL;
/* To be passed to idmap_get_uidbysid */
gid_t gid = UNDEFINED_GID;
/* To be passed to idmap_get_gidbysid */
uid_t uid = UNDEFINED_UID;
/* Create an in-memory structure for all the batch: */
stat = idmap_get_create(&ghandle);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Unable to create handle for communicating"
" with idmapd(1M) (%s)\n"),
idmap_stat2string(stat));
idmap_get_destroy(ghandle);
goto cleanup;
}
/* Schedule the request: */
if (type_to == TYPE_UID) {
stat = idmap_getext_uidbysid(ghandle,
nm->sidprefix,
nm->rid,
flag,
&uid,
&info,
&map_stat);
} else if (type_to == TYPE_GID) {
stat = idmap_getext_gidbysid(ghandle,
nm->sidprefix,
nm->rid,
flag,
&gid,
&info,
&map_stat);
} else if (type_to == TYPE_PID) {
stat = idmap_getext_pidbysid(ghandle,
nm->sidprefix,
nm->rid,
flag,
&nm->pid,
&nm->is_user,
&info,
&map_stat);
} else if (type_from == TYPE_UID) {
stat = idmap_getext_sidbyuid(ghandle,
nm->pid,
flag,
&nm->sidprefix,
&nm->rid,
&info,
&map_stat);
} else if (type_from == TYPE_GID) {
stat = idmap_getext_sidbygid(ghandle,
(gid_t)nm->pid,
flag,
&nm->sidprefix,
&nm->rid,
&info,
&map_stat);
} else {
/* This can never happen: */
print_error(pos,
gettext("Internal error in show.\n"));
exit(1);
}
if (stat < 0) {
print_error(pos,
gettext("Request for %.3s not sent (%s)\n"),
argv[0], idmap_stat2string(stat));
idmap_get_destroy(ghandle);
goto cleanup;
}
/* Send the batch to idmapd and obtain results: */
stat = idmap_get_mappings(ghandle);
if (stat < 0) {
print_error(pos,
gettext("Mappings not obtained because of"
" RPC problem (%s)\n"),
idmap_stat2string(stat));
idmap_get_destroy(ghandle);
goto cleanup;
}
/* Destroy the batch handle: */
idmap_get_destroy(ghandle);
if (type_to == TYPE_UID)
nm->pid = uid;
else if (type_to == TYPE_GID)
nm->pid = (uid_t)gid;
}
/*
* If there was -c flag, we do output whatever we can even in
* the case of error:
*/
if (map_stat < 0 && flag & IDMAP_REQ_FLG_NO_NEW_ID_ALLOC)
goto errormsg;
/*
* idmapd returns fallback uid/gid in case of errors. However
* it uses special sentinel value i.e 4294967295 (or -1) to
* indicate that falbback pid is not available either. In such
* case idmap(1M) should not display the mapping because there
* is no fallback mapping.
*/
if ((type_to == TYPE_UID || type_to == TYPE_GID ||
type_to == TYPE_PID) && nm->pid == UNDEFINED_UID)
goto errormsg;
if (nm2type(nm, type_from, &fromname) < 0)
goto errormsg;
if (nm2type(nm, type_to, &toname) < 0) {
if (!(flag & IDMAP_REQ_FLG_NO_NEW_ID_ALLOC))
(void) printf("%s -> %s:%u\n",
fromname,
type_to & IS_GROUP ? ID_GID : ID_UID,
UID_NOBODY);
free(fromname);
} else {
(void) printf("%s -> %s\n", fromname, toname);
free(fromname);
free(toname);
}
errormsg:
if (map_stat < 0) {
print_error(pos, gettext("Error:\t%s\n"),
idmap_stat2string(map_stat));
print_error_info(&info);
} else {
print_info(&info);
}
idmap_info_free(&info);
cleanup:
if (nm != NULL)
name_mapping_fini(nm);
return (stat < 0 || map_stat < 0 ? -1 : 0);
}
static int
flags2cred(flag_t *f, char **user, char **passwd, cmd_pos_t *pos)
{
*user = NULL;
*passwd = NULL;
if (f[D_FLAG] == NULL)
return (0); /* GSSAPI authentification => OK */
*user = strdup(f[D_FLAG]);
if (*user == NULL) {
print_error(pos, "%s.\n", strerror(ENOMEM));
return (-1);
}
/* Password: */
if (f[j_FLAG] != NULL) {
char line[MAX_INPUT_LINE_SZ];
int i;
FILE *file = fopen(f[j_FLAG], "r");
if (file == NULL) {
print_error(pos,
gettext("Failed to open password file \"%s\": (%s)"
".\n"), f[j_FLAG], strerror(errno));
goto fail;
}
/* The password is the fist line, we ignore the rest: */
if (fgets(line, MAX_INPUT_LINE_SZ, file) == NULL) {
print_error(pos,
gettext("The password file \"%s\" is empty.\n"),
f[j_FLAG]);
(void) fclose(file);
goto fail;
}
if (fclose(file) != 0) {
print_error(pos,
gettext("Unable to close the password file \"%s\""
".\n"), f[j_FLAG], strerror(errno));
goto fail;
}
/* Trim the eol: */
for (i = strlen(line) - 1;
i >= 0 && (line[i] == '\r' || line[i] == '\n');
i--)
line[i] = '\0';
*passwd = strdup(line);
if (*passwd == NULL) {
print_error(pos, "%s.\n", strerror(ENOMEM));
goto fail;
}
} else if (!batch_mode) {
/* If in the interactive mode, read the terminal input: */
char *it = getpassphrase("Enter password:");
if (it == NULL) {
print_error(NULL,
gettext("Failed to get password (%s).\n"),
strerror(errno));
goto fail;
}
*passwd = strdup(it);
(void) memset(it, 0, strlen(it));
if (*passwd == NULL) {
print_error(pos, "%s.\n", strerror(ENOMEM));
goto fail;
}
} else {
print_error(pos, gettext("No password given.\n"));
goto fail;
}
return (0);
fail:
if (*passwd != NULL) {
(void) memset(*passwd, 0, strlen(*passwd));
free(*passwd);
*passwd = NULL;
}
free(*user);
return (-1);
}
static int
do_set_namemap(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
idmap_stat stat;
name_mapping_t *nm;
int is_first_win;
char *user;
char *passwd;
if (argc < 2) {
print_error(pos,
gettext("Not enough arguments: two names needed for a "
"namemap.\n"));
return (-1);
} else if (argc > 2) {
print_error(pos,
gettext("Too many arguments: two names needed for a "
"namemap.\n"));
return (-1);
}
nm = args2nm(&is_first_win, argc, argv, pos);
if (nm == NULL)
return (-1);
if (flags2cred(f, &user, &passwd, pos) < 0)
return (-1);
nm->direction = is_first_win ? IDMAP_DIRECTION_W2U
: IDMAP_DIRECTION_U2W;
if (init_nm_command(user, passwd, f[a_FLAG], nm->windomain,
nm->direction, pos) < 0)
return (-1);
stat = idmap_set_namemap(namemaps.handle, nm->winname, nm->unixname,
nm->is_user, nm->is_wuser, nm->direction);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Failed to set namemap (%s).\n"),
idmap_stat2string(stat));
}
if (passwd != NULL) {
(void) memset(passwd, 0, strlen(passwd));
free(passwd);
}
free(user);
fini_nm_command();
name_mapping_fini(nm);
return (stat != IDMAP_SUCCESS ? -1 : 0);
}
static int
do_unset_namemap(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
idmap_stat stat;
name_mapping_t *nm;
int is_first_win;
char *user;
char *passwd;
if (argc < 1) {
print_error(pos,
gettext("Not enough arguments: a name needed to unset a "
"namemap.\n"));
return (-1);
} else if (argc > 2) {
print_error(pos,
gettext("Too many arguments: Only target name and type is "
"needed to unset namemap.\n"));
return (-1);
}
nm = args2nm(&is_first_win, 1, argv, pos);
if (nm == NULL)
return (-1);
if (flags2cred(f, &user, &passwd, pos) < 0)
return (-1);
nm->direction = is_first_win ? IDMAP_DIRECTION_W2U
: IDMAP_DIRECTION_U2W;
if (argc > 1 && !is_first_win) {
print_error(pos,
gettext("Target type \"%s\" is redundant.\n"),
argv[1]);
stat = IDMAP_ERR_ARG;
goto cleanup;
} else if (argc > 1) {
switch (string2type(argv[1], pos)) {
case TYPE_INVALID:
name_mapping_fini(nm);
return (-1);
case TYPE_UU:
nm->is_user = IDMAP_YES;
break;
case TYPE_UG:
nm->is_user = IDMAP_NO;
break;
default:
print_error(pos,
gettext("Invalid target type \"%s\": here the "
"possible target type is unixuser or "
"unixgroup.\n"), argv[1]);
stat = IDMAP_ERR_ARG;
goto cleanup;
}
}
if (init_nm_command(user, passwd, f[a_FLAG], nm->windomain,
nm->direction, pos) < 0)
return (-1);
stat = idmap_unset_namemap(namemaps.handle, nm->winname, nm->unixname,
nm->is_user, nm->is_wuser, nm->direction);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Failed to unset namemap (%s).\n"),
idmap_stat2string(stat));
}
cleanup:
if (passwd != NULL) {
(void) memset(passwd, 0, strlen(passwd));
free(passwd);
}
free(user);
fini_nm_command();
name_mapping_fini(nm);
return (stat == IDMAP_SUCCESS ? 0 : -1);
}
static int
/* LINTED E_FUNC_ARG_UNUSED */
do_get_namemap(flag_t *f, int argc, char **argv, cmd_pos_t *pos)
{
idmap_stat stat;
name_mapping_t *nm;
int is_first_win;
int is_source_ad;
char *winname = NULL;
char *unixname = NULL;
char *unixuser = NULL;
char *unixgroup = NULL;
if (argc < 1) {
print_error(pos,
gettext("Not enough arguments: a name needed to get a "
"namemap.\n"));
return (-1);
} else if (argc > 1) {
print_error(pos,
gettext("Too many arguments: just one name needed to get "
"a namemap.\n"));
return (-1);
}
nm = args2nm(&is_first_win, argc, argv, pos);
if (nm == NULL)
return (-1);
nm->direction = is_first_win ? IDMAP_DIRECTION_W2U
: IDMAP_DIRECTION_U2W;
/* nm->is_user is IDMAP_UNKNOWN for IDMAP_DIRECTION_W2U */
if (nm->is_user == IDMAP_YES) {
unixuser = strdup(nm->unixname);
if (unixuser == NULL) {
print_error(pos, "%s.\n", strerror(ENOMEM));
goto cleanup;
}
} else if (nm->is_user == IDMAP_NO) {
unixgroup = strdup(nm->unixname);
if (unixgroup == NULL) {
print_error(pos, "%s.\n", strerror(ENOMEM));
goto cleanup;
}
}
if (init_nm_command(NULL, NULL, NULL, nm->windomain,
nm->direction, pos) < 0)
return (-1);
stat = idmap_get_namemap(namemaps.handle, &is_source_ad, &nm->winname,
&nm->windomain, &nm->is_wuser, &unixuser, &unixgroup);
if (stat != IDMAP_SUCCESS) {
print_error(pos,
gettext("Failed to get namemap info (%s).\n"),
idmap_stat2string(stat));
goto cleanup;
}
if (nm2winqn(nm, &winname) < 0)
goto cleanup;
switch (is_source_ad) {
case IDMAP_YES:
if (unixuser == NULL && unixgroup == NULL)
(void) printf(gettext("\t\tNo namemap found in AD.\n"));
else {
(void) printf(gettext("AD namemaps for %s\n"), winname);
if (unixuser != NULL)
(void) printf(gettext("\t\t->\t%s:%s\n"),
ID_UNIXUSER, unixuser);
if (unixgroup != NULL)
(void) printf(gettext("\t\t->\t%s:%s\n"),
ID_UNIXGROUP, unixgroup);
}
break;
case IDMAP_NO:
if (nm2unixname(nm, &unixname) < 0)
goto cleanup;
if (nm->winname == NULL)
(void) printf(gettext("\t\tNo namemap found in "
"native LDAP.\n"));
else {
(void) printf(gettext("Native LDAP namemap for %s\n"),
unixname);
(void) printf(gettext("\t\t->\t%s\n"), winname);
}
break;
default:
/*
* This can never happen; the error must be recognized in
* args2nm
*/
print_error(pos,
gettext("Internal error: unknown source of namemaps.\n"));
}
cleanup:
fini_nm_command();
name_mapping_fini(nm);
if (winname != NULL)
free(winname);
if (unixuser != NULL)
free(unixuser);
if (unixgroup != NULL)
free(unixgroup);
return (stat == IDMAP_SUCCESS ? 0 : -1);
}
/* printflike */
static
void
idmap_cli_logger(int pri, const char *format, ...)
{
va_list args;
if (pri == LOG_DEBUG)
return;
va_start(args, format);
(void) vfprintf(stderr, format, args);
(void) fprintf(stderr, "\n");
va_end(args);
}
/* main function. Returns 1 for error, 0 otherwise */
int
main(int argc, char *argv[])
{
int rc;
/* set locale and domain for internationalization */
(void) setlocale(LC_ALL, "");
(void) textdomain(TEXT_DOMAIN);
/* Redirect logging */
idmap_set_logger(idmap_cli_logger);
adutils_set_logger(idmap_cli_logger);
/* idmap_engine determines the batch_mode: */
rc = engine_init(sizeof (commands) / sizeof (cmd_ops_t),
commands,
argc - 1,
argv + 1,
&batch_mode);
if (rc < 0) {
(void) engine_fini();
if (rc == IDMAP_ENG_ERROR_SILENT)
help();
return (1);
}
udt_used = 0;
if (batch_mode) {
if (init_udt_batch() < 0)
return (1);
}
rc = run_engine(argc - 1, argv + 1);
if (batch_mode) {
batch_mode = 0;
if (fini_udt_command(rc == 0 ? 1 : 0, NULL))
rc = -1;
fini_nm_command();
}
(void) engine_fini();
return (rc == 0 ? 0 : 1);
}